ES之四:Elasticsearch Mapping类型映射概述及高版本中删除mapping

 

一、ES mapping作用

ES的mapping非常类似于java中的数据类型,可以用来设置字段中存储的数据的类型。但ES中的mapping的作用比数据类型广泛一些:

  • 定义索引这里面的字段和名称
  • 定义字段的数据类型,字符串、布尔、数字......
  • 字段,倒排索引相关的配置,是否分词
  • 日期值的格式。
  • 动态添加字段的规则定义等

当你的查询没有返回相应的数据, 你的mapping很有可能有问题。当你拿不准的时候, 直接检查你的mapping。

剖析mapping

一个mapping由一个或多个analyzer组成, 一个analyzer又由一个或多个filter组成的。当ES索引文档的时候,它把字段中的内容传递给相应的analyzer,analyzer再传递给各自的filters。

filter的功能:一个filter就是一个转换数据的方法, 输入一个字符串,这个方法返回另一个字符串,比如一个将字符串转为小写的方法就是一个filter很好的例子。

一个analyzer由一组顺序排列的filter组成,执行分析的过程就是按顺序一个filter一个filter依次调用, ES存储和索引最后得到的结果。

总结来说, mapping的作用就是执行一系列的指令将输入的数据转成可搜索的索引项。

 

二、映射类型

Elasticsearch支持meta-fields、fields or properties两种映射类型,将决定文档的索引方式。

2.1、Meta-fields元数据

元数据字段用于定义文档的元数据字段的特征,文档的元数据字段主要包括_index、_type、_id、_source这4个字段

执行:GET /ecommerce/product/1
返回结果:
{
   "_index": "ecommerce",
   "_type": "product",
   "_id": "1",
   "_version": 1,
   "found": true,
   "_source": {
     "name": "gaolujie yagao",
     "desc": "gaoxiao meibai",
     "price": 30,
     "producer": "gaolujie producer",
     "tags": [
       "meibai",
       "fangzhu"
     ]
   }
}

 字段说明:

4、_source元数据:就是说,我们在创建一个document的时候,使用的那个放在request body中的json串,默认情况下,在get的时候,会原封不动的给我们返回回来。

示例说明:

A、_id:document id。生成方式有两种:

1、自动生成document id

                自动生成的id,长度为20个字符,URL安全,base64编码,GUID,分布式系统并行生成时不可能会发生冲突
                 语法:POST  /index/type
POST /test_index/test_type
{
  "test_content": "my test"
}
返回结果:
{
  "_index": "test_index",
  "_type": "test_type",
  "_id": "AVp4RN0bhjxldOOnBxaE",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}  

 2、手动指定document id

语法:PUT   /index/type/id

PUT /test_index/test_type/2
{
  "test_content": "my test"
}

4、_source元数据

添加document:   
         PUT /test_index/test_type/1
{
   "test_field1": "test field1",
   "test_field2": "test field2"
}
查询指定document:
GET  /test_index/test_type/1

{
   "_index": "test_index",
   "_type": "test_type",
   "_id": "1",
   "_version": 2,
   "found": true,
   "_source": {
     "test_field1": "test field1",
     "test_field2": "test field2"
   }

}

_source元数据:就是说,我们在创建一个document的时候,使用的那个放在request body中的json串,默认情况下,在get的时候,会原封不动的给我们返回回来。

_source的返回结果可定制,可在请求时指定返回的_source中,返回哪些field。如下面的示例:

GET /test_index/test_type/1?_source=test_field1,test_field2
{
   "_index": "test_index",
   "_type": "test_type",
   "_id": "1",
   "_version": 2,
   "found": true,
   "_source": {
     "test_field2": "test field2"
   }

}

 

2.2、Felds or properties

属性字段列表,通过properties字段定义整个文档有效载荷的各字段的数据类型、分词器等属性。
映射类型,可以理解为以何种方式来定义索引中一个类型的字段集。

自定义mapping:

PUT "index_2"    # 索引名
{
    "mappings": {
      "user2": {   # 类型名
        "dynamic":"strict",  #dynamic属性
        "properties": {      # 字段列表
          "age": {           #字段名
            "type": "long"   #字段类型
          },
          "birthday": {
            "type": "date"
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
}

 

三、 数据类型

每一个字段都会指定一个数据类型,数据类型通常如下:

  • 简单类型,例如text、keyword、date、long、double、boolean、ip
  • 复合类型,诸如object(json)、netsed.
  • 特殊类型,诸如geo_point、geo_shape(地图相关类型)、completion。

后续章节会单独重点剖析elasticsearch所支持的数据类型。

四、 映射保护机制

es提供如下参数来限制es的行为:

index.mapping.total_fields.limit t
索引中允许定义的最大字段(属性)个数,默认为1000。
index.mapping.depth.limit
字段级联的最大深度,默认为20。
index.mapping.nested_fields.limit
一个索引最多包含字段类型为nested的个数,默认为50。

五、 创建mapping

5.1 动态映射机制

与关系型数据库不同的是,一个type(对应关系型数据库的表)中的字段可以在使用过程中动态添加。具体的动态映射机制。

动态映射时Elasticsearch的一个重要特性: 不需要提前创建iindex、定义mapping信息和type类型, 你可以 直接向ES中插入文档数据时, ES会根据每个新field可能的数据类型, 自动为其配置type等mapping信息, 这个过程就是动态映射(dynamic mapping).

Elasticsearch动态映射的示例:

字段内容(field)映射的字段类型(type)
true | false boolean
1234 long
123.4 float
2018-10-10 date
"hello world" text

说明: 动态映射虽然方便, 可并不直观, 为了个性化自定义相关设置, 可以在添加文档之前, 先创建index和type, 并配置type对应的mapping, 以取代动态映射.

(写入文档的时候,索引不存在,会自动创建索引, 无需手动创建,ES会根据内容推断字段的类型,推断会不准确,可能造成某些功能无法使用,例如 范围查询。)

体验动态映射

(1) 插入如下数据:

如果已有website索引, 先删除DELETE blog , 再执行下面的插入操作: .

PUT blog/_doc/1
{
    "blog_id": 10001,
    "author_id": 5520,
    "post_date": "2018-01-01",
	  "title": "my first blog",
	  "content": "my first blog in the website"
}

PUT blog/_doc/2
{
    "blog_id": 10002,
    "author_id": 5520,
    "post_date": "2018-01-02",
    "title": "my second blog",
    "content": "my second blog in the website"
}

PUT blog/_doc/3
{
    "blog_id": 10003,
    "author_id": 5520,
    "post_date": "2018-01-03",
    "title": "my third blog",
    "content": "my third blog in the website"
}

(2) 进行如下检索测试:

注意这里结果数是3的情况.

GET blog/_search?q=2018                 // 1条结果, 5.6之前的版本是3条结果
GET blog/_search?q=2018-01-01           // 1条结果, 5.6之前的版本是3条结果
GET blog/_search?q=post_date:2018       // 1条结果
GET blog/_search?q=post_date:2018-01-01	// 1条结果

(3) 查看ES自动建立的mapping:

GET blog/_mapping

// 结果如下:
{
  "blog" : {                  // index是blog
    "mappings" : {
      "_doc" : {              // type是_doc - 方便后期升级版本
        "properties" : {
          "author_id" : {
            "type" : "long"
          },
          "blog_id" : {
            "type" : "long"
          },
          "content" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "post_date" : {
            "type" : "date"  // 日期"2018-01-01"被自动识别为date类型
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}

(4)为已经存在的index增加字段

PUT index_duan/_mapping
{
    "properties": {
        "payee_account_type": {
            "type": "keyword",
            "ignore_above":256
        }
    }
}

 

5.2、搜索结果不一致的原因分析

如果使用的是ES 5.6.x之前的版本, [1.2]节中搜索结果会不一致, 这是因为:

ES自动建立mapping时, 为不同的field映射了不同的数据类型, 而不同数据类型在分词、搜索等行为上也是不同的.

我们通过q=xxx的方式搜索, 底层是从每个文档的_all字段中进行匹配的 —— ES默认将每个文档的所有field的值抽取到一个名为_all的元字段中.

官方文档指出, 从6.0+版本开始, _all字段就被禁止使用了, 建议我们使用copy_to实现相似的功能.—— 也就是说, 如果_all字段被关闭, 就不会出现搜索结果不一致的情况.

(1) GET website/blog/_search?q=2018

id=1的文档的_all的值为:

2018-01-01 my first blog my first blog in the website 5520

说明: _all字段将所有的值作为字符串索引, 所以日期被索引为年、月、日三个值, _all字段的倒排索引结果如下:

 doc1doc2Doc3
2018 * * *
01 * * *
02   *  
03     *

此项搜索中, ES是从_all字段中检索, 3个文档中都有 2018 , 所以结果数是3.

(2) GET website/blog/?q=2018-01-01

同(1), ES也是从_all字段中检索, 结果数同样是3.

(3) GET website/blog/_search?q=post_date:2018-01-01

此检索指定了检索条件, ES将从post_date字段中检索, 而post_date被映射为date类型, 所以将进行精确匹配.

而date类型的字段索引的内容有其默认的固定格式. post_date字段的倒排索引结果如下:

 doc1doc2doc3
2018-01-01 00:00:00 UTC *    
2018-01-02 00:00:00 UTC   *  
2018-01-03 00:00:00 UTC     *

可以看出, 满足条件的只有1个结果, 即doc1.

(4) GET /_search?q=post_date:2018

这是ES 5.x版本中做的一个优化, 搜索post_date:01等是不会出现结果的, 搜索2018会出现第一条结果.

5.3 开启dynamic mapping动态映射策略

    • 新增字段
      • dynamic设置为true,一旦有新增字段的文档写入,mapping也同时被更新。
      • dynamic设置为false,mapping不会被更新,新增的字段数据无法被索引,但是信息会出现在source中.
      • dynamic设置为strict,文档写入失败
    • 已有的字段,一旦有数据写入,不支持修改(倒排索引不支持修改)
    • 希望更改字段类型,用Reindex API,重建索引(es不支持直接修改已索引的已存在的字段映射,因为修改字段映射,意味着已索引的数据生效,可以使用别名机制来修改字段的名称,如果需要修改已存在字段的映射,建议重新创建一个索引,再使用reindex API迁移数据。)
      设计原因
    • 如果修改字段数据类型,会导致已经被索引的文档不能被搜索。
    • 新增字段不存在影响。

 

约束策略

策略功能说明
true 开启 —— 遇到陌生字段时, 进行动态映射
false 关闭 —— 忽略遇到的陌生字段
strict 遇到陌生字段时, 作报错处理

策略示例

(1) 使用不同的约束策略:

PUT blog_user
{
  "mappings": {
      "_doc": {
          "dynamic": "strict",			// 严格控制策略
          "properties": {
              "name": { "type": "text" },
              "address": {
                  "type": "object",
                  "dynamic": "true"		// 开启动态映射策略
              }
          }
      }
  }
}

(2) 插入数据演示:

// 插入数据时多添加一个字段
PUT blog_user/1
{
    "name": "shou feng",
    "content": "this is my blog",  // 多添加的字段
    "address": {
        "province": "guangdong",
        "city": "guangzhou"
    }
}

将抛出如下错误信息:

{
  "error": {
    "root_cause": [
      {
        "type": "strict_dynamic_mapping_exception",
        // 错误原因: 不允许动态添加field
        "reason": "mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed"
      }
    ],
    "type": "strict_dynamic_mapping_exception",
    "reason": "mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed"
  },
  "status": 400
}

添加符合约束的数据, 操作就能成功:

PUT blog_user/_doc/1
{
    "name": "shou feng",
    "address": {
        "province": "guangdong",
        "city": "guangzhou"
    }
}

(3) 查看映射信息:

GET user/_mapping

// 映射信息如下: 
{
  "blog_user" : {
    "mappings" : {
      "_doc" : {
        "dynamic" : "strict",      // 严格约束条件
        "properties" : {
          "address" : {
            "dynamic" : "true",    // 开启动态映射策略
            "properties" : {
              "city" : {
                "type" : "text",
                "fields" : {
                  "keyword" : {
                    "type" : "keyword",
                    "ignore_above" : 256
                  }
                }
              },
              "province" : {
                "type" : "text",
                "fields" : {
                  "keyword" : {
                    "type" : "keyword",
                    "ignore_above" : 256
                  }
                }
              }
            }
          },
          "name" : {
            "type" : "text"
          }
        }
      }
    }
  }
}

5.4 定制dynamic mapping策略

5.4.1、date_detection - 日期识别策略

对于date类型的数据, Elasticsearch有其默认的识别策略, 比如"yyyy-MM-dd". 存在这种情况:

① 第一次添加文档时, 某个field是类似"2018-01-01"的值, 其类型就动态映射成date;
② 后期再次添加文档, 该field是类似"hello world"的值, ES就会因为类型不匹配而报错.

为解决这一问题, 可以手动关闭某个type的date_detection; 如果不需要关闭, 建议手动指定这个field为date类型. 示例如下:

PUT blog_user/_mapping/_doc
{
    "date_detection": false
}

5.4.2、 在type中自定义动态映射模板

(1) 在type中定义动态映射模板(dynamic mapping template) —— 把所有的String类型映射成text和keyword类型:

先删除已有的blog_user索引: DELETE blog_user, 再执行下述命令:

PUT blog_user
{
    "mappings": {
        "_doc": {
            "dynamic_templates": [
                {
                    "en": {       // 动态模板的名称
                        "match": "*_en",           // 匹配名为"*_en"的field
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "text",        // 把所有的string类型, 映射成text类型
                            "analyzer": "english", // 使用english分词器
                            "fields": {
                                "raw": {
                                    "type": "keyword",
                                    "ignore_above": 256
                                }
                            }
                        }
                    }
                }
            ]
        }
    }
}

(2) 添加数据:

PUT blog_user/_doc/1
{
    "name": "the first register user"
}

PUT blog_user/_doc/2
{
    "name_en": "the second register user"
}

(3) 检索数据:

// 有结果: "name"没有匹配到任何动态模板, 默认使用standard分词器
GET blog_user/_search
{
    "query": {
        "match": {"name": "the"}
    }
}

// 无结果: "name_en"匹配到了动态模板, 使用english分词器, the是停用词, 被过滤掉了
GET blog_user/_search
{
    "query": {
        "match": {"name_en": "the"}	
    }
}

说明:

这里的match_mapping_type的类型支持 [object, string, long, double, boolean, date, binary], 若使用text将抛出如下错误信息:

{
  "error": {
    "root_cause": [
      {
        "type": "mapper_parsing_exception",
        "reason": "Failed to parse mapping [_doc]: No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]"
      }
    ],
    "type": "mapper_parsing_exception",
    "reason": "Failed to parse mapping [_doc]: No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]",
    "caused_by": {
      "type": "illegal_argument_exception",
      "reason": "No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]"
    }
  },
  "status": 400
}

在6.0之前的版本, 将抛出如下过期的警告信息:

Deprecation: match_mapping_type [text] is invalid and will be ignored: 
No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]

5.5 [过期]在index中自定义默认映射模板

_default mapping - 默认映射模板是类似于全局变量的存在, 对当前配置的索引起作用.

默认映射模板在Elasticsearch 6.x版本中已经不再支持, 因为6.0版本开始, 每个索引只能有一个类型, 因此默认模板没有存在的意义了.

下面的演示过程, 是在6.0之前的版本上的测试.

(1) 在index中定义默认映射模板(default mapping template):

先删除已有的blog_user索引: DELETE blog_user, 再执行下述命令:

PUT blog_user
{
    "mappings": {
        "_default_": {
            "_all": { "enabled":  false }
        },
        "_doc": {
            "_all": { "enabled":  true  }
        }
    }
}

(2) 动态映射可以和索引模板(Index Templates)配合使用.

无论是自动创建Index, 还是手动显式创建Index, 索引模板都可以用来配置新索引的默认mappings(映射)、settings(配置项)和aliases(别名). 具体使用方法请参考博客: ES 10 - 如何使用Elasticsearch的索引模板(index template)

5.6 显示的设置mapping信息

显示的设置mapping可以更灵活控制ES。

根据API手册,手写,减少出错概率的方法。

  • 创建一个临时的index,写入一些样本数据
  • 通过mapping api获得该临时文件的动态mapping定义
  • 修改后用,使用该配置创建索引
  • 删除临时索引

5.6.1 控制字段是否被index

index的选项配置

  • docs:记录doc id
  • freqs:记录doc id和term的频次
  • position: 记录doc id、term频次、term位置
  • offsets:doc id、term频次、term位置、字符串的偏移量

text类型默认是positions,其他的默认为docs,记录内容越多,占据空间越大

例如创建mapping,字段名为user_name,字符串类型。不需要索引,info字段的倒排索引类型为positions。

PUT mapping_test3
{
  "mappings": {
    "properties": {
      "user_name":{
        "index": false,
        "type": "text"
      },
      "info":{
        "index_options": "positions",
        "type": "text"
      }
    }
  }
}

5.6.2 null_value

对Null值进行搜索,可以通过keyword类型的字段设置null_value。

"mobile":{
     "type":"keyword",
      "null_value":"NULL"
 }

就可以对NULL搜索:GET users/_search?q=mobile:NULL

5.6.3 copy_to

  • 用来满足一些搜索需要,类似于数据库 title like "%a%" or title2 like "%a%"
  • copy_to的字段不会出现在_source里面
PUT users
{
  "mappings": {
    "properties": {
      "first_name":{
        "type": "text",
        "copy_to": "full_name"
      },
      "last_name":{
        "type": "text",
        "copy_to": "full_name"
      }
    }
  }
}

 

就可以在支持 :GET users/_search?q=full_name:(zhang san)搜索。

 

六、mapping的弃用

index和type的关系

Index是什么 :Index 存储在多个分片中,其中每一个分片都是一个独立的 Lucene Index。这就应该能提醒你,添加新 index 应该有个限度:每个 Lucene Index 都需要消耗一些磁盘,内存和文件描述符。因此,一个大的 index 比多个小 index 效率更高:Lucene Index 的固定开销被摊分到更多文档上了。
另一个重要因素是你准备怎么搜索你的数据。在搜索时,每个分片都需要搜索一次, 然后 ES 会合并来自所有分片的结果。例如,你要搜索 10 个 index,每个 index 有 5 个分片,那么协调这次搜索的节点就需要合并 5x10=50 个分片的结果。这也是一个你需要注意的地方:如果有太多分片的结果需要合并,或者你发起了一个结果巨大的搜索请求,合并任务会需要大量 CPU 和内存资源。这是第二个让 index 少一些的理由。
type 是什么:使用 type 允许我们在一个 index 里存储多种类型的数据,这样就可以减少 index 的数量了。在使用时,向每个文档加入 _type 字段,在指定 type 搜索时就会被用于过滤。使用 type 的一个好处是,搜索一个 index 下的多个 type,和只搜索一个 type 相比没有额外的开销 —— 需要合并结果的分片数量是一样的。
但是,这也是有限制的:
  • 不同 type 里的字段需要保持一致。例如,一个 index 下的不同 type 里有两个名字相同的字段,他们的类型(string, date 等等)和配置也必须相同。
  • 只在某个 type 里存在的字段,在其他没有该字段的 type 中也会消耗资源。这是 Lucene Index 带来的常见问题:它不喜欢稀疏。由于连续文档之间的差异太大,稀疏的 posting list 的压缩效率不高。这个问题在 doc value 上更为严重:为了提高速度,doc value 通常会为每个文档预留一个固定大小的空间,以便文档可以被高速检索。这意味着,如果 Lucene 确定它需要一个字节来存储某个数字类型的字段,它同样会给没有这个字段的文档预留一个字节。未来版本的 ES 会在这方面做一些改进,但是我仍然建议你在建模的时候尽量避免稀疏。[1]
  • 得分是由 index 内的统计数据来决定的。也就是说,一个 type 中的文档会影响另一个 type 中的文档的得分。

 

这意味着,只有同一个 index 的中的 type 都有类似的映射 (mapping) 时,才应该使用 type。否则,使用多个 type 可能比使用多个 index 消耗的资源更多。

 

索引在创建时,Elasticsearch6.x版本只支持一个映射类型,而7.x版本后将完成删除映射类型。es5.x中一个索引包含多个type的情况再6.x版本将继续支持查询,7.0版本后,API将完成移除与多类型相关的API

 

Elasticsearch6.x版本后为什么不继续对单一索引库提供多类型支持呢?
当初,为了方便理解es,通常与关系型数据库进行类比,例如es中的index(索引)相当于关系型数据库的database,而类型(type)相当于关系型数据库中的table。其实这是一个错误的比喻。在关系型数据库中,表是相互独立的,一个表中的列名与另外一个表中的列名相同是没有关系的,但对于es的类型映射定义,情况并非如此。
在es单一索引中,不同映射类型(type)具有相同名称的字段在内部都是由同一个Lucence字段来存储,这也就意味着同一个索引内不同的类型,如果出现名字相同的字段,其数据类型也必须相同。更重要的是,存储在同一索引中具有很少或没有共同字段的不同类型(实体)会导致数据稀疏,大大降低Lucece高效压缩文档的能力,影响其检索性能。
基于上述各种原因,故es将在后续版本中不支持一个索引中定义多个类型

 

每个文档都有与之关联的元数据,例如_index、mapping _type和_id元字段。在创建映射类型时,可以定制其中一些元字段的行为。

identity meta-fields(表明文档身份的元字段)
_index
文档所在的索引,类似于关系型数据库的database。
_uid
_type与_id的组合,文档的唯一标识。
_type
文档映射类型。
_id
文档的_id值。
document source meta-fields
_source
文档的原始json数据。
_size
文档_souce字段的字节长度,需要插件:mapper-size plugin。
indexing meta-fields
_all
将所有字段映射成一个_all字段,在6.0.0版本后废弃,可以使用copy_to来定义需要聚合的字段。
_field_names
_field_names字段,用于索引文档中包含除null之外的任何值的每个字段的名称。exist查询使用这个字段来查找对于特定字段具有或不具有任何非空值的文档,也就是该字段记录的是字段值不为null的所有字段名称。当前版本,_field_names字段不包含启用了doc_values、norm的字段,对于启用doc_values或norm的字段,exist查询仍然可用,但不会使用_field_names字段。
注:禁用_field_names通常是不必要的,因为它不再承载以前的索引开销。如果你有很多禁用doc_value和norm的字段,并且你不需要使用这些字段执行exist查询,你可能想禁用_field_names,你可以通过如下方式禁用_field_names字段:

PUT tweets
{
  "mappings": {
    "_doc": {
      "_field_names": {
        "enabled": false
      }
    }
  }
}

_ignored
设置为ignore_malformed=true的所有字段。
routing meta-field
_routing
分片路由字段。
other meta-field
1._meta
用于用户自定义的元数据,例如:

PUT my_index
{
  "mappings": {
    "_doc": {
      "_meta": { 
        "class": "MyApp::User",
        "version": {
          "min": "1.0",
          "max": "1.3"
        }
      }
    }
  }
}

本文初步介绍了Elasticsearch 类型映射与元字段类型,后续文章将介绍映射参数、elasticsearch支持的数据类型、自动类型映射机制等。

3、映射类型(mapping type)从Elasticsearch中移除

注意:在Elasticsearch6.0.0或者或者更新版本中创建的索引只会包含一个映射类型(mapping type)。在5.x中创建的具有多个映射类型的索引在Elasticsearch6.x中依然会正常工作。在Elasticsearch7.0.0中,映射类型将会被完全移除。

什么是映射类型?

从Elasticsearch的第一个发行版开始,每一个文档都会被存储在一个单独的索引中,并且配以一个单独的映射类型。一个映射类型被用来表示被索引的文档或者实体的类型,比如一个twitter索引可能会有一个user类型和一个tweet类型。

每一个映射类型都可以有其自身的字段,所以user类型可能有一个full_name字段,一个user_name字段和一个email字段,而tweet类型可能会包含一个content字段,一个tweeted_at字段,以及与user类型中类似的user_name字段。

每个文档都有一个_type元字段用来保存类型名,搜索可以通过在URL中指定类型名将搜索限定于一个或多个类型中:

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

 

_type字段的值会与文档的_id字段的值组合起来生成_uid字段,所以具有相同_id的不同类型的多个文档可以共存于一个索引中。

映射类型也被用来建立文档之间的父子关系,比如question类型的文档可以是answer类型文档的父亲。

 

为什么要移除映射类型(mapping types

开始的时候,我们说“索引(index)”类似于SQL数据库中的“数据库”,将“类型(type)”等同于“表”。

这是一个糟糕的类比,并且导致了一些错误的假设。在SQL数据库中,表之间是相互独立的。一个表中的各列并不会影响到其它表中的同名的列。而在映射类型(mappingtype)中却不是这样的。

在同一个Elasticsearch索引中,其中不同映射类型中的同名字段在内部是由同一个Lucene字段来支持的。换句话说,使用上面的例子,user类型中的user_name字段与tweet类型中的user_name字段是完全一样的,并且两个user_name字段在两个类型中必须具有相同的映射(定义)。

这会在某些情况下导致一些混乱,比如,在同一个索引中,当你想在其中的一个类型中将deleted字段作为date类型,而在另一个类型中将其作为boolean字段。

在此之上需要考虑一点,如果同一个索引中存储的各个实体如果只有很少或者根本没有同样的字段,这种情况会导致稀疏数据,并且会影响到Lucene的高效压缩数据的能力。

基于这些原因,将映射类型的概念从Elasticsearch中移除。

 

映射类型的可选替代方案

每种文档类型一个索引

第一种选择就是每个文档类型对应一个索引。你可以不将tweets和users存储于同一个索引,而将它们分别存储于tweets索引和users索引中。索引之间是完全相互独立的,不同索引中的(同名的)字段类型也就不会产生冲突了。

这种方式有两个好处:

  • 数据更倾向于密集(而不是稀疏),这样就能获益于Lucene的压缩技术。

  • 因为同一个索引中的所有的文档代表同一种实体,用于为全文搜索打分的条件统计会更精确。

每个索引可以依据其可能的文档存储量级来设置相关的配置:可以对users使用较少的主分片,同时对tweets使用较大数量的主分片。

自定义类型字段

当然,一个集群中可以创建的主分片的数量是有限制的,所以你可能不想为一个只有几千个文档的集合去浪费一整个分片。这种情况下你可以使用你自己定义的type字段,它看起来和原来的_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": "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"
    }
  }
}

映射类型的移除计划

对于用户来说这是一个大的变化,所以我们尽可能将这个过程变得平滑。这次变更的计划如下:

Elasticsearch 5.6.0

  • 在一个索引上设置index.mapping.single_type:true会启用单索引-单一类型的行为,而这种行为在6.0中是强制的。

  • 在5.6中创建的索引可以使用创建文档间父子关系的新的join字段。

Elasticsearch 6.x

  • 5.x中创建的索引可以在6.x中正常工作

  • 6.x中创建的索引只允许单个索引中存在单一的类型。任意的名字都可以作为类型,但是只能有一个。

  • _type名不再与_id组合生成_uid字段。_uid字段变成仅仅是_id字段的别名。

  • 新创建的索引不再支持旧风格的父子关系,而应该使用join字段。

  • _default_映射类型被标记为不推荐使用。

Elasticsearch 7.x

  • URL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。

  • GET|PUT_mapping API支持一个查询字符串参数(include_type_name),通过这个参数来指定请求体是否应该包含一个类型名的层。默认是true7.x中没有显式指定类型的索引将会使用默认的_doc类型名。

  • _default_映射类型移除。

Elasticsearch 8.x

  • 不再支持URL中的type参数。

  • include_type_name参数默认为false

Elasticsearch 9.x

  • include_type_name参数移除。

将索引从多类型迁移到单类型

可以用ReindexAPI将多类型索引转化成单类型索引。下面的例子可以在Elasticsearch 5.6或者Elasticsearch6.x中使用。在6.x中,不必指定index.mapping.single_type,因为6.x中是默认开启的。

 

转:https://blog.csdn.net/prestigeding/article/details/84537296

参考:https://blog.csdn.net/cnweike/article/details/79223542

参考:https://www.jianshu.com/p/f1e5f0fde294

posted on 2015-12-27 23:23  duanxz  阅读(7720)  评论(0编辑  收藏  举报