公号:码农充电站pro
主页:https://codeshellme.github.iohtml
ES 中的 Mapping 至关于传统数据库中的表定义,它有如下做用:git
一个 Mapping 是针对一个索引中的 Type 定义的:github
经过下面语法能够获取一个索引的 Mapping 信息:shell
GET index_name/_mapping
字段的 mapping 能够设置不少参数,以下:数据库
false
,表示数据仅作存储,不支持搜索和聚合分析(数据保存在 _source 中)。
true
。false
,表示不创建倒排索引(节省空间),同时数据也没法被搜索,但依然支持聚合分析,数据也会出如今 _source 中。true
。false
,可节省空间。true
。false
,以节省磁盘空间。
true
。true
。
false
。false
,数据存储在 _source 中。
true
,false
,strict
。null
的值。让一个字段拥有多个子字段类型,使得一个字段可以被多个不一样的索引方式进行索引。数组
示例 1:app
PUT index_name { "mappings": { # 设置 mappings "properties": { # 属性,固定写法 "city": { # 字段名 "type": "text", # city 字段的类型为 text "fields": { # 多字段域,固定写法 "raw": { # 子字段名称 "type": "keyword" # 子字段类型 } } } } } }
示例 2 :elasticsearch
PUT index_name { "mappings": { "properties": { "title": { # 字段名称 "type": "text", # 字段类型 "analyzer": "english", # 字段分词器 "fields": { # 多字段域,固定写法 "std": { # 子字段名称 "type": "text", # 子字段类型 "analyzer": "standard" # 子字段分词器 } } } } } }
ES 中字段的数据类型有如下这些:ide
text 类型与 keyword 类型oop
字符串数据能够定义成 text 或 keyword 类型,text 类型数据会作分词处理,而 keyword 类型数据不会作分词处理。
数组类型
对于数组类型 Arrays,ES 并无提供专门的数组类型,可是任何字段均可以包含多个相同类型的数据,好比:
["one", "two"] # 一个字符串数组 [1, 2] # 一个整数数组 [1, [ 2, 3 ]] # 至关于 [ 1, 2, 3 ] [{ "name": "Mary", "age": 12 }, { "name": "John", "age": 10 }] # 一个对象数组
当在 Mapping 中查看这些数组的类型时,其实仍是数组中的元素的类型,而不是一个数组类型。
Nested 是一种对象类型,它保留了子字段之间的关系。
假如咱们有以下结构的数据:
POST my_movies/_doc/1 { "title":"Speed", "actors":[ # actors 是一个数组类型,数组中的元素是对象类型 { "first_name":"Keanu", "last_name":"Reeves" }, { "first_name":"Dennis", "last_name":"Hopper" } ] }
将数据插入 ES 以后,执行下面的查询:
# 查询电影信息 POST my_movies/_search { "query": { "bool": { "must": [ {"match": {"actors.first_name": "Keanu"}}, {"match": {"actors.last_name": "Hopper"}} ] } } }
按照上面的查询语句,咱们想查询的是 first_name=Keanu
且 last_name=Hopper
的数据,因此咱们刚才插入的 id 为 1 的文档应该不符合这个查询条件。
可是在 ES 中执行上面的查询语句,却能查出 id 为 1 的文档。这是为何呢?
这是由于,ES 对于这种 actors 字段这样的结构的数据,ES 并无考虑对象的边界。
实际上,在 ES 内部,id 为 1 的那个文档是这样存储的:
"title":"Speed" "actors.first_name":["Keanu","Dennis"] "actors.last_name":["Reeves","Hopper"]
因此这种存储方式,并非咱们想象的那样。
若是咱们查看 ES 默认为上面(id 为 1)结构的数据生成的 mappings,以下:
{ "my_movies" : { "mappings" : { "properties" : { "actors" : { # actors 内部又嵌套了一个 properties "properties" : { "first_name" : { # 定义 first_name 的类型 "type" : "text", "fields" : { "keyword" : {"type" : "keyword", "ignore_above" : 256} } }, "last_name" : { # 定义 last_name 的类型 "type" : "text", "fields" : { "keyword" : {"type" : "keyword", "ignore_above" : 256} } } } }, # end actors "title" : { "type" : "text", "fields" : { "keyword" : {"type" : "keyword", "ignore_above" : 256} } } } } } }
那如何才能真正的表达一个对象类型呢?这就须要使用到 Nested 类型。
Nested 类型容许对象数组中的对象被独立(看做一个总体)索引。
咱们对 my_movies 索引设置这样的 mappings:
DELETE my_movies PUT my_movies { "mappings" : { "properties" : { "actors" : { "type": "nested", # 将 actors 设置为 nested 类型 "properties" : { # 这时 actors 数组中的每一个对象就是一个总体了 "first_name" : {"type" : "keyword"}, "last_name" : {"type" : "keyword"} }}, "title" : { "type" : "text", "fields" : {"keyword":{"type":"keyword","ignore_above":256}} } } } }
写入数据后,在进行这样的搜索,就不会搜索出数据了:
# 查询电影信息 POST my_movies/_search { "query": { "bool": { "must": [ {"match": {"actors.first_name": "Keanu"}}, {"match": {"actors.last_name": "Hopper"}} ] } } }
可是这样的查询也查不出数据:
POST my_movies/_search { "query": { "bool": { "must": [ {"match": {"actors.first_name": "Keanu"}}, {"match": {"actors.last_name": "Reeves"}} ] } } }
这是由于,查询 Nested 类型的数据,要像下面这样查询:
POST my_movies/_search { "query": { "bool": { "must": [ { "nested": { # nested 查询 "path": "actors", # 自定 actors 字段路径 "query": { # 查询语句 "bool": { "must": [ {"match": {"actors.first_name": "Keanu"}}, {"match": {"actors.last_name": "Hopper"}} ] } } } # end nested } ] # end must } # end bool } }
对 Nested 类型的数据进行聚合,示例:
# Nested Aggregation POST my_movies/_search { "size": 0, "aggs": { "actors": { # 自定义聚合名称 "nested": { # 指定 nested 类型 "path": "actors" # 聚合的字段名称 }, "aggs": { # 子聚合 "actor_name": { # 自定义子聚合名称 "terms": { # terms 聚合 "field": "actors.first_name", # 子字段名称 "size": 10 } } } } } }
使用普通的聚合方式则没法工做:
POST my_movies/_search { "size": 0, "aggs": { "actors": { # 自定义聚合名称 "terms": { # terms 聚合 "field": "actors.first_name", "size": 10 } } } }
Nested 类型的对象与其父/子级文档的关系,使得每次文档有更新的时候须要重建整个文档(包括根对象和嵌套对象)的索引。
Join 数据类型(相似关系型数据库中的 Join 操做)为同一索引中的文档定义父/子关系。
Join 数据类型能够维护一个父/子关系,从而分离两个对象,它的优势是:
Nested 类型与 Join(Parent/Child) 类型的优缺点对比:
定义 Join 类型的语法以下:
DELETE my_blogs # 设定 Parent/Child Mapping PUT my_blogs { "mappings": { "properties": { "blog_comments_relation": { # 字段名称 "type": "join", # 定义 join 类型 "relations": { # 定义父子关系 "blog": "comment" # blog 表示父级文档,comment 表示子级文档 } }, "content": { "type": "text" }, "title": { "type": "keyword" } } } }
先插入两个父文档:
# 插入 blog1 PUT my_blogs/_doc/blog1 { "title":"Learning Elasticsearch", "content":"learning ELK @ geektime", "blog_comments_relation":{ "name":"blog" # name 为 blog 表示父文档 } } # 插入 blog2 PUT my_blogs/_doc/blog2 { "title":"Learning Hadoop", "content":"learning Hadoop", "blog_comments_relation":{ "name":"blog" # name 为 blog 表示父文档 } }
插入子文档:
# 插入comment1 PUT my_blogs/_doc/comment1?routing=blog1 # routing 的值是父文档 id { # 确保父子文档被索引到相同的分片 "comment":"I am learning ELK", "username":"Jack", "blog_comments_relation":{ "name":"comment", # name 为 comment 表示子文档 "parent":"blog1" # 指定父文档的 id,表示子文档属于哪一个父文档 } } # 插入 comment2 PUT my_blogs/_doc/comment2?routing=blog2 # routing 的值是父文档 id { # 确保父子文档被索引到相同的分片 "comment":"I like Hadoop!!!!!", "username":"Jack", "blog_comments_relation":{ "name":"comment", # name 为 comment 表示子文档 "parent":"blog2" # 指定父文档的 id,表示子文档属于哪一个父文档 } } # 插入 comment3 PUT my_blogs/_doc/comment3?routing=blog2 # routing 的值是父文档 id { # 确保父子文档被索引到相同的分片 "comment":"Hello Hadoop", "username":"Bob", "blog_comments_relation":{ "name":"comment", # name 为 comment 表示子文档 "parent":"blog2" # 指定父文档的 id,表示子文档属于哪一个父文档 } }
根据父文档 id 来查询父文档,普通的查询没法查出子文档的信息:
GET my_blogs/_doc/blog2
若是想查到子文档的信息,须要使用 parent_id 查询:
POST my_blogs/_search { "query": { "parent_id": { # parent_id 查询 "type": "comment", # comment 表示是子文档,便是表示想查询子文档信息 "id": "blog2" # 指定父文档的 id } # 这样能够查询到 blog2 的全部 comment } }
has_child 查询能够经过子文档的信息,查到父文档信息。
POST my_blogs/_search { "query": { "has_child": { # has_child 查询 "type": "comment", # 指定子文档类型,表示下面的 query 中的信息要在 comment 子文档中匹配 "query" : { "match": {"username" : "Jack"} } # 在子文档中匹配信息,最终返回全部的相关父文档信息 } } }
has_parent 查询能够经过父文档的信息,查到子文档信息。
POST my_blogs/_search { "query": { "has_parent": { # has_parent 查询 "parent_type": "blog", # 指定子文档类型,表示下面的 query 中的信息要在 blog 父文档中匹配 "query" : { "match": {"title" : "Learning Hadoop"} } # 在父文档中匹配信息,最终返回全部的相关子文档信息 } } }
普通的查询没法查到:
GET my_blogs/_doc/comment3
须要指定 routing 参数,提供父文档 id:
GET my_blogs/_doc/comment3?routing=blog2
更新子文档不会影响到父文档。
示例:
# URI 中指定子文档 id,并经过 routing 参数指定父文档 id PUT my_blogs/_doc/comment3?routing=blog2 { "comment": "Hello Hadoop??", "blog_comments_relation": { "name": "comment", "parent": "blog2" } }
ES 中的动态 Mapping 指的是:
ES 类型的自动识别规则以下:
字段类型是否可以修改,分两种状况:
mappings._doc.dynamic
为 ture
,当有新字段写入时,Mappings
会自动更新。mappings._doc.dynamic
为 false
,当有新字段写入时,Mappings
不会更新;新增字段不会创建倒排索引,可是信息会出如今 _source
中。mappings._doc.dynamic
为 strict
,当有新字段写入时,写入失败。Reindex
重建索引。dynamic
有 3 种取值,使用下面 API 能够修改 dynamic
的值:
PUT index_name/_mapping { "dynamic": false/true/strict }
经过下面语法能够获取一个索引的 Mapping:
GET index_name/_mapping
自定义 Mapping 的语法以下:
PUT index_name { "mappings" : { # 定义 } }
自定义 Mapping 的小技巧:
Mappings 有不少参数能够设置,能够参考这里。
若是咱们要在 ES 中插入以下结构的数据:
PUT blog/_doc/1 { "content":"I like Elasticsearch", "time":"2019-01-01T00:00:00", "user": { # 是一个对象类型 "userid":1, "username":"Jack", "city":"Shanghai" } }
其中的 user 字段是一个对象类型。
这种结构的数据对应的 mappings 应该像下面这样定义:
PUT /blog { "mappings": { "properties": { "content": { "type": "text" }, "time": { "type": "date" }, "user": { # user 内部又嵌套了一个 properties "properties": { "city": { "type": "text" }, "userid": { "type": "long" }, "username": { "type": "keyword" } } } } } }
若是咱们要在 ES 中插入以下结构的数据:
POST my_movies/_doc/1 { "title":"Speed", "actors":[ # actors 是一个数组类型,数组中的元素是对象类型 { "first_name":"Keanu", "last_name":"Reeves" }, { "first_name":"Dennis", "last_name":"Hopper" } ] }
其中的 actors 字段是一个数组类型,数组中的元素是对象类型。
像这种结构的数据对应的 mappings 应该像下面这样定义:
PUT my_movies { "mappings": { "properties": { "actors": { # actors 字段 "properties": { # 嵌入了一个 properties "first_name": {"type": "keyword"}, "last_name": {"type": "keyword"} } }, "title": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } }
能够经过设置字段的 index 值,来控制某些字段是否可被搜索。
index
有两种取值:true / false
,默认为 true
。
当某个字段的 index
值为 false
时,ES 就不会为该字段创建倒排索引(节省空间),该字段也不能被搜索(若是搜索的话会报错)。
设置语法以下:
PUT index_name { "mappings" : { # 固定写法 "properties" : { # 固定写法 "firstName" : { # 字段名 "type" : "text" }, "lastName" : { # 字段名 "type" : "text" }, "mobile" : { # 字段名 "type" : "text", "index": false # 设置为 false } } } }
咱们能够经过设置 index_options 的值来控制倒排索引项的内容,它有 4 种取值:
docs
:只记录文档 id
freqs
:记录文档 id
和 词频
positions
:记录文档 id
,词频
和 单词 position
offsets
:记录文档 id
,词频
,单词 position
和 字符 offset
Text
类型的数据,index_options
的值默认为 positions
;其它
类型的数据,index_options
的值默认为 docs
。
注意:对于 index_options 的默认值,不一样版本的 ES,可能不同,请查看相应版本的文档。
对于倒排索引项,其记录的内容越多,占用的空间也就越大,同时 ES 也会对字段进行更多的分析。
设置语法以下:
PUT index_name { "mappings": { # 固定写法 "properties": { # 固定写法 "text": { # 字段名 "type": "text", # 字段的数据类型 "index_options": "offsets" # index_options 值 } } } }
默认状况下 null
和 空数组[]
是不可以被搜索的,好比下面的两个文档:
PUT my_index/_doc/1 { "status_code": null } PUT my_index/_doc/2 { "status_code": [] }
要想使得这两个文档可以被搜索,须要设置 null_value 参数,以下:
PUT my_index { "mappings": { "properties": { "status_code": { "type": "keyword", # 只有 Keyword 类型的数据,才支持设置 null_value "null_value": "NULL" # 将 null_value 设置为 NULL,就能够经过 NULL 搜索了 } } } }
注意只有 Keyword
类型的数据,才支持设置 null_value
,将 null_value
设置为 NULL
,就能够经过 NULL
搜索了,以下:
GET my-index/_search?q=status_code:NULL
索引模板(Index Template)设置一个规则,自动生成索引的 Mappings 和 Settings。
索引模板有如下特性:
多个模板时的 merge 规则,当一个索引被建立时:
对于相同字段的不一样只会进行覆盖,对于不一样的字段会进行叠加依次使用。
索引模板示例:
PUT _template/template_1 # template_1 是自定义的索引模板的名称 { "index_patterns": ["te*", "bar*"], # 匹配索引的规则,该模板会做用于这些索引名上 "settings": { # settings 设置 "number_of_shards": 1 }, "mappings": { # mappings 设置 "_source": { "enabled": false }, "properties": { "host_name": { "type": "keyword" }, "created_at": { "type": "date", "format": "EEE MMM dd HH:mm:ss Z yyyy" } } } }
多个索引模板:
PUT /_template/template_1 { "index_patterns" : ["*"], "order" : 0, "settings" : { "number_of_shards" : 1 }, "mappings" : { "_source" : { "enabled" : false } } } PUT /_template/template_2 { "index_patterns" : ["te*"], "order" : 1, "settings" : { "number_of_shards" : 1 }, "mappings" : { "_source" : { "enabled" : true } } }
动态模板(Dynamic Template)用于设置某个指定索引中的字段的数据类型。
(本节完。)
推荐阅读:
欢迎关注做者公众号,获取更多技术干货。