Mapping 映射,描述了文档字段的属性以及每个字段的数据类型,比如 string, integer 或 date —以及Lucene是如何索引和存储这些字段的,这就是mapping 映射的功能。
这样说可能有些偏概念,我们可以这样来理解,对于mysql数据库来说,每一个表都有一个 schemal 定义,mapping 就是类似 数据库的 schemal,它来描述 索引文档的字段信息。
# 查看一个索引的mapping
GET movies/_mapping
{
"movies" : {
"mappings" : {
"properties" : {
"genre" : { # 字段名字
"type" : "text", # 字段类型
"analyzer": "whitespace" # 倒排索引分词器
},
"id" : {
"type" : "text",
},
"title" : {
"type" : "text",
},
"year" : {
"type" : "long"
}
}
}
}
}
上边就是一个mapping实例,大致样子就是这样,可以看到,定义了字段的名字、类型、倒排索引分析器等信息
当 Elasticsearch 遇到文档 mapping 中以前所没有定义过的字段时,它会自动确定字段的数据类型并把新的字段添加到 mapping 中,这叫做动态映射。
这样的机制,一般场景还能满足,如果有特殊场景,可能会导致 一些字段不能正确映射,至少与你预期的不一致,这个时候可以选择关闭动态映射,手动添加映射关系,默认动态映射是开启的。
dynamic = true 动态添加新的字段—缺省
false 忽略新的字段
strict 如果遇到新字段抛出异常
dynamic mapping 有如下特性
JSON 类型 | Elastic 类型 |
---|---|
字符串 | text |
布尔值 | boolean |
浮点数 | float |
整数 | long (对,不是integer) |
对象 | Object |
数组 | 由第一个非空数字决定类型 |
空值 null | 忽略 |
我们来测试一下自动类型推断
#写入文档,查看 Mapping
PUT mapping_test/_doc/1
{
"firstName":"Chan",
"lastName": "Jackie",
"loginDate":"2018-07-24T10:29:48.103Z",
"bat": null,
"age": 11
}
#查看 Mapping文件
GET mapping_test/_mapping
结果:{
"mapping_test" : {
"mappings" : {
"properties" : {
"age" : {
"type" : "long"
},
"firstName" : {
"type" : "text",
},
"lastName" : {
"type" : "text",
},
"loginDate" : {
"type" : "date"
}
}
}
}
}
我们可以看到结果, age 被计算成了 long, loginDate被计算成了 date,bat 则被忽略掉了
dynamic配置不同时,新增字段的状态是不一样的
修改 dynamic 之前,这条索引必须已经存在,否则会异常
#修改dynamic false
PUT dynamic_mapping_test/_mapping
{
"dynamic": false
}
我们设置 dynamic为 false并且新增一个字段 anotherField
#修改为dynamic false
PUT dynamic_mapping_test/_mapping
{
"dynamic": false
}
#新增 anotherField
PUT dynamic_mapping_test/_doc/10
{
"anotherField":"someValue"
}
新增结果
{
"_index" : "dynamic_mapping_test",
"_type" : "_doc",
"_id" : "10",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
结果显示新增字段成功了,我们查看一下索引 mapping 、_source 以及 search一下
#查看mapping
get dynamic_mapping_test/_mapping
{
"dynamic_mapping_test" : {
"mappings" : {
"dynamic" : "false",
"properties" : {
"newField" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
#查询这条数据
get dynamic_mapping_test/_doc/10
{
"_index" : "dynamic_mapping_test",
"_type" : "_doc",
"_id" : "10",
"_version" : 1,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"anotherField" : "someValue"
}
}
#该字段不可以被搜索,因为dynamic已经被设置为false
POST dynamic_mapping_test/_search
{
"query":{
"match":{
"anotherField":"someValue"
}
}
}
搜索结果:{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ] # hits 为空不能被搜索
}
}
可以看到,_source中有这个字段,但是mapping中没有这个字段映射,并且不能被搜索
有些时候 dynamic mapping 会出现一些纰漏,所以一般在生产中使用 ElasticSearch 时,dynamic mapping 是关闭的并且使用自定义的设定 mapping 方式来管理索引映射。
显式的设定 mapping Api 其实是很简单的,请求格式如下:
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"mobile" : {
"type" : "text",
}
}
}
}
如上就是一个自定义mapping的设置格式,一般在 mapping 下是由一个 properties 属性的,这个是格式。
一个 mapping 映射文件,并不是如上只设置一个数据类型便好的,mapping还可以设置很多属性。
# 设置索引 mobile 为 index:false
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"mobile" : {
"type" : "text",
"index": false # 设置不允许创建倒排所云
}
}
}
}
# 索引一个文档
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName": "Yiming",
"mobile": "12345678"
}
#根据 mobile 搜索
POST /users/_search
{
"query": {
"match": {
"mobile":"12345678"
}
}
}
# 报错 非法参数异常
"index": "users",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Cannot search on field [mobile] since it is not indexed."
}
ElasticSearch 在处理 null 值得搜索时,是不会去处理的,但是如果我们有需求,就是要对一些null值做搜索,那应该怎么办,这个时候就可以设置 null_value 属性。
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
}
"mobile" : {
"type" : "keyword",
"null_value": "empty"
}
}
}
}
以上就是一个 null_value的配置,这个 empty 是你在搜索时要指定的那个值,相当于是代替 null的一个标识,只要使用 empty ,便是表明你要搜索 null 值。 'empty ’ 可以替代为任何值,比如 ‘ddd’ 都行。
# 一个有 null 值的文档
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName": "Yiming",
"mobile": null
}
# 正常的索引搜索
POST /users/_search
{ "hits" : {
"query": { "total":{}
"match": { "max_score" : null,
"mobile":"null" "hits" : [ ]
} }
}
}
# 设置了 null_value 之后的值
GET users/_search "hits" : [
{ {
"query": { "_index" : "users",
"match": { "_type" : "_doc",
"mobile":"empty" "_source" : {
} "firstName" : "Ruan",
} "mobile" : null
} }
}
正常的结果是没有找到数据,我们再试一下设置了 null_value的字段,通过 empty 去查找,发现找到了mobile 为 null 的值。
copy_to 有些类似废弃的 _all 属性,它所起的功能是将多个字段的值组合为一个搜索项,然后支持搜索,比如有个文档 ‘one’:‘start’,‘two’:‘end’,然后在这两个值上设置了copy_to 属性,那么最终会合成一个搜索项 ‘start end’.
#设置 Copy to
PUT users
{
"mappings": {
"properties": {
"firstName":{
"type": "text",
"copy_to": "fullName"
},
"lastName":{
"type": "text",
"copy_to": "fullName"
}
}
}
}
以上就是一个 copy_to 的使用方法,最终将 firstName 与 lastName合并成了一个 fullName。
#索引一个文档
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName": "Yiming"
}
#搜索fulleName 为两个字段的组合值
GET users/_search?q=fullName:(Ruan Yiming)
结果:
"hits" : [
{
"_index" : "users",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.5753642,
"_source" : {
"firstName" : "Ruan",
"lastName" : "Yiming"
}
}
]
最终是可以通过 fullName 将文档搜索出来,但是在 _source 中,并没有 fullName 这个字段,也就是文档中其实并不不存在 fullName,只是建了一个fullName的倒排索引。
多字段属性,可以让同一个 text 文本有多种不同的索引方式,包括使用不同的分词器,ElasticSearch 默认会为text 文本添加一个 keyword 的搜索方式,支持精确查找以及聚合等功能。
# 多字段属性
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text",
"fields": {
"keyword": { # 配置了一个子字段 keyword 支持精确查找
"type": "keyword"
},
"english": { # 配置了一个子字段 english 支持 英文分词
"type": "text",
"analyzer": "english"
}
}
}
}
}
}
# 搜索
POST /users/_search
{
"query": {
"match": {
"firstName.english":"Ruan"
}
}
}
是在处理 text 文本这种类型是建立倒排索引所需要的词项处理工具,他会根据不同的规则去处理词项,比如中文分词器,会根据汉语的规则去分词,中文分词器比较好用的是清华大学研制的一款ElasticSearch 分词器。
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text",
"analyzer": "english"
}
}
}
}
如上就是一个多字段的配置,在 firstName 下,我们多了两个子字段,一个支持精确查找,一个支持英文分词,搜索的时候可以 firstName.keyword 或者 firstName.english 来指定使用哪个功能。
参数名 | 描述 |
---|---|
analyzer | 分词器(分析器),配置这个指定倒排索引根据什么去分词,比如:“analyzer”: “ik_max_word” |
normalizer | 用于解析前的标准化配置,比如把所有的字符转化为小写等 |
boost | 设置字段的权重,会在搜索时优先考虑这个值 例如:“boost”: 2 |
coerce | 是否去自动转化文档字段数据为mapping的映射类型,关闭之后如果添加文档时类型不相符则会报错 例如:“coerce”: false |
copy_to | 将多个字段的值组合成为一个 搜索项,只能被搜索,不会出现在 _source中,例如:“copy_to”: “full_name” |
doc_values | 是否保存字段到磁盘中,关闭后字段数据将不会存储到磁盘中并且不会被索引以及聚合。例如: “doc_values”: false |
dynamic | 对嵌套字段是否开启动态映射功能 例如: “dynamic”: true |
enabled | 与 index 功能类似,只会被存储并不会被搜索。例如:“enabled”: false |
fielddata | fielddata会占用大量的堆内存,是针对text型的字段查询的一个数据结构,它会将 text 的词项在查询时保存到内存中来做索引,资源消耗很大,不推荐使用,默认不要开启就好了 |
eager_global_ordinals | 全局映射器,开启这个会默认在分片中为词项分配一个全局的递增序列号,倒排索引时只需要处理这个序列号便可以不需要加载原本的数据值,这个参数可以优化查询性能。例如:“eager_global_ordinals”: true |
format | 用于格式化日期属性 比如:“format”: “yyyy-MM-dd” |
ignore_above | 超过 ignore_above 的字符串不会被索引存储。对于字符串数组, 如果单个子元素字符串超过了 ignore_above也不会被索引存储。例如:“ignore_above”: 20 |
ignore_malformed | ignore_malformed可以无视不规则数据。一般如果数据格式映射不对整个文档会报错,如果ignore_malformed参数设为true,异常会被忽略,出异常的字段不会被索引,其它字段正常索引。“ignore_malformed”: true |
index_options | index_options 参数控制将哪些信息添加到倒排索引,用于搜索和突出显示目的,这个一般默认就好,是建立倒排索引用的。 |
index_phrases | 与 copy_to 的功能类似 |
index_prefixes | 设置前缀索引的索引词范围 例如:“index_prefixes”: { “min_chars” : 1, “max_chars” : 10 } |
index | 是否为该字段建立倒排索引,默认为true,设为false,不能被搜索 |
fields | 多字段属性,可以让 同一 text 文本有多种不同的索引方式,动态映射默认就是用的这个 |
norms | norms 在计算相关性得分时很有用, 用于在查询时计算查询条件的相关性得分的标准化因子,但是同样需要消耗大量内存。例如:“norms”: false |
null_value | 指定null值可被搜索 |
position_increment_gap | 为了支持短语查询,需要保存可分词字符串自顿啊中分词的位置。当字符串字段索引多个值,一个“虚拟”缺口会被加载各个值之间来防止短语查询跨值匹配,缺口的大小可以使用position_increment_gap配置,默认100. 例如 :“position_increment_gap”: 10 |
properties | 一般是一个mapping的跟节点,会将 字段,对象,嵌套对象类型的 子字段成为属性可以直接被搜索。 |
search_analyzer | 为搜索单独设置一个分词器。例如:“search_analyzer”: “standard” |
similarity | Elasticsearch 允许你为每个字段配置得分算法或者相似算法。similarity 提供了一个简单的算法来选择不同于默认BM25的相似算法, 例如 TF/IDF.Similarities 大多用于 text 型字段, 但是也可以用于其他类型字段。 |
store | 是否被存储 |
term_vector | 包含分析过程产生的索引词信息,可以选择要存储那些信息,不建议配置 |