最近想整理下mapping的参数,感觉设计mapping时,那些参数还是挺重要的。
字段类型这次暂时不整理了,可以看官网文档,或我之前的博客。
Elasticsearch教程(19) 详解mapping之keyword
Elasticsearch教程(20) 详解mapping之date date_nanos
Elasticsearch教程(21) 详解mapping之boolean
Elasticsearch教程(22) 详解mapping之数值类型
Elasticsearch教程(23) 详解mapping之range
Elasticsearch教程(24) 详解mapping之object
Elasticsearch教程(25) 详解mapping之nested
Elasticsearch教程(26) mapping参数doc_values fielddata enabled
下面的语句可以在没有创建Index的情况下执行,它执行完后,会自动创建Index。
当然也可以先创建Index,同时设置Index的Settings,然后再执行下面语句创建Mapping。
PUT pigg_test_store
{
"mappings": {
"properties": {
"name": {
"type": "keyword"
},
"age": {
"type": "integer"
},
"address": {
"type": "text",
"fields": {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"birthday": {
"type": "date"
},
"socres": {
"type": "integer"
},
"chinese": {
"type": "integer"
},
"math":{
"type": "integer"
},
"english":{
"type": "integer"
}
}
}
}
如果要后加新的字段,可以按如下格式
PUT pigg_test_store/_mapping
{
"properties": {
"first_name": {
"type": "keyword",
"index": false
}
}
}
对于文档的mapping,一旦创建好,是不能直接修改字段的mapping的。
工作中一般有2种方法:
reindex的使用可以参考我以前的博客
Elasticsearch笔记(十九) reindex 重建索引,非常实用的功能
查看index的整个mapping
GET pigg_test_store/_mapping
查看个别字段的mapping
查看first_name和name这2个字段的配置
GET /pigg_test_store/_mapping/field/first_name,name
analyzer定义文本字段的分词器,默认在文档的索引和查询时都会用到。
ES本身自带了很多分词器,但多为适合英语。如果要对中文分词,推荐安装ik分词器。
例如下面,title字段用es自带的standard。content用ik分词的ik_smart。
PUT pigg_blog
{
"mappings": {
"properties": {
"title":{
"type": "text",
"analyzer": "standard"
},
"content":{
"type": "text",
"analyzer": "ik_smart"
}
}
}
}
用_analyze可以测试分词器对文本的处理。
POST /_analyze
{
"analyzer": "ik_smart",
"text": ["小跳蛙的宝宝"]
}
返回
{
"tokens" : [
{
"token" : "小跳蛙",
"start_offset" : 0,
"end_offset" : 3,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "的",
"start_offset" : 3,
"end_offset" : 4,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "宝宝",
"start_offset" : 4,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 2
}
]
}
boost 参数可以设置字段的权重。
其实从ES5.0就不建议在定义mapping的时候就设置boost,因为mapping设置后不方便修改。
建议在查询语句的时候加boost。
GET pigg_blog/_search
{
"query": {
"match": {
"content": {
"query": "宝宝",
"boost": 2
}
}
}
}
coerce:是否开启自动数据类型转换功能, 默认是true(开启)
例如:
实验: 创建一个index
field_one默认coerce为true
field_two设置coerce为false
PUT pigg_test_coerce
{
"mappings": {
"properties": {
"field_one": {
"type": "integer"
},
"field_two": {
"type": "integer",
"coerce": false
}
}
}
}
# 执行成功
PUT pigg_test_coerce/_doc/1
{
"field_one": "10"
}
# 执行报错: failed to parse field [field_two] of type [integer] in document
PUT pigg_test_coerce/_doc/2
{
"field_two": "10"
}
copy_to这个属性,可以将多个字段的值,复制到同一个字段中。但是这个复制后的字段在_source中不存在,
想当一个虚拟的字段。
PUT pigg_user
{
"mappings": {
"properties": {
"first_name": {
"type": "text",
"copy_to": "full_name"
},
"last_name": {
"type": "text",
"copy_to": "full_name"
},
"full_name": {
"type": "text"
}
}
}
}
PUT pigg_user/_doc/1
{
"first_name": "John",
"last_name": "Smith"
}
返回的_source里不存在full_name
GET pigg_user/_doc/1
{
"_source" : {
"first_name" : "John",
"last_name" : "Smith"
}
可以根据copy_to的字段进行检索
GET pigg_user/_search
{
"query": {
"match": {
"full_name": {
"query": "John Smith",
"operator": "and"
}
}
}
}
为了加快排序、聚合操作,在建立倒排索引的时候,额外增加一个列式存储映射,是一个空间换时间的做法。默认是开启的,对于确定不需要聚合或者排序的字段可以关闭。
实验: 创建index, session_id的doc_values为false
PUT pigg_test_docvalues
{
"mappings":{
"properties":{
"status_code":{
"type":"keyword"
},
"session_id":{
"type":"keyword",
"doc_values":false
}
}
}
}
# 插入下面2个文档
PUT pigg_test_docvalues/_doc/1
{
"status_code": "200",
"session_id": "aaa"
}
PUT pigg_test_docvalues/_doc/2
{
"status_code": "500",
"session_id": "bbb"
}
测试对session_id进行检索
GET pigg_test_docvalues/_search
{
"query": {
"term": {
"session_id": {
"value": "aaa"
}
}
}
}
返回有数据
"hits" : [
{
"_index" : "pigg_test_docvalues",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.6931471,
"_source" : {
"status_code" : "200",
"session_id" : "aaa"
}
}
]
测试对session_id排序
GET pigg_test_docvalues/_search
{
"sort": [
{
"session_id": {
"order": "desc"
}
}
]
}
返回报错
"caused_by" : {
"type" : "illegal_argument_exception",
"reason" : "Can't load fielddata on [session_id] because fielddata is unsupported on fields of type [keyword]. Use doc values instead."
}
测试对session_id聚合
GET pigg_test_docvalues/_search
{
"aggs": {
"terms_by_sessionId": {
"terms": {
"field": "session_id"
}
}
}
}
返回报错
"caused_by" : {
"type" : "illegal_argument_exception",
"reason" : "Can't load fielddata on [session_id] because fielddata is unsupported on fields of type [keyword]. Use doc values instead."
}
dynamic:是否允许根据文档动态添加mapping类型,默认true(允许)。
如果文档有个字段是object类型,这个object有哪些字段,一开始是不知道的,允许添加文档时,动态的生成它内部的字段。
添加一个mapping,其中social_networks字段是动态的。
PUT pigg_test_dynamic
{
"mappings": {
"dynamic": false,
"properties": {
"user": {
"properties": {
"name": {
"type": "text"
},
"social_networks": {
"dynamic": true,
"properties": {}
}
}
}
}
}
}
插入一个文档
PUT pigg_test_dynamic/_doc/1
{
"user":{
"name": "王老板",
"social_networks": {
"name": "大舅",
"age":36,
"address": "南京"
}
}
}
GET pigg_test_dynamic/_mapping
返回结果如下,可以看到social_networks下有动态生成的字段。
{
"pigg_test_dynamic" : {
"mappings" : {
"dynamic" : "false",
"properties" : {
"user" : {
"properties" : {
"name" : {
"type" : "text"
},
"social_networks" : {
"dynamic" : "true",
"properties" : {
"address" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"age" : {
"type" : "long"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
}
}
}
}
eager_global_ordinals:是否开启预加载全局序号,加快查询,默认false不开启。
此参数只支持text和keyword,keyword默认有eager_global_ordinals参数,而text需要设置fielddata属性。
因为Elasticsearch 是默认延迟加载fielddata到内存里的。当elasticsearch第一次遇到一个查询需要一个指定field的fielddata的时候,就会把索引的每个段中整个field加载到内存。
对于小段,这是个可以忽略不计的时间,但是如果你有一些非常大的fielddata到内存里,这个过程需要数十秒,习惯于秒内响应时间的用户会被网突如其来的迟钝所打击。
对于上面慢的问题新版有2个优化方法:
这里说的eager_global_ordinals参数就是第二种方法。
PUT pigg_test_eager
PUT pigg_test_eager/_mapping
{
"properties": {
"tags": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
enabled
参数只用于object
类型,默认值是true(开启)实验如下:
PUT pigg_test_enabled
{
"mappings": {
"properties": {
"name": {"enabled": false}
}
}
}
插入多种格式的值
PUT pigg_test_enabled/_doc/1
{
"name": "winter"
}
PUT pigg_test_enabled/_doc/2
{
"name": {
"first_name": "wang",
"last_name": "dong"
}
}
GET pigg_test_enabled/_search
返回发现: 不同的值都可以存入
"hits" : [
{
"_index" : "pigg_test_enabled",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "winter"
}
},
{
"_index" : "pigg_test_enabled",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"name" : {
"first_name" : "wang",
"last_name" : "dong"
}
}
}
]
# 查看mapping
GET pigg_test_enabled/_mapping
返回如下: name类型是object, 并不会解析name在其下面添加first_name和last_name子字段
{
"pigg_test_enabled" : {
"mappings" : {
"properties" : {
"name" : {
"type" : "object",
"enabled" : false
}
}
}
}
}
在enable=false的字段上检索文档, 是查询不到数据的
GET pigg_test_enabled/_search
{
"query": {
"term": {
"name": {
"value": "winter"
}
}
}
}
返回没有数据
format是日期字段,设置日期格式的,多个格式用||隔开。
date的格式可以被指定的,如果没有特殊指定,默认格式是"strict_date_optional_time||epoch_millis"。
epoch_millis就是从开始纪元(1970-01-01 00:00:00 UTC)开始的毫秒数-长整型。
#重建索引
PUT /test_date_index
{
"mappings":{
"properties":{
"birthday":{
"type":"date",
"format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
#2020/03/01 17:44:09的毫秒级时间戳
PUT /test_date_index/_doc/1
{
"birthday": 1583055849000
}
PUT /test_date_index/_doc/2
{
"birthday": "2020-03-01 16:29:41"
}
PUT /test_date_index/_doc/3
{
"birthday": "2020-02-29"
}
ignore_above是在keywor类型下设置一个长度,当字符的长度超过ignore_above的值,那么它不会被索引。
首先随意往ES插一条数据:
put my_index/_doc/1
{
"name": "李星云"
}
查看ES自动生成的mapping,name是text类型,其下还有子类型keyword,且"ignore_above" : 256
GET /my_index/_mapping
name定义如下:
"properties" : {
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
对于keyword类型, 可设置ignore_above限定字符长度。超过 ignore_above 的字符会被存储,但不会被倒排索引。比如ignore_above=4,”abc“,”abcd“,”abcde“都能存进ES,但是不能根据”abcde“检索到数据。
【1】创建一个keyword类型的字段,ignore_above=4
PUT test_index
{
"mappings": {
"_doc": {
"properties": {
"message": {
"type": "keyword",
"ignore_above": 4
}
}
}
}
}
【2】向索引插入3条数据:
PUT /test_index/_doc/1
{
"message": "abc"
}
PUT /test_index/_doc/2
{
"message": "abcd"
}
PUT /test_index/_doc/3
{
"message": "abcde"
}
此时ES倒排索引是:
词项 | 文档ID |
---|---|
abc | 1 |
abcd | 2 |
【3】根据message进行terms聚合: |
GET /test_index/_search
{
"size": 0,
"aggs": {
"term_message": {
"terms": {
"field": "message",
"size": 10
}
}
}
}
返回结果:
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 3,
"max_score" : 1.0,
"hits" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"message" : "abcd"
}
},
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"message" : "abc"
}
},
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"message" : "abcde"
}
}
]
},
"aggregations" : {
"term_message" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [#注意这分组里没有”abcde“
{
"key" : "abc",
"doc_count" : 1
},
{
"key" : "abcd",
"doc_count" : 1
}
]
}
}
}
【4】根据”abcde“进行term精确查询,结果为空
GET /test_index/_search
{
"query": {
"term": {
"message": "abcde"
}
}
}
然后结果:
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
通过上面结果能知道”abcde“已经存入ES,也可以搜索出来,但是不存在词项”abcde“,不能根据”abcde“作为词项进行检索。
对于已存在的keyword字段,其ignore_above子属性可以修改,但只对新数据有效。
ignore_malformed 可以忽略不规则的数据,该参数默认为 false。
PUT pigg_test_ignore_malformed
{
"mappings": {
"properties": {
"age1": {
"type": "integer",
"ignore_malformed": true
},
"age2":{
"type": "integer"
}
}
}
}
这个保存成功,忽略了验证"什么"不是数字
PUT pigg_test_ignore_malformed/_doc/1
{
"age1": "什么"
}
这个保存失败
PUT pigg_test_ignore_malformed/_doc/1
{
"age2": "什么"
}
PUT pigg_test_index
{
"mappings": {
"properties": {
"name": {
"type": "keyword"
},
"words": {
"type": "keyword",
"index": false
}
}
}
}
#插入如下数据
PUT pigg_test_index/_doc/1
{
"name": "亚瑟王",
"words": "死亡骑士, 不是死掉的骑士"
}
PUT pigg_test_index/_doc/2
{
"name": "扁鹊",
"words": "命长的是赢家"
}
查询文档, _source是有words的
"hits" : [
{
"_index" : "pigg_test_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "亚瑟王",
"words" : "死亡骑士, 不是死掉的骑士"
}
},
{
"_index" : "pigg_test_index",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"name" : "扁鹊",
"words" : "命长的是赢家"
}
}
]
在index=false的字段上检索文档
GET pigg_test_index/_search
{
"query": {
"term": {
"words": {
"value": "命长的是赢家"
}
}
}
}
该查询会报错:
"caused_by" : {
"type" : "illegal_argument_exception",
"reason" : "Cannot search on field [words] since it is not indexed."
}
index_options 控制索引时哪些信息被存储到倒排索引中(用在 text 字段中),有四种取值
index_options | 说明 |
---|---|
docs | 只存文档的编号 |
freqs | 在docs的基础上,存词项频率 |
positions | 默认这个,在freqs的基础上,存词项位置偏移信息 |
offsets | 在positions 的基础上,存词项开始和结束位置 |
PUT pigg_test_options
{
"mappings": {
"properties": {
"text": {
"type": "text",
"index_options": "offsets"
}
}
}
}
fields 参数可以让同一字段有多种不同的索引方式。
最常见的就text类型下面有个keywrod,这样可以用text下的keyword进行term 查询。
PUT pigg_test_fields
{
"mappings": {
"properties": {
"city": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
normalizer 参数用于解析前(索引或者查询)的标准化配置。
比如,在 es 中,对于一些我们不想切分的字符串,我们通常会将其设置为 keyword,搜索时候也是使用整个词进行搜索。如果在索引前没有做好数据清洗,导致大小写不一致,此时,我们就可以使用 normalizer 在索引之前以及查询之前进行文档的标准化。
PUT index
{
"settings": {
"analysis": {
"normalizer": {
"my_normalizer": {
"type": "custom",
"char_filter": [],
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"foo": {
"type": "keyword",
"normalizer": "my_normalizer"
}
}
}
}
PUT index/_doc/1
{
"foo": "BAR"
}
PUT index/_doc/2
{
"foo": "bar"
}
下面语句能返回2个文档
GET index/_search
{
"query": {
"term": {
"foo": "BAR"
}
}
}
PUT my_index/_mapping/_doc
{
"properties": {
"title": {
"type": "text",
"norms": false
}
}
}
在 es 中,值为 null 的字段不索引也不可以被搜索,null_value 可以让值为 null 的字段显式的可索引、可搜索。
但是在工作中,null_value基本不用,因为在公司的环境中,人员水平参差不齐,各个项目组要通知知晓和维护这个null_value太难了。所以要用null_value得非常慎重,最好不用,不要去挖坑给后人。
对于null_value 易造成的混乱,看下面代码体会吧。
PUT users
{
"mappings": {
"properties": {
"name":{
"type": "keyword",
"null_value": "my_null"
}
}
}
}
PUT users/_doc/1
{
"name":null,
"age":99
}
GET users/_search
{
"query": {
"term": {
"name": "my_null"
}
}
}
之前第一个analyzer,它定义文本字段的分词器,默认在文档的索引和查询时都会用到。
而search_analyzer是在查询时,先对要查询的text类型的输入做分词,再去倒排索引搜索。
如果想要让 索引 和 查询 时使用不同的分词器,ElasticSearch也是能支持的,只需要在字段上加上search_analyzer参数
PUT pigg_blog
{
"mappings": {
"properties": {
"content":{
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "standard"
}
}
}
}
store默认false,如果设置为ture,那就会在_source的同级外面单独存下它原始值,就是不拆成词项的值。一般是_source中某个字段的值很大,但是要查询个别小的字段而查询_source是效率低的,如果把小的字段在_source外面也存一份,如果单独取它时,可以直接从store里取,而不用从_source中取。
PUT pigg_blog
{
"mappings": {
"properties": {
"title":{
"type": "text",
"analyzer": "standard",
"store": true
},
"author":{
"type": "keyword",
"store": true
},
"content":{
"type": "text",
"analyzer": "ik_smart",
"fields": {
"length": {
"type": "token_count",
"analyzer": "ik_smart"
}
}
}
}
}
}
PUT pigg_blog/_doc/1
{
"title": "宝贝ABC",
"author": "宝贝巴士",
"content": "宝贝,宝贝ABC"
}
GET pigg_blog/_doc/1?stored_fields=title,content
{
"_index" : "pigg_blog",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"fields" : {
"title" : [
"宝贝ABC"
]
}
}
term_vectors 是通过分词器产生的信息,包括:
一组 terms
每个 term 的位置
term 的首字符/尾字符与原始字符串原点的偏移量
设置term_vector
PUT pigg_blog
{
"mappings": {
"properties": {
"text": {
"type": "text",
"term_vector": "with_positions_offsets"
}
}
}
}
要查看term_vectors的信息,可以如下:
GET pigg_blog/_termvectors/1?fields=content
返回
{
"_index" : "pigg_blog",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"found" : true,
"took" : 363,
"term_vectors" : {
"content" : {
"field_statistics" : {
"sum_doc_freq" : 2,
"doc_count" : 1,
"sum_ttf" : 3
},
"terms" : {
"abc" : {
"term_freq" : 1,
"tokens" : [
{
"position" : 2,
"start_offset" : 5,
"end_offset" : 8
}
]
},
"宝贝" : {
"term_freq" : 2,
"tokens" : [
{
"position" : 0,
"start_offset" : 0,
"end_offset" : 2
},
{
"position" : 1,
"start_offset" : 3,
"end_offset" : 5
}
]
}
}
}
}
}