文章目录
- 1. mapping param
- 1. 概览
- 2. mapping param详述
- 1.1 analyzer
- 1.2.search_quote_analyzer
- 2. normalizer
- 3. boost
- 4. coerce
- 5. copy_to
- 6. doc_values
- 7. dynamic
- 8. enabled
- 9. fielddata
- 10. eager_global_ordinals
- 11. format
- 12. ignore_above
- 13. ignore_malformed
- 14. index_options
- 15. index_phrases
- 16. index_prefixes
- 17. index
- 18. fields
- 19. norms
- 20. null_value
- 21. position_increment_gap
- 22. properties
- 23. search_analyzer
- 24. similarity
- 25. store
- 26. term_vector
1. mapping param
mapping param 是在定义es中index存储的doc的某个字段的一些特征的,他决定了该字段的存储方式以及在存储之前的analysis部分如何进行。
不同的数据类型的field可以设置的mapping param是下面概览的中的一个子集。在前文中不同的type的field能够设置的mapping param也是有记录的。
1. 概览
- analyzer
- normalizer
- boost
- coerce
- copy_to
- doc_values
- dynamic
- enabled
- fielddata
- eager_global_ordinals
- format
- ignore_above
- ignore_malformed
- index_options
- index_phrases
- index_prefixes
- index
- fields
- norms
- null_value
- position_increment_gap
- properties
- search_analyzer
- similarity
- store
- term_vector
2. mapping param详述
1.1 analyzer
这个主要设置的是index的时候首先选择的analyzer,也是search中中等优先使用的analyzer.
可以参考analyzer相关文章来回顾。
样例
PUT /my_index
{
"mappings": {
"properties": {
"text": {
"type": "text",
"fields": {
"english": {
"type": "text",
"analyzer": "english"
}
}
}
}
}
}
GET my_index/_analyze
{
"field": "text",
"text": "The quick Brown Foxes."
}
GET my_index/_analyze
{
"field": "text.english",
"text": "The quick Brown Foxes."
}
1.2.search_quote_analyzer
不太明白官方在这一个页面挤进来两个analyzer,不知道怎么想的,但是为了更好的和官方对应,我也挤一挤好了
这个analyzer是专门为精准的phrase查询服务的,也就是针对那些可能有停用词的phrase查询。
如果想要实现精确的phrase查询,需要在mapping中有这样的配置
- analyzer: indexing的时候使用,会包含所有的词,包括stop words
- search_analyzer: 非phrase查询使用,会remove stop words
- search_quote_analyzer: 针对phrase查询使用,不会去掉stop words
PUT my_index
{
"settings":{
"analysis":{
"analyzer":{
"my_analyzer":{
"type":"custom",
"tokenizer":"standard",
"filter":[
"lowercase"
]
},
"my_stop_analyzer":{
"type":"custom",
"tokenizer":"standard",
"filter":[
"lowercase",
"english_stop"
]
}
},
"filter":{
"english_stop":{
"type":"stop",
"stopwords":"_english_"
}
}
}
},
"mappings":{
"properties":{
"title": {
"type":"text",
"analyzer":"my_analyzer",
"search_analyzer":"my_stop_analyzer",
"search_quote_analyzer":"my_analyzer"
}
}
}
}
PUT my_index/_doc/1
{
"title":"The Quick Brown Fox"
}
PUT my_index/_doc/2
{
"title":"A Quick Brown Fox"
}
GET my_index/_search
{
"query":{
"query_string":{
"query":"\"the quick brown fox\""
}
}
}
2. normalizer
注意,这里不要和后面的norms搞混了,这个也是analysis的一部分。
只不过他是char_filter 和 token_filter的组合。
还有就是他可以在keyword类型的字段中使用,会改变dov_values中存储的值。
PUT index
{
"settings": {
"analysis": {
"normalizer": {
"my_normalizer": {
"type": "custom",
"char_filter": [],
"filter": ["lowercase", "asciifolding"]
}
}
}
},
"mappings": {
"properties": {
"foo": {
"type": "keyword",
"normalizer": "my_normalizer"
}
}
}
}
PUT index/_doc/1
{
"foo": "BÀR"
}
PUT index/_doc/2
{
"foo": "bar"
}
PUT index/_doc/3
{
"foo": "baz"
}
使用agg查询的时候是这样
GET index/_search
{
"size": 0,
"aggs": {
"foo_terms": {
"terms": {
"field": "foo"
}
}
}
}
返回
...
"aggregations": {
"foo_terms": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "bar",
"doc_count": 2
},
{
"key": "baz",
"doc_count": 1
}
]
}
}
这里是因为doc_values中的存储和_source中对应的字段的值是不一样的导致的。
3. boost
首先说一下,在index的时候指定boost的方式在5.0的时候就已经过期了
在mapping中定义的boost只是在search的时候使用。
也就是
PUT my_index
{
"mappings": {
"properties": {
"title": {
"type": "text",
"boost": 2
},
"content": {
"type": "text"
}
}
}
}
POST _search
{
"query": {
"match" : {
"title": {
"query": "quick brown fox"
}
}
}
}
的查询效果和
POST _search
{
"query": {
"match" : {
"title": {
"query": "quick brown fox",
"boost": 2
}
}
}
}
是一致的,主要是用来干预打分。
4. coerce
这个属性是为了处理一些相对来说没有那么脏的数据,
非常脏的数据,比如type为integer,index的时候给了一个字符串 “abc” ,那么就只能是抛异常,或者是通过设置 ignore_malformed 来忽略对应的字段的dov_values等设置,只是在_source字段中进行存储。
没有那么脏的数据,比如
- 定义的type为integer, indexing为 “5” 这种数字字符串,这种可以辅助进行处理
- 定义type 为integer, indexing为 5.1 这种,会进行truncate 操作
注意这个设置只是针对number类型和geometry 类型的数据进行处理
定义type为keyword, indexing为 数字可以成功,这个功劳不是coerce的,后面会看到这个是dynamic mapping的功劳
实际上在dynamic mapping中也没有看到,应该是其他类型的往string转都是直接根据mapping中的显式定义转了
样例
PUT my_index
{
"aliases" : { },
"mappings" : {
"properties" : {
"requests" : {
"type" : "text"
},
"number":{
"type": "integer",
"coerce":true
}
}
}
}
PUT my_index/_doc/1
{
"requests":"hahaha",
"number":"5"
}
PUT my_index/_doc/2
{
"requests":"hahaha",
"number":6.1
}
PUT my_index/_doc/3
{
"requests":"hahaha",
"number":7
}
GET my_index/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"number": {
"order": "asc"
}
}
]
}
返回
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "1",
"_score" : null,
"_source" : { "requests" : "hahaha", "number" : "5" },
"sort" : [ 5 ]
},
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "2",
"_score" : null,
"_source" : { "requests" : "hahaha", "number" : 6.1 },
"sort" : [ 6 ]
},
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "3",
"_score" : null,
"_source" : { "requests" : "hahaha", "number" : 7 },
"sort" : [ 7 ]
}
]
}
}
从sort字段中可以看出来存储的对应的doc_values都已经发生了变化了,因为被corces 处理了。
假如mapping中将corece定义为false,则put操作中的6.1,“5” 都会直接抛异常。
同时这个设置可以直接在mapping级别进行设置
PUT my_index
{
"settings": {
"index.mapping.coerce": false
},
"mappings": {
"properties": {
"number_one": {
"type": "integer",
"coerce": true
},
"number_two": {
"type": "integer"
}
}
}
}
5. copy_to
这个是可以设置将多个filed拷贝到一个或者多个其他的field当中。
PUT my_index
{
"mappings": {
"properties": {
"first_name": {
"type": "text",
"copy_to": "last_name"
},
"last_name": {
"type": "text",
"copy_to": "full_name"
},
"full_name": {
"type": "text"
}
}
}
}
PUT my_index/_doc/1
{
"first_name": "John good boy ",
"last_name": "Smith bad girl "
}
GET my_index/_search
{
"query": {
"match": {
"full_name": {
"query": "smith"
}
}
}
}
上面的文档会命中put进去的文档,但是query换成john,good ,boy 中的任意组合都是不能搜索出来的,也就是拷贝会不进行循环,first_name 拷贝到last_name的值不会再拷贝到full_name,只有直接indexing进last_name的值会进入索引。
假如使用下面的查询
GET my_index/_search
{
"query": {
"match": {
"last_name": "good"
}
}
}
也是可以命中该文档的。
总的来说,copy_to 有以下特性:
- 拷贝的是field,不是terms,所以具体产生的terms还是要看目标field的analyzer
- _source字段不会发生改变,所以正常返回的文档中不会不有copy过的内容
- 一个字段可以被拷贝到多个字段,配置的方式是"copy_to":
[ "field_1", "field_2" ]
- 进行递归的拷贝是无效的,fa–>fb fb—>fc fa的值并不会被拷贝到fc当中
6. doc_values
大部分field都进行了indexed操作生成了倒排词典来查询,但是进行agg,sort,script_field操作的时候,我们需要快速根据文档的编号获取某个field的值。
doc_values存储在磁盘上,在indexing的时候生成,采用列式存储,获取对应的field更加快速。除了 analyzed 的字段,doc_values支持其他的大部分常见字段。
如果这个字段不用来agg,sort,script_field操作,你也可以禁用doc_values来节约磁盘。
PUT my_index
{
"mappings": {
"properties": {
"status_code": {
"type": "keyword"
},
"session_id": {
"type": "keyword",
"doc_values": false
}
}
}
}
7. dynamic
默认的情况下doc mapping中的field是可以根据文档内容动态增加的,对于object类型的field其子field也是可以动态增加的。
其背后就是这个dynamic mapping param在起作用,该字段可以有三个值
- true : 默认值,新检测到的field可以添加到mapping当中
- false: 新检测到的field会被忽略,不能通过新加入的这个filed进行search操作,但是这个字段还是会出现在_source字段中哦,因为_source就是啥都不管,直接存
- strict: 新检测到的field会抛异常,且当前indexing的doc会失败
在2,3情况下想要正价filed只有修改mapping
这个字段可以直接在整个mapping级别设置或者为某一个字段设置,采用最近父亲生效原则,比如下面的social_networks可以动态增加field
PUT my_index
{
"mappings": {
"dynamic": false,
"properties": {
"user": {
"properties": {
"name": {
"type": "text"
},
"social_networks": {
"dynamic": true,
"properties": {}
}
}
}
}
}
}
8. enabled
如果对某个field设置了enabled:false
该字段不会被index,所以也不能被搜索,也可以对整个mapping设置,这样的话index的所有字段都是没有indexed的。
样例
PUT my_index
{
"mappings": {
"properties": {
"user_id": {
"type": "keyword"
},
"last_updated": {
"type": "date"
},
"session_data": {
"type": "object",
"enabled": false
}
}
}
}
PUT my_index/_doc/session_1
{
"user_id": "kimchy",
"session_data": {
"arbitrary_object": {
"some_array": [ "foo", "bar", { "baz": 2 } ]
}
},
"last_updated": "2015-12-06T18:20:22"
}
PUT my_index/_doc/session_2
{
"user_id": "jpountz",
"session_data": "none",
"last_updated": "2015-12-06T18:22:13"
}
GET my_index
返回
{
"my_index" : {
"aliases" : { },
"mappings" : {
"properties" : {
"last_updated" : {
"type" : "date"
},
"session_data" : {
"type" : "object",
"enabled" : false
},
"user_id" : {
"type" : "keyword"
}
}
}
}
首先,这个地方的put session_1 的session_data的内容会被忽略,只会存在_source当中,不能被搜索,对应的mapping文件也不会有dynamic field加入。
对整个mapping设置的样例
PUT my_index
{
"mappings": {
"enabled": false
}
}
PUT my_index/_doc/session_1
{
"user_id": "kimchy",
"session_data": {
"arbitrary_object": {
"some_array": [ "foo", "bar", { "baz": 2 } ]
}
},
"last_updated": "2015-12-06T18:20:22"
}
GET my_index?flat_settings=true
返回
{
"my_index" : {
"aliases" : { },
"mappings" : {
"enabled" : false
},
"settings" : {
"index.creation_date" : "1605509510151",
"index.number_of_replicas" : "1",
"index.number_of_shards" : "1",
"index.provided_name" : "my_index",
"index.uuid" : "UFZynHQuQl29jg4bFf8mtQ",
"index.version.created" : "7020099"
}
}
}
9. fielddata
1. fielddata 功能
text field没有doc_values结构,所以针对text field 的agg,sort ,script操作维护了一个 query-time in-memory 的数据结构,这个数据结构就叫 fielddata ,
他会在第一次使用一个text field进行agg,sort,script操作的时候构建的。这个操作耗时耗内存,他是通过加载所有的倒排词典,构建doc->term的正向信息,在jvm中保存这些信息。
对于text field这个字段默认也是 disabled 的。
如果非要使用text field进行聚合的话应该使用fields特性新定一个filed 存储为keyword 使用doc_values特性进行agg, sort,script 操作。
PUT my_index/_mapping
{
"properties": {
"my_field": {
"type": "text",
"fielddata": true
}
}
}
PUT my_index
{
"mappings": {
"properties": {
"my_field": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
2.fielddata_frequency_filter
为了优化fielddata对内存的使用,增加了一个2.fielddata_frequency_filter 来选择性的只加载一些
PUT my_index
{
"mappings": {
"properties": {
"tag": {
"type": "text",
"fielddata": true,
"fielddata_frequency_filter": {
"min": 0.001,
"max": 0.1,
"min_segment_size": 500
}
}
}
}
}
含有该term的doc的数量要大于总量的千分之一但是要小于十分之一,segment的doc的数量大于500才会被加载。
10. eager_global_ordinals
1. 什么是全局序数
全局序数主要是为了dov_values类型的数据的agg查询做优化的。
在每个segment当中,对于dov_vlues类型的数据存储做了优化,并不是直接存储doc_id—> field的映射,而是维护了一个segment ordinals mapping,这个mapping的key是一个数字,这个数字是怎么来的呢,比如把所有的filed值做字典排序后的序数,或者直接就是赋予一个递增的integer(去重后),mapping的val是一个keyword(对应的field的value)。这样的话在doc_values中就只需要存储doc_id—>(maping中的key)就行了。这样可以实现数据的压缩,对于重复的字段,在doc_values中就只会存储一份。
整个结构类似这样
doc_values:
doc_id—>segment-ordianl
segment-ordinal—>field-value
注意,上面讲的只是segment ordianals,并不是glabal ordinals。
global ordinals 就是把当前shard中的所有的segment ordinals 构建为一个全局的 ordinals。
主要的作用在于使用agg等操作的时候不需要再访问field的真实值即可进行访问。对于sort操作不起作用,因为global-ordinal的大小不代表field-value的大小,只能取得field value之后再进行比较。
所以sort操作只需要segment-ordinals就足够了,不需要glabal-ordinals
gobal-ordinals的使用场景
- keyword,ip,flattened类型字段的agg操作
- text字段开启了fielddata之后的agg操作
- join 字段的has_child查询或者parent agg 查询
global ordinals的存储:
global-ordinals的存储是在内存中构建的,如果内存紧张的话也会被 fielddata circuit breaker 给阻断,而且global-ordinals的内存占用可以通过node_stats api获取,在fielddata项下面。
2. global ordinals的load时机
默认的情况下是在第一次需要的query 查询发生的时候。如果想保持比较高的index速度,这个方式是很正确的,但是如果对search performance 更在意,应该使用建议开启eager_global_ordinals 选项,
当eager_global_ordinals 开启之后,每当shard 产生了refresh(就是产生了doc的增删改之后产生新的segment以便于这些内容能够被search),都会重建global ordinals,这会增加indexing的代价但是优化查询单速度。同时开启这个设置之后再增加副本或者是迁移副本的时候也会eagerly创建global ordianals 。
在开启了相关设置之后还可以通过update mapping进行关闭
PUT my_index/_mapping
{
"properties": {
"tags": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
PUT my_index/_mapping
{
"properties": {
"tags": {
"type": "keyword",
"eager_global_ordinals": false
}
}
}
对于frozen的index ,global ordianls会在每次查询的时候创建,在查询结束后再丢弃,所以eager_global_ordinals 不应该在frozen的index中开启,同时对于frozen尽量先进行段合并,合并为一个segment最好,这样就不用创建global ordinals了,因为segment ordinal 和 global ordinal是等价的了。
3. global ordinals使用建议
一般情况下,global ordinals 不会暂用太多的内存,但是对于有比较大shard的索引或者索引当中有很多term都是uniqued ,load global ordinals 会是一个昂贵的操作。
还有一些可选的配置来改善global ordinals 的使用
- 对于terms,smapler,significant_terms 的agg操作支持一个叫 execution_hint 的参数,默认的话是使用 global ordinals 但是可以设置直接使用field-value 进行bucketed 操作
- 对于不再更新的索引,可以force-merged 成一个segment,这样的话segment ordinal 和 global ordinal是等价的了。
11. format
这个主要使用在es对日期类型的数据识别上。因为json对日期没有特殊的支持,只是一个string,所以需要进行特殊识别才行。
这个format分为两种,一种是es内建的format,支持用户通过名称进行快速的选配,还有一种是自定义的format,只要符合java中java.time.format.DateTimeFormatter 中定义的规范的string格式都可以
1. 内建format的样例
PUT my_index
{
"mappings": {
"properties": {
"date": {
"type": "date",
"format": "yyyy-MM-dd"
}
}
}
}
2. built-in format
epoch_millis: Java Long.MIN_VALUE and Long.MAX_VALUE.
epoch_second: Java Long.MIN_VALUE and Long.MAX_VALUE.除以1000,标识的范围和上一个保持一致
date_optional_time or strict_date_optional_time: iso标准时间定义
basic_date: yyyyMMdd
basic_date_time: yyyyMMdd’T’HHmmss.SSSZ
basic_date_time_no_millis: yyyyMMdd’T’HHmmssZ
basic_ordinal_date: yyyyDDD
basic_ordinal_date_time: yyyyDDD’T’HHmmss.SSSZ
basic_ordinal_date_time_no_millis: yyyyDDD’T’HHmmssZ
basic_time: HHmmss.SSSZ
basic_time_no_millis: HHmmssZ
basic_t_time: 'T’HHmmss.SSSZ
basic_t_time_no_millis: 'T’HHmmssZ
basic_week_date or strict_basic_week_date: xxxx’W’wwe
basic_week_date_time or strict_basic_week_date_time: xxxx’W’wwe’T’HHmmss.SSSZ
basic_week_date_time_no_millis or strict_basic_week_date_time_no_millis: xxxx’W’wwe’T’HHmmssZ
date or strict_date: yyyy-MM-dd
date_hour or strict_date_hour: yyyy-MM-dd’T’HH
date_hour_minute or strict_date_hour_minute: yyyy-MM-dd’T’HH:mm
date_hour_minute_second or strict_date_hour_minute_second: yyyy-MM-dd’T’HH:mm:ss
date_hour_minute_second_fraction or strict_date_hour_minute_second_fraction: yyyy-MM-dd’T’HH:mm:ss.SSS
date_hour_minute_second_millis or strict_date_hour_minute_second_millis: yyyy-MM-dd’T’HH:mm:ss.SSS
date_time or strict_date_time: yyyy-MM-dd’T’HH:mm:ss.SSSZZ
date_time_no_millis or strict_date_time_no_millis: yyyy-MM-dd’T’HH:mm:ssZZ
hour or strict_hour: HH
hour_minute or strict_hour_minute: HH:mm.
hour_minute_second or strict_hour_minute_second: HH:mm:ss
hour_minute_second_fraction or strict_hour_minute_second_fraction: HH:mm:ss.SSS
hour_minute_second_millis or strict_hour_minute_second_millis: HH:mm:ss.SSS
ordinal_date or strict_ordinal_date: yyyy-DDD
ordinal_date_time or strict_ordinal_date_time: yyyy-DDD’T’HH:mm:ss.SSSZZ
ordinal_date_time_no_millis or strict_ordinal_date_time_no_millis: yyyy-DDD’T’HH:mm:ssZZ
time or strict_time: HH:mm:ss.SSSZZ
time_no_millis or strict_time_no_millis: HH:mm:ssZZ
t_time or strict_t_time: 'T’HH:mm:ss.SSSZZ
t_time_no_millis or strict_t_time_no_millis: 'T’HH:mm:ssZZ
week_date or strict_week_date: xxxx-'W’ww-e
week_date_time or strict_week_date_time: xxxx-'W’ww-e’T’HH:mm:ss.SSSZZ
week_date_time_no_millis or strict_week_date_time_no_millis: xxxx-'W’ww-e’T’HH:mm:ssZZ
weekyear or strict_weekyear: xxxx
weekyear_week or strict_weekyear_week: xxxx-'W’ww
weekyear_week_day or strict_weekyear_week_day: xxxx-'W’ww-e
year or strict_year: yyyy
year_month or strict_year_month: yyyy-MM
year_month_day or strict_year_month_day: yyyy-MM-dd
es真是太宝妈了,但是估计大部分人都没有耐心看完这些吧😭
12. ignore_above
对string类型的数据起作用,是一个int值,长度超过这个值的string不会被 indexed或者stored(field store),对于string arr ,限制会应用到其中的每一个元素。但是对于那些超过长度的字段仍然会在_source字段中进行存储。
这个测试是对keyword类型的起作用,text类型的不允许设置这个参数。
PUT my_index
{
"mappings": {
"properties": {
"message": {
"type": "keyword",
"ignore_above": 20
}
}
}
}
PUT my_index/_doc/1
{
"message": "Syntax error"
}
这个也会put成功,但是doc_values不会被存储
PUT my_index/_doc/2
{
"message": "Syntax error with some long stacktrace"
}
GET my_index/_search
{
"aggs": {
"messages": {
"terms": {
"field": "message"
}
}
}
}
在查询中会返回所有的文档,但是agg的结果没有第二个文档
13. ignore_malformed
这个设置对很多字段都生效,会忽略格式上不合法的字段,但是整个doc依然能够成功indexed,不合法的字段在_source字段依然会有存储,但是没有doc_value等信息的存储了。
如果这个指端设置为false(默认值),出现不合法的写入之后会抛异常,整个文档也没有办法正常写入。
样例
PUT my_index
{
"mappings": {
"properties": {
"number_one": {
"type": "integer",
"ignore_malformed": true
},
"number_two": {
"type": "integer"
}
}
}
}
会成功
PUT my_index/_doc/1
{
"text": "Some text value",
"number_one": "foo"
}
抛异常
PUT my_index/_doc/2
{
"text": "Some text value",
"number_two": "foo"
}
还可以在index级别设置这个值
PUT my_index
{
"settings": {
"index.mapping.ignore_malformed": true
},
"mappings": {
"properties": {
"number_one": {
"type": "byte"
},
"number_two": {
"type": "integer",
"ignore_malformed": false
}
}
}
}
同样的,对于malformed的字段会有所记录,在doc中增加一个 _ignored字段进行标识。
对于object,nested,range 类型的字段没有办法设置这个属性
14. index_options
这个属性主要是针对string.text的字段进行设置的。
主要设置了哪些信息会被加入到倒排索引当中。
可以是下面的几个值
- docs: 只有doc number 被加入,只能回答当前doc的这个field是否包含这个term
- freqs: doc-number + term-frequencies ,就是这个term在当前field中出现的频次,可以用来计算score
- positions: doc-numer + term-frequencies + term-position position可以用来做phrase或者距离查询 ()
- offsets: doc-numer + term-frequencies + term-position+ character-offsets offsets 包含了term中在原始的string中的字符级别的位置,可以用来加速高亮显示
对于string.text positions 是默认的设置
对于其他可以设置的字段 docs是默认的设置
对于numeric 类型的数据不支持这个属性
PUT my_index
{
"mappings": {
"properties": {
"text": {
"type": "text",
"index_options": "offsets"
}
}
}
}
PUT my_index/_doc/1
{
"text": "Quick brown fox"
}
GET my_index/_search
{
"query": {
"match": {
"text": "brown fox"
}
},
"highlight": {
"fields": {
"text": {}
}
}
}
15. index_phrases
这个是为了优化phrase查询而做的设置,设置了true,会假如一个2gram的shingles filter的功能,会把两个term结合到一起产生新的field.
这会让phrase queries 运行的更加高效,但是index的存储占用也会变大,而且在没有stop-word的工作的更好,因为有stop-word的查询会退化为一个普通的phrase query
默认值为false
16. index_prefixes
这个允许index term的前缀来加快前缀查询匹配的速度。
有两个参数
- min_chars : 前缀最少的字符数
- max_chars : 最大的前缀长度
PUT my_index
{
"mappings": {
"properties": {
"full_name": {
"type": "text",
"index_prefixes": {
"min_chars" : 1,
"max_chars" : 10
}
}
}
}
}
这个不知道怎么进行验证,后面看看吧
17. index
这个就是设置字段是否indexed, 对应值为false的字段不能被搜索,默认为true
18. fields
这个属性主要是为了支撑一个field多种存储类型的需求
PUT my_index
{
"mappings": {
"properties": {
"city": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
两个字段可以有不同个type,不同的analyzer等
19. norms
这个是存储各种各样的normalization factors 归一化系数,这些系数是为了能够在query的时候计算出相似度score使用
尽管norms对计算相似度打分很有用,但是也会占据不少存储空间,正常情况下,index的每个文档中的每个field都会占用一个byte,即使某个doc中没有这个field也会占用(这是lucene的存储原理决定的),所以如果你不需要对一个field进行score计算时可以针对这个field关闭这个选项。
这个mapping param 从able变成 disable 可以通过PUT mapping api进行修改,但是无法再从disable变成able
PUT my_index/_mapping
{
"properties": {
"title": {
"type": "text",
"norms": false
}
}
}
update 成false之后不会立即移除norms存储,会在old segment合并生成新的segment的时候去除norms信息
20. null_value
正常情况下,null值被认为该字段没有值,所以该doc的该字段不会被indexed,也不可searchable
null_value 的设置可以在该field indexing的值为null的时候放入一个特殊值,这个值可以进行indexed和searchable,需要注意的是该字段必须被设置为null,如果index的时候不传该字段的话则也不会被替换成null_value的值
当然,该字段同样不会修改_source字段
样例
PUT my_index
{
"mappings": {
"properties": {
"status_code": {
"type": "keyword",
"null_value": "NULL"
}
}
}
}
PUT my_index/_doc/1
{
"status_code": null
}
PUT my_index/_doc/2
{
"status_code": []
}
PUT my_index/_doc/3
{
“message”:"this is message"
}
GET my_index/_search
{
"query": {
"term": {
"status_code": "NULL"
}
}
}
对于查询而要,doc 1 会被召回,doc2 ,doc3不会被召回,doc3是因为没有显式的指定该字段的值为null。
21. position_increment_gap
这个mapping param主要用在string.text字段,而且是当该field 的value是一个数组的时候才会起作用。
正常情况下一个text输入field之前会进行analysis,一般会有ananlzyer分词,并且记录每个词的position,当一个filed的输入有多个values的时候,在这些values之间实际上是不应该有position信息的,所以一旦这样的输入出现,会填充虚假的gap,让不同的field之间的position加大。
观察下面的analyze
POST my_index/_analyze
{
"analyzer": "standard",
"text": ["want some","like bread"]
}
{
"tokens" : [
{
"token" : "want",
"start_offset" : 0,
"end_offset" : 4,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "some",
"start_offset" : 5,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "like",
"start_offset" : 10,
"end_offset" : 14,
"type" : "<ALPHANUM>",
"position" : 102
},
{
"token" : "bread",
"start_offset" : 15,
"end_offset" : 20,
"type" : "<ALPHANUM>",
"position" : 103
}
]
}
可以看到 like bread
的position 一个是102,一个是103,这就说明中间加入了虚假的gap,因为中间的position实际上是没有词的。
gap 的值 就是由position_increment_gap 设置的,默认是100
这种操作主要是为了在进行phrase查询的时候出错。
比如查询some like
命中了该文档是不合理的。
比如下面的案例
PUT my_index
{
"mappings": {
"properties": {
"names": {
"type": "text",
"position_increment_gap": 0
}
}
}
}
PUT my_index/_doc/1
{
"names": [ "John Abraham", "Lincoln Smith"]
}
GET my_index/_search
{
"query": {
"match_phrase": {
"names": "Abraham Lincoln"
}
}
}
该查询会将doc1召回,显然是不合理的。
22. properties
mapping中的 object fields 和 nested fields 包含properties属性,这个属性可以是任何数据类型,包括object和nested类型
properties可以通过三种方式设置
- 创建index的时候设置
- update index mapping (使用put mapping api)
- 根据indexing的doc动态的生成
PUT my_index
{
"mappings": {
"properties": {
"manager": {
"properties": {
"age": { "type": "integer" },
"name": { "type": "text" }
}
},
"employees": {
"type": "nested",
"properties": {
"age": { "type": "integer" },
"name": { "type": "text" }
}
}
}
}
}
PUT my_index/_doc/1
{
"region": "US",
"manager": {
"name": "Alice White",
"age": 30
},
"employees": [
{
"name": "John Smith",
"age": 34
},
{
"name": "Peter Brown",
"age": 26
}
]
}
这些子属性可以通过点号.
来再query或者agg中引用。
GET my_index/_search
{
"query": {
"match": {
"manager.name": "Alice White"
}
},
"aggs": {
"Employees": {
"nested": {
"path": "employees"
},
"aggs": {
"Employee Ages": {
"histogram": {
"field": "employees.age",
"interval": 5
}
}
}
}
}
}
23. search_analyzer
一般情况下,会在indexing和query的时候使用相同的analyzer 来保持分词使用的一致性。
但是有些时候我们也可以在index和query的时候使用不同的analyzer来达到更好的搜索效果,比如,对于著名的中文分词器 ik-analyzer 插件,
在index的时候我们一般使用 ik_max_word
analyzer 来产生尽可能多的词汇以便于在搜索的时候使用
在query的时候我们一般使用 ik_smart
产生最佳分词(不是最多的,可能是按照最有意义的分词方式)这样的命中效果会更好一些,防止召回的太多。
又或者使用edge_ngram进行indexing分词,在query的时候使用standard,可以实现前缀匹配
PUT my_index
{
"settings": {
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
},
"mappings": {
"properties": {
"text": {
"type": "text",
"analyzer": "autocomplete",
"search_analyzer": "standard"
}
}
}
}
PUT my_index/_doc/1
{
"text": "Quick Brown Fox"
}
GET my_index/_search
{
"query": {
"match": {
"text": {
"query": "Quick Br",
"operator": "and"
}
}
}
}
24. similarity
这个主要用来设置es的string.text查询的相似度计算模型
这个计算模型可以自定义,也可以使用es内置的开箱即用,一般情况下内建的开箱即用就可以满足大部分需求,如果采用自定义的方式需要对相似度计算的模型比较熟悉才好操作。在后面介绍相似度模型的时候会重点再介绍es中有哪些相似度计算模型,这里只是着重说一下使用es内置的如果和配置。
es内置的相似度打分模型有3个
- BM25 : es中默认的打分模型
- classic: es早起版本的打分模型,也是经典的打分模型
- boolean: 只能判断是否命中,得分score就是query boost
PUT my_index
{
"mappings": {
"properties": {
"default_field": {
"type": "text"
},
"boolean_sim_field": {
"type": "text",
"similarity": "boolean"
}
}
}
}
关于BM25的discount_overlaps 的一个比较好的解释
https://stackoverflow.com/questions/44115497/elasticsearch-similarity-discount-overlaps
25. store
正常情况下field会被indexed,可以被search,整个doc的内容都会被存储到_source字段,
但是这个字段的原始值不会被单独存储,如果只想获取这个字段的原始值的话需要通过source filtering
PUT my_index
{
"mappings": {
"properties": {
"title": {
"type": "text",
"store": true
},
"date": {
"type": "date",
"store": true
},
"content": {
"type": "text"
}
}
}
}
PUT my_index/_doc/1
{
"title": "Some short title",
"date": "2015-01-01",
"content": "A very long content field..."
}
GET my_index/_search
{
"stored_fields": [ "title", "date" ]
}
返回
"hits" : [
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"fields" : {
"date" : [
"2015-01-01T00:00:00.000Z"
],
"title" : [
"Some short title"
]
}
}
]
同样的,我们也可以这样使用
GET my_index/_search
{
"_source": ["title","date"]
}
返回
hits" : [
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"date" : "2015-01-01",
"title" : "Some short title"
}
}
]
一般情况下没有必要的话不要开启这个属性,只有在doc中有某些field的内容非常大,而有些查询不需要的那些大内容的field的时候才建议对小内容的field使用store属性。
26. term_vector
term向量是string.text类型的数据进行了analysis处理后的产生的term的组合。
他包含了下面的内容
- 一个terms的list
- 每个term中的position
- 每个term在原始文档中的offset
- payload 信息
term_vector 的设置可以有
no: No term vectors are stored. (default) 这个是默认值
yes: Just the terms in the field are stored.
with_positions: Terms and positions are stored.
with_offsets: Terms and character offsets are stored.
with_positions_offsets: Terms, positions, and character offsets are stored.
with_positions_payloads: Terms, positions, and payloads are stored.
with_positions_offsets_payloads: Terms, positions, offsets and payloads are stored.
当我们使用term vectors API的时候可以查看一个文档中field的vector
样例
PUT my_index
{
"mappings": {
"properties": {
"text": {
"type": "text",
"term_vector": "with_positions_offsets"
},
"number":{
"type": "text"
}
}
}
}
PUT my_index/_doc/1?refresh
{
"text": "Quick brown fox",
"number":"want app and want banana"
}
使用terms vectors api查看field包含的terms
GET my_index/_termvectors/1?fields=number,text
返回
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"found" : true,
"took" : 0,
"term_vectors" : {
"text" : {
"field_statistics" : {
"sum_doc_freq" : 3,
"doc_count" : 1,
"sum_ttf" : 3
},
"terms" : {
"brown" : {
"term_freq" : 1,
"tokens" : [ { "position" : 1, "start_offset" : 6, "end_offset" : 11}]
},
"fox" : {
"term_freq" : 1,
"tokens" : [ { "position" : 2, "start_offset" : 12, "end_offset" : 15}]
},
"quick" : {
"term_freq" : 1,
"tokens" : [ { "position" : 0, "start_offset" : 0, "end_offset" : 5}]
}
}
},
"number" : {
"field_statistics" : {
"sum_doc_freq" : 4,
"doc_count" : 1,
"sum_ttf" : 5
},
"terms" : {
"and" : {
"term_freq" : 1,
"tokens" : [ { "position" : 2, "start_offset" : 9, "end_offset" : 12}]
},
"app" : {
"term_freq" : 1,
"tokens" : [ { "position" : 1, "start_offset" : 5, "end_offset" : 8}]
},
"banana" : {
"term_freq" : 1,
"tokens" : [ { "position" : 4, "start_offset" : 18, "end_offset" : 24}]
},
"want" : {
"term_freq" : 2,
"tokens" : [ { "position" : 0, "start_offset" : 0, "end_offset" : 4},
{ "position" : 3, "start_offset" : 13, "end_offset" : 17}]
}
}
}
}
}
以上,可以看到文档的term vector信息,为什么number字段没有设置保存term_vector 在使用api的时候依然能够查到呢,这是因为terms vector api的原理导致的,如果没有的话他会自己从index中进行计算得到数据
假如直接使用
GET my_index/_termvectors/1
这个只会获取默认已经存储的term vectors,那么就不会返回number的term vectors了。
使用
GET my_index/_termvectors/1?fields=number,text
会触发强制计算term vectors。
这个一般情况下没有什么用,只是在快速高亮显示上又一些用处。
而且term_vector设置到with_positions_offsets 就会使这个字段在索引中的存储翻倍,因为需要一个额外的list terms去维护。