一、概述
(1)映射
映射是定义文档如何被存储和索引的,包括含有哪些字段,字段的类型,字符串类型的字段应当作为全文本还是关键字,日期字段的格式等等。
(2)映射类型
每个索引都有唯一一个映射类型(Elasticsearch计划去除映射类型,详情看第二节),映射类型决定了文档是如何被索引的。一个索引类型包括了:
元字段:用于定义文档的元数据关联如何处理。元字段包括文档的_index字段、_type字段、_id字段和_source字段。
字段或属性:每个映射类型包含文档相关的一系列字段或属性。
(3)字段数据类型
每个字段拥有一个数据类型。可以是简单数据类型,比如文本型(text)、关键字型(keyword)、日期型(date)、长整型(long)、双精度浮点型(double)、布尔型(boolean)或者IP;也可以是支持JSON的层次性的类型,比如对象(object)、嵌套(nested);或者其它特殊类型,比如地理点(geo_point)、地理形状(geo_shape)、完成型(completion)。
为了不同的目的,以不同的方式对相同的字段进行索引通常是有用的。 例如,字符串字段可以被索引为全文搜索的文本字段,以及用于排序或聚合的关键字字段。
这是多字段的目的。 大多数数据类型通过fields参数支持多字段。
(4)防止映射爆炸的设置
在索引中定义太多的字段是可能导致映射爆炸,这会导致内存错误和难以恢复的情况。 这个问题可能比预期的更普遍。 例如,考虑插入每个新文档引入新字段的情况。 这在动态映射中很常见。 每次文档包含新字段时,这些都将导致索引的映射增长。 少量的数据这不需要担心,但随着映射的增长,这可能成为一个问题。 以下设置允许您限制手动或动态创建的字段映射的数量,以防止不良文档导致映射爆炸:
index.mapping.total_fields.limit
索引的最大字段数,默认为1000。
index.mapping.depth.limit
字段的最大深度,该深度用于表示内部对象的数量。例如,如果所有字段都是在根级别对象定义的,则深度为1。如果存在一个对象映射,则深度为2,以此类推。默认值是20。
index.mapping.nested_fields.limit
索引中嵌套字段的最大数目,默认为50。索引一个含有100个嵌套字段的文档实际上索引了101文档, 因为每个嵌套文档都被索引为单独的隐藏文档。
(5)动态映射
字段和映射类型在使用之前不需要定义。由于动态映射,只需索引一个文档,新的字段名称可以自动加入。可以将新字段添加到顶层映射类型以及内部对象和嵌套字段。
可以配置新字段的动态映射规则。
(6)显示映射
如果你非常了解自己的数据,而无需elasticsearch去猜测,你可以显示指定映射。可以在创建索引时,创建字段映射;也可以使用PUT映射API向已存在的映射添加字段。
(7)更新已存在的字段映射
除了文档的位置,已存在字段映射不能够更新。改变映射意味着已索引文档无效。相反,你应该用正确的映射创建新的索引,并重新索引你的数据到新的索引上。
二、去除映射类型
注意:在Elasticsearch 6.0.0或更高版本中创建的索引可能只包含一个映射类型。在5.x中创建的 使用多种映射类型的索引将继续像以前一样在Elasticsearch 6.x中运行。 映射类型将在Elasticsearch 7.0.0中完全删除。
什么是映射类型?
自Elasticsearch首次发布以来,每个文档都被存储在一个索引中,并被分配了一个映射类型。 映射类型用于表示被索引的文档或实体的类型,例如,twitter索引可能有一个user类型和tweet类型。
每个映射类型可以有自己的字段,所以user类型可能有一个full_name字段,一个user_name字段和一个email字段,而tweet类型可以有一个content字段,一个tweeted_at字段,和一个与用户类型一样的user_name字段。
每个文档都有一个包含类型名称的_type元字段,通过在URL中指定类型名称,可以限制搜索一种或多种类型:
GET twitter/user,tweet/_search
{
"query": {
"match": {
"user_name": "kimchy"
}
}
}
_type字段与文档的_id结合生成_uid字段,因此具有相同_id的不同类型的文档可以存在于同一个索引中。
也使用映射类型来建立文档之间的父子关系。
为什么要去除映射类型?
最初,我们谈到了索引类似于SQL数据库中的“数据库”,类型与表相当。
这是一个不好的类比,导致错误的假设。 在一个SQL数据库中,表是相互独立的。 一个表中的列与另一个表中相同名称的列没有关系。 映射类型的字段不是这种情况。
在Elasticsearch索引中,在不同映射类型中具有相同名称的字段在内部由相同的Lucene字段支持。 换句话说,使用上面的示例,用户类型中的user_name字段存储在与tweet类型中的user_name字段完全相同的字段中,并且这两个user_name字段在两种类型中都必须具有相同的映射(定义)。
最重要的是,在同一索引中,存储有少量公共字段(甚至没有公共字段)的不同实体,会导致稀疏数据并影响Lucene高效压缩文档的能力。
由于这些原因,Elasticsearch将去除映射类型的概念。
替代映射类型
- 一个索引一个文档类型
第一个选择是每个文档类型都有一个索引。 不将tweet和user存储在同一个twitter索引中,而是将tweet存储在tweet索引中,并将user存储在user索引中。索引是完全相互独立的,所以索引之间不存在字段类型的冲突。
这种方法有两个好处:
(1)数据更可能是密集的,因此受益于Lucene中使用的压缩技术。
(2)在全文搜索中用于计分的索引词统计更可能是准确的,因为同一索引中的所有文档都代表单个实体。
每个索引的大小可以根据其包含的文档数量适当调整:user可以使用较小数量的主分片,并tweet可以使用较大数量的主分片。
- 自定义类型字段
当然,在一个集群中可以存在的主分片数是有限制的。所以,你可能不想为只有几千个文档的集合浪费整个分片。在这种情况,你可以实现自己的自定义类型字段,它将以类似于旧的_type的方式工作。
以上面的user/tweet为例。 最初,工作流将看起来像这样:
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": "[email protected]"
}
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": "[email protected]"
}
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字段。
迁移多类型索引到单类型
Reindex API可用于将多类型索引转换为单类型索引。 以下示例可用于Elasticsearch 5.6或Elasticsearch 6.x。在6.x中,不需要指定index.mapping.single_type,因为这是默认值。
- 一个索引一个文档类型
第一个示例将我们的Twitter索引分成一个tweets索引和一个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"
}
}
- 自定义类型字段
下面的示例添加一个自定义类型字段, 并将其设置为原始 _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';
"""
}
}