03.elasticsearch-mapping-param解析

1. mapping param

mapping param 是在定义es中index存储的doc的某个字段的一些特征的,他决定了该字段的存储方式以及在存储之前的analysis部分如何进行。
不同的数据类型的field可以设置的mapping param是下面概览的中的一个子集。在前文中不同的type的field能够设置的mapping param也是有记录的。

1. 概览

  1. 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

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中有这样的配置

  1. analyzer: indexing的时候使用,会包含所有的词,包括stop words
  2. search_analyzer: 非phrase查询使用,会remove stop words
  3. 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字段中进行存储。
没有那么脏的数据,比如

  1. 定义的type为integer, indexing为 “5” 这种数字字符串,这种可以辅助进行处理
  2. 定义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 有以下特性:

  1. 拷贝的是field,不是terms,所以具体产生的terms还是要看目标field的analyzer
  2. _source字段不会发生改变,所以正常返回的文档中不会不有copy过的内容
  3. 一个字段可以被拷贝到多个字段,配置的方式是"copy_to": [ "field_1", "field_2" ]
  4. 进行递归的拷贝是无效的,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在起作用,该字段可以有三个值

  1. true : 默认值,新检测到的field可以添加到mapping当中
  2. false: 新检测到的field会被忽略,不能通过新加入的这个filed进行search操作,但是这个字段还是会出现在_source字段中哦,因为_source就是啥都不管,直接存
  3. 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的使用场景

  1. keyword,ip,flattened类型字段的agg操作
  2. text字段开启了fielddata之后的agg操作
  3. 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 的使用

  1. 对于terms,smapler,significant_terms 的agg操作支持一个叫 execution_hint 的参数,默认的话是使用 global ordinals 但是可以设置直接使用field-value 进行bucketed 操作
  2. 对于不再更新的索引,可以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的字段进行设置的。
主要设置了哪些信息会被加入到倒排索引当中。
可以是下面的几个值

  1. docs: 只有doc number 被加入,只能回答当前doc的这个field是否包含这个term
  2. freqs: doc-number + term-frequencies ,就是这个term在当前field中出现的频次,可以用来计算score
  3. positions: doc-numer + term-frequencies + term-position position可以用来做phrase或者距离查询 ()
  4. 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的前缀来加快前缀查询匹配的速度。
有两个参数

  1. min_chars : 前缀最少的字符数
  2. 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可以通过三种方式设置

  1. 创建index的时候设置
  2. update index mapping (使用put mapping api)
  3. 根据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_wordanalyzer 来产生尽可能多的词汇以便于在搜索的时候使用
在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个

  1. BM25 : es中默认的打分模型
  2. classic: es早起版本的打分模型,也是经典的打分模型
  3. 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的组合。
他包含了下面的内容

  1. 一个terms的list
  2. 每个term中的position
  3. 每个term在原始文档中的offset
  4. 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去维护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值