在ES6.0.0及更高的版本中,创建的索引只能包含一个映射类型。在6.0.0以下的版本中创建的一个索引映射多个类型的索引在6.0.0版本中继续发挥作用,但是将在7.0.0中完全删除。
自第一次发布弹出式搜索以来,每个文档都存储在一个索引中,并分配一个映射类型。映射类型用于表示被索引的文档或实体的类型,例如Twitter索引可能具有user类型和tweet类型。
每个映射类型都可以有自己的字段,因此用户类型可能有一个FuleNyNoX字段、一个用户名称字段和一个电子邮件字段,而Twitter类型可以有一个内容字段、一个TwiteEdEdAT字段和一个用户类型,一个用户名称字段。
每个文档都有一个包含类型名称的“类型”元字段,并且可以通过指定URL中的类型名称来限制搜索到一个或多个类型:
GET twitter/user,tweet/_search
{
"query": {
"match": {
"user_name": "kimchy"
}
}
}
_type
字段和文档的_id
字段结合生成一个UUID
字段,因此在同一个索引中可以存在不同类型的具有相同SyID的文档。
为什么删除多个类型映射?
我们谈到了一个类似于SQL数据库中的“数据库”的“索引”,一个“类型”相当于一个“表”。
这是一个糟糕的类比,导致了错误的假设。在SQL数据库中,表是相互独立的。一个表中的列与另一个表中具有相同名称的列没有关联。这不是映射类型中的字段的情况。
在弹性搜索索引中,在不同的映射类型中具有相同名称的字段由相同的Lucene字段在内部支持。换句话说,使用上面的示例,用户类型中的UsRyNoNd字段与Twitter类型中的UsSeriNoX字段完全相同的字段,并且两个用户名称字段必须在两种类型中都具有相同的映射(定义)。
例如,当您希望删除一个类型中的日期字段和在同一索引中的另一个类型中的布尔字段时,这可能会导致挫折。
除此之外,在同一索引中存储很少或没有相同字段的不同实体会导致稀疏数据并干扰Lucene高效压缩文档的能力。
如何解决多个类型映射问题?
第一种选择是每个文档类型都有索引。您可以在推特索引和用户索引中存储Twitter,而不是在单个Twitter索引中存储Twitter和用户。索引是完全独立的,因此在索引之间不会有字段类型冲突。
这种方法有两个好处:
user
使用较少数量的分片和用于Twitter
的更大数量的分片。当然,在集群中存在多少个主碎片是有限度的,所以您可能不想浪费一个只有几千个文档集合的碎片。在这种情况下,您可以实现自己的自定义类型字段,该字段将以类似于旧类型的方式工作。
旧的实现方式:
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"
}
}
}
}
}
以前,父子关系是由一个映射类型为父函数和一个或多个其他映射类型的子代来表示的。没有类型,我们就不能再使用这个语法了。父子特征将一如既往地起作用,除了表示文档之间关系的方式已被更改为使用新的联接字段。
各个版本对类型操作的变化如下:
Elasticsearch 5.6.0
index.mapping.single_type: true
on an index will enable the Elasticsearch 6.x
_type
name can no longer be combined with the _id
to form the _uid
field. The _uid
field has become an alias for the _id
field._default_
mapping type is Elasticsearch 7.x
GET|PUT _mapping APIs
support a query string parameter include_type_name
) which indicates whether the body should include a _doc
._default_
mapping type is removed.Elasticsearch 8.x
Elasticsearch 9.x
include_type_name
parameter is removed.The Reindex API 可以用于将多类型索引转换为单类型索引:
这个第一个例子把我们的Twitter索引分成一个tweet索引和一个user索引:
PUT users
{
"settings": {
"index.mapping.single_type": true
},
"mappings": {
"user": {
"properties": {
"name": {
"type": "text"
},
"user_name": {
"type": "keyword"
},
"email": {
"type": "keyword"
}
}
}
}
}
PUT tweets
{
"settings": {
"index.mapping.single_type": true
},
"mappings": {
"tweet": {
"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"
}
}
自定义类型字段
下一个示例添加自定义类型字段,并将其设置为原始类型的值。它还将类型添加到yid中,以防存在不同类型的具有冲突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';
"""
}
}