【Elasticsearch7.0】为什么不在使用映射type

  |   0 评论   |   0 浏览

为什么es不在使用type了, 让我们来了解下原因。

什么是映射type

在7.0之前所有发布的版本中,每个文档存储在单独的映射类型里面。一个映射类型被代表了一个文档的类型或者实体索引。例如一个商品索引他可能有电器类型和生鲜类型。
每个映射类型有自己的属性,电器类型可能有名称、生产日期、保修时间等属性,生鲜类型可能有名称、过期时间、最近使用时间等。每个文档都有_type属性来指定属于哪个映射类型,搜索的时候可以在url里面指定一个或者多个type。

GET product/electric,fresh/_search
{
  "query": {
    "match": {
      "name": "好"
    }
  }
}

_type属性和文档中的_id一起生成一个_uid属性,因此在同一个索引中可能存在相同的_id。
有时候映射类型可以用来指定文档的父子关系,如question类型的文档可以是answer类型文档的父文档。

为什么映射类型被移除

一开始,我们把索引比作是关系型数据库中的数据库,映射type比作了表。其实这个比喻是错误的,在关系型数据库中表是相互独立的,不同表中相同的列是没有关联关系的,这个与映射type是不一样的。
在es的索引中,不同的映射类型中存在相同的属性名,他们底层都是使用同一个Lucene属性。比如上面的商品例子中,电器和生鲜都有name字段,他们必须要有相同的映射类型才可以。例如在同一个索引中,deleted属性你在A类型中想用date来表示,在B类型中想用boolean来表示,像这种情况就不支持了。
最重要的是同一个索引中共同字段比较少或者没有,那么会导致数据太稀疏,这会影响Lucene有效压缩文档的能力,导致你的搜索很慢。
就是因为这些原因,es官方决定删除映射type这个概念。

替换方案

一个文档一个索引

把之前的电器类型和生鲜类型分别建立电器索引和生鲜索引。因为索引是相互独立的,不存在相同字段的冲突问题。使用这种方式有2个好处:
1、数据更加密集,有利于Lucene的压缩。
2、做统计的时候数据会更加准确,因为数据都在同一个索引中。
每个索引都可以根据自己实际需要存储多大的数据来进行调整,你可以对不同的索引设置不同的分片和副本。

自定义类型属性

上面的方式,会浪费分片,因为每个集群中的分片是有限制的,如果你不想为了几千个文档而浪费了不必要的分片,那么你可以自定义类型属性。
没有使用自定义类型示例:

PUT twitter
{
  "mappings": {
    "user": {
      "properties": {
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" }
      }
    },
    "tweet": {
      "properties": {
        "content": { "type": "text" },
        "user_name": { "type": "keyword" },
        "tweeted_at": { "type": "date" }
      }
    }
  }
}

PUT twitter/user/kimchy
{
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/tweet/1
{
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

GET twitter/tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

使用自定义类型示例:

PUT twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": { "type": "keyword" }, 
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" },
        "content": { "type": "text" },
        "tweeted_at": { "type": "date" }
      }
    }
  }
}

PUT twitter/_doc/user-kimchy
{
  "type": "user", 
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/_doc/tweet-1
{
  "type": "tweet", 
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

GET twitter/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "user_name": "kimchy"
        }
      },
      "filter": {
        "match": {
          "type": "tweet" 
        }
      }
    }
  }
}

没有父子关系映射type

以前会把一个映射类型表示父,另外一些映射类型表示子,没有了type,就没有这种语法了。如果你想继续表示这种父子关系,可以使用join属性。如

PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": { 
        "type": "join",
        "relations": {
          "question": "answer" 
        }
      }
    }
  }
}

移除映射type

es对这块迁移,做了很多版本的兼容,es5.6.0之后的版本会慢慢的不在支持映射type。在es8中,彻底不支持映射类型。之前的版本你可以通过设置来开启,但是会有警告日志。

多type迁移到单type

es官方也考虑到迁移的问题,为了方便用户迁移,他们提供了Reindex接口,该接口可以把多类型索引迁移到单类型索引,大大简化了迁移过程。

一个索引一个类型

把原本一个索引两个类型的改为一个索引一个文档

PUT users
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        }
      }
    }
  }
}

PUT tweets
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "user"
  },
  "dest": {
    "index": "users"
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "tweet"
  },
  "dest": {
    "index": "tweets"
  }
}

自定义类型

将自定义类型的索引进行迁移。

PUT new_twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        },
        "content": {
          "type": "text"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  },
  "script": {
    "source": """
      ctx._source.type = ctx._type;
      ctx._id = ctx._type + '-' + ctx._id;
      ctx._type = '_doc';
    """
  }
}

这里对_id进行了处理,防止id重复。

es7中接口举例

在es7中索引接口都不需要指定type,如果你指定了会有弃用的警告日志。

索引接口

PUT index?include_type_name=false
{
  "mappings": {
    "properties": { 
      "foo": {
        "type": "keyword"
      }
    }
  }
}
PUT index/_mappings?include_type_name=false
{
  "properties": { 
    "bar": {
      "type": "text"
    }
  }
}

文档接口

PUT index/_doc/1
{
  "foo": "baz"
}

返回值

{
  "_index": "index",
  "_id": "1",
  "_type": "_doc",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

查询文档

GET index/_doc/1
POST index/_update/1
{

    "doc" : {

        "foo" : "qux"
    }
}

GET /index/_source/1
POST _bulk
{ "index" : { "_index" : "index", "_id" : "3" } }
{ "foo" : "baz" }
{ "index" : { "_index" : "index", "_id" : "4" } }
{ "foo" : "qux" }

搜索接口

GET /twitter/_search
{
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

到此我们大概知道了es为什么要弃用映射type了。

也可以关注我的公众号:程序之声
图片
关注公众号,领取更多资源

本文为博主原创文章,未经博主允许不得转载。