一、重要需知
在Elasticsearch 7.0.0 或更高版本中创建的索引不再接受_default_映射。在6.x中创建的索引将继续在6.x中运行。在7.0的API中,类型已弃用,并且对索引创建、put映射、get映射、put模板、get模板和get字段映射的API进行了重大更改。
二、什么是映射类型(mapping types)?
从Elasticsearch的第一个版本开始,每个文档都存储在一个索引中并分配了一个映射类型。映射类型用于表示要建立索引的文档或者实体的类型,例如twitter索引可能具有user(用户)类型和tweet(推文)类型。
每种映射类型都可以有自己的字段,因此user类型可以有full_name字段、user_name字段和email字段,而tweet类型可以有content字段、tweeted_at(发布推文的时间)字段、以及和user类型类似的user_name字段。
每个文档都有一个包含类型名称的_type元数据字段,我们可以在搜索中指定类型名称,将搜索限制在一种或多种类型。
GET twitter/user,tweet/_search
{
"query": {
"match": {
"user_name": "kimchy"
}
}
}
_type字段与文档的_id组合在一起可以生成_uid字段,因此具有相同_id的不同类型的文档可以存在于单个索引中。映射类型还用于在文档之间建立父子关系,因此question类型的文档可以是answer类型文档的父级。
三、为什么要删除映射类型?
最初,我们谈到的“index”类似于SQL数据库中的“database”,“type”等同于“table”。这是一个不合适的类比,会导致错误的设想。在SQL数据库中,表彼此独立,一个表中的列与另一表中具有相同名称的列毫无关系,但是映射类型的字段却不是这种情况。
在一个Elasticsearch索引中,所有不同类型的同名字段内部是由相同的Lucene字段支持。换句话说,在上面的例子中,user类型中的user_name字段和tweet类型中的user_name字段是储存在完全相同的字段中,并且两个user_name字段在这两种类型中必须具有相同的映射(定义)。例如,当您希望删除同一索引中的一种类型同名的日期字段和另一种类型同名的布尔字段时,这可能会导致问题。
最重要的是,存储在同一索引中具有很少或没有共同字段的不同实体会导致数据稀疏,并干扰Lucene有效压缩文档的能力。
由于这些原因,我们决定从Elasticsearch中删除映射类型的概念。
四、映射类型的替代方法
1、为每个文档类型创建一个索引
第一个选择就是为每个文档类型创建一个索引,来代替将user和tweet都存储在twitter索引中。您可以将推文存储在tweet索引中,将用户存储在user索引中。索引之间彼此完全独立,因此索引之间的字段类型不会发生冲突。
这种方法有两个好处:
• 数据可能更密集,因此可以从Lucene中使用的压缩技术中受益。
• 在全文搜索中用于评分的术语统计可能更准确了,因为同一索引中的所有文档都代表一个实体。
每个索引的大小可以根据包含的文档数量进行适当调整,您可以为user设置使用较少数量的主分片,为tweet设置使用较大数量的主分片。
2、自定义类型字段
当然,一个集群中可以存在多少个主要分片是有限制的,因此您可能不想只存储几千个文档就浪费了整个分片。在这种情况下,您可以实现自己的自定义类型字段,它的工作方式与旧_type相似。让我们以上面的用户/推文示例为例。
最初,可能会这样做(6.x以下可以这么做,因为6.x每个索引只允许一个单一类型):
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"
}
}
}
您可以通过添加自定义类型字段来实现相同的目的,如下所示(7.x版本以下可以这么做):
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"
}
}
}
}
}
五、文档父子关系的建立将无映射类型
以前,通过将一种映射类型作为父级,而将另一种或多种其他映射类型作为子级来表示父子关系。没有类型,我们将无法再使用此语法。父子功能将继续像以前一样起作用,除了表达文档之间关系的方式已更改为使用新的join字段来实现。
六、删除映射类型时间表
对于用户来说,移除映射类型这是一个很大的变化,因此我们试图使其尽可能轻松,更改将按以下方式一步步推出:
Elasticsearch 5.6.0
• 在索引上设置index.mapping.single_type: true启用单一索引类型行为,该行为将在6.0中强制执行。
• 在5.6中创建的父子关系上可以使用join字段替换。
Elasticsearch 6.x
• 在5.x中创建的索引将像在5.x中一样继续在6.x中运行。
• 在6.x中创建的索引仅允许每个索引使用一种类型,该类型可以使用任何名称,但是只能有一个。首选的类型名称是_doc,因此索引API具有与7.0中相同的路径:PUT {index}/_ doc/{id} 和 POST {index}/_doc。
• _type名称不能再与_id组合以形成_uid字段。_uid字段已成为_id字段的别名。
• 新索引不再支持以前文档间建立父/子关系的旧方法,而应使用join字段实现。
• _default_ 映射类型已弃用。
• 在6.8中,创建索引、索引模板和映射(mappings)的API支持查询字符串参数include_type_name,该参数表示请求和响应是否应包括类型名称,默认为true。
Elasticsearch 7.x
• 在请求中指定类型已经被弃用。不再为文档创建索引就必须需要一种文档类型。对于显式ID,新索引API为PUT {index}/_doc/{id};对于自动生成的ID,则为POST {index}/_doc。请注意,在7.0版本中,doc是路径的永久部分,它表示端点名称而不是文档类型了。
• 索引创建、索引模板和映射(mappings)的API中的include_type_name参数将默认为false,当请求和响应中包括类型名称时将导致弃用警告。
• _default_映射类型已删除。
Elasticsearch 8.x
• 不再支持在请求中指定类型。
• include_type _name参数已删除。
七、将多类型索引迁移到单类型
Reindex API可用于将多类型索引转换为单类型索引。以下示例可在Elasticsearch 5.6或Elasticsearch 6.x中使用。在6.x中,无需指定index.mapping.single_type,因为它是默认值。
1、为每个文档类型创建一个索引
依照最开始的示例,我们将twitter索引分为tweets索引和users索引:
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",
"type": "_doc"
}
}
POST _reindex
{
"source": {
"index": "twitter",
"type": "tweet"
},
"dest": {
"index": "tweets",
"type": "_doc"
}
}
2、自定义类型字段
以下示例将添加了一个自定义类型字段,并将其设置为原始_type的值。如果有任何不同类型的文档具有冲突的ID,它也会将类型添加到_id中:
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';
"""
}
}
八、7.0中的无类型API的使用
在Elasticsearch 7.0中,每个API将支持无类型的请求,如果指定了类型将产生弃用警告。但是需要注意的是,即使目标索引包含了自定义类型,无类型的API也能正常工作。例如,如果索引的自定义类型名为my_type,我们可以使用无类型索引调用向其中添加文档,也可以使用无类型get调用加载文档。如下:
PUT test/my_type/1
{
"name" : "test"
}
GET test/my_type/1
GET test/_doc/1
结果:GET获取文档数据,两者结果一样,但是前者包含了类型名称,所以会有弃用警告,后者没有。
# GET test/my_type/1
#! Deprecation: [types removal] Specifying types in document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead.
{
"_index" : "test",
"_type" : "my_type",
"_id" : "1",
"_version" : 2,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "test"
}
}
# GET test/_doc/1
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "test"
}
}
1、Index APIs
索引创建、索引模板和映射的API支持在URL中使用include_type_name参数,该参数指定请求和响应中的映射定义是否应包含类型名称,可以设置为true或false。在6.8版中,该参数默认为true,以支持在7.0之前在映射(mappings)中使用类型名称的行为。在7.0版中默认为false,在版本8.0中将被删除。
将此选项设置为false,如下与Elasticsearch进行交互的一些示例(以下映射直接包含在映射键下,没有类型名称)。
PUT /my-index-000001?include_type_name=false
{
"mappings": {
"properties": {
"foo": {
"type": "keyword"
}
}
}
}
PUT /my-index-000001/_mappings?include_type_name=false
{
"properties": {
"bar": {
"type": "text"
}
}
}
GET /my-index-000001/_mappings?include_type_name=false
结果:
{
"my-index-000001" : {
"mappings" : {
"properties" : {
"bar" : {
"type" : "text"
},
"foo" : {
"type" : "keyword"
}
}
}
}
}
2、Document APIs
在7.0中,索引API必须使用{index}/_doc路径进行调用。对于显式ID,索引API为PUT {index}/_doc/{id};对于自动生成的ID,则为POST {index}/_doc。
PUT /my-index-000001/_doc/1
{
"foo": "baz"
}
结果:
{
"_index" : "my-index-000001",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
同样,获取和删除APl使用路径也要为{index}/_ doc/{id}:
GET /my-index-000001/_doc/1
需要注意的是,在7.0中,_doc表示端点名称,而不是文档类型。 _doc组件是文档索引、获取和删除API路径的永久部分,在8.0中不会删除。
对于像_update这种同时包含类型和端点名称的API路径,在7.0中,_update直接跟在索引名称后面即可:
POST /my-index-000001/_update/1
{
"doc" : {
"foo" : "qux"
}
}
GET /my-index-000001/_source/1
类型也不应再出现在请求正文中,类型也不应再出现在请求正文中。以下创建批量索引示例在URL和单个批量命令中都省略了该类型:
POST _bulk
{ "index" : { "_index" : "my-index-000001", "_id" : "3" } }
{ "foo" : "baz" }
{ "index" : { "_index" : "my-index-000001", "_id" : "4" } }
{ "foo" : "qux" }
3、Search APIs
调用诸如_search,_msearch或_explain之类的搜索API时,URL中不应包含类型。此外,不应在查询、聚合或脚本中使用类型字段。
4、Types in responses
文档和搜索API将继续在回应中返回_type 键,以避免中断响应解析。但是该键被认为已弃用,不应再被引用。在8.0版中,类型将从回应中完全删除。
请注意,当使用不推荐使用的类型API时,索引的映射类型将正常返回,但是无类型API会在响应中返回伪类型_doc。例如,即使映射具有自定义类型名称(例如my_type),以下无类型的get调用也将始终以doc作为类型返回:
PUT /my-index-000001/my_type/1
{
"foo": "baz"
}
GET /my-index-000001/_doc/1
结果:
{
"_index" : "my-index-000001",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found": true,
"_source" : {
"foo" : "baz"
}
}
5、Index templates
建议通过将include_type_name设置为false来重新添加索引模板,使索引模板无类型。在后台,无类型的模板在创建索引时将使用伪类型_doc。
如果将无类型的模板与有类型的索引创建调用一起使用,或者将有类型的模板与无类型的索引创建调用一起使用,则仍将应用该模板,但是是否应该有一个类型将由索引的创建调用决定。
例如,在下面的示例中,尽管index-1-01与无类型的模板匹配,但index-1-01将具有类型;尽管index-2-01与定义了类型的模板匹配,但index-2-01将是无类型的。 index-1-01和index-2-01都将从它们匹配的模板中继承foo字段。
PUT _template/template1
{
"index_patterns":[ "index-1-*" ],
"mappings": {
"properties": {
"foo": {
"type": "keyword"
}
}
}
}
PUT _template/template2?include_type_name=true
{
"index_patterns":[ "index-2-*" ],
"mappings": {
"type": {
"properties": {
"foo": {
"type": "keyword"
}
}
}
}
}
PUT index-1-01?include_type_name=true
{
"mappings": {
"type": {
"properties": {
"bar": {
"type": "long"
}
}
}
}
}
PUT index-2-01
{
"mappings": {
"properties": {
"bar": {
"type": "long"
}
}
}
}
6、混合版本集群
在由6.8和7.0节点组成的集群中,应在诸如索引创建之类的索引API中指定参数include_type_name。这是因为参数在6.8和7.0之间具有不同的默认值,因此相同的映射定义对两个节点版本均无效。
无类型文档API(例如批量和更新)仅在7.0以后可用,不适用于6.8节点。对于执行文档查找的无类型查询,例如术语,也是如此。
原文为官方文档,此篇文章为翻译,不能保证完全正确性,有地方存在歧义或者不准确,仅做参考,建议阅读官方原文:https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html