ES 常见类型
通用类型:
二进制: binary
布尔型: boolean
字符串: keyword
, constant_keyword
, wildcard, text
别名: alias
对象: object, flattened, nested, join
结构化数据类型: Range, ip, version, murmur3
空间数据类型: geo_point, geo_shape, point, shape
性能相关参数
_all
_all
字段的索引方式是将所有其他字段的值作为一个大字符串索引的,通过include_in_all参数指定哪些字段的内容放入_all
字段,_all
字段在v6.0+已经废弃,v7.0正式移除,但是可以通过copy_to来实现_all相同功能.
PUT /test_index
{
"mappings": {
"properties": {
"field1": {
"type": "text",
"copy_to": "field_all"
},
"field2": {
"type": "text",
"copy_to": "field_all"
},
"field_all": {
"type": "text"
}
}
}
}
POST /test_index/_doc/1
{
"field1": "smith",
"field2": "John@example.com"
}
GET /test_index/_search
{
"query": {
"match": {
"field_all": "smith"
}
}
}
现在field_all存的是[“smith”, “John@example.com”].
优点: 当我们想要查询field1或者field2中包含“smith”的时候,只要查询field_all字段就行. 不需要从两个字段的倒排索引中查询两遍.
缺点: 我们不清楚“smith”是在field1还是field2, 需要按照字段或者“smith”在字段中的占比算score的时候就不支持了. 额外需要硬盘空间存储field_all字段内容.
_source
_source 字段用于存储 post 到 ES 的原始 json 文档。为什么要存储原始文档呢?因为 ES 采用倒排索引对文本进行搜索,而倒排索引无法存储原始输入文本。一段文本交给ES后,首先会被分析器(analyzer)打散成单词,为了保证搜索的准确性,在打散的过程中,会去除文本中的标点符号,统一文本的大小写,甚至对于英文等主流语言,会把发生形式变化的单词恢复成原型或词根,然后再根据统一规整之后的单词建立倒排索引,经过如此一番处理,原文已经面目全非。因此需要有一个地方来存储原始的信息,以便在搜到这个文档时能够把原文返回给查询者。和所有被存储的字段一样, _source
字段在被写入磁盘之前先会被压缩。
那么一定要存储原始文档吗?不一定!如果没有取出整个原始 json 结构体的需求,可以在 mapping 中关闭 source 字段或者只在 source 中存储部分字段(使用store)。但是这样做有些负面影响:
(1)不能获取到原文
(2)无法reindex:如果存储了 source,当 index 发生损坏,或需要改变 mapping 结构时,由于存在原始数据,ES可以通过原始数据自动重建index,如果不存 source 则无法实现
(3)无法在查询中使用script:因为 script 需要访问 source 中的字段
(4)如果没有 _source
字段,部分 update
请求不会生效。
如果不需要如上功能,可以禁用_source,减少空间或者指定字段储存到_source, 一般不建议禁用_source,可以通过设置best_compression高压缩比来代替禁用_source.
PUT test_index
{
"mappings": {
"_source": {
"enabled": false
}
}
}
PUT /test_index
{
"mappings": {
"_source": {
"includes": [
"field1"
],
"excludes": [
"field2"
]
}
}
}
store
store 决定一个字段是否要被单独存储。大家可能会有疑问,_source 里面不是已经存储了原始的文档嘛,为什么还需要一个额外的 store 属性呢?原因如下:
(1)如果禁用了 _source 保存,可以通过指定 store 属性来单独保存某个或某几个字段,而不是将整个输入文档保存到 _source 中。
(2)如果 _source 中有长度很长的文本(如一篇文章)和较短的文本(如文章标题),当只需要取出标题时,如果使用 _source 字段,ES需要读取整个 _source 字段,然后返回其中的 title,由此会引来额外的IO开销,降低效率。此时可以选择将 title 的 store 设置为true,在 _source 字段外单独存储一份。读取时不必在读取整 _source 字段了。但是需要注意,应该避免使用 store 查询多个字段,因为 store 的存储在磁盘上不连续,ES在读取不同的 store 字段时,每个字段的读取均需要在磁盘上进行查询操作,而使用 _source 字段可以一次性连续读取多个字段。
PUT /test_index
{
"mappings": {
"properties": {
"field1": {
"type": "text",
"copy_to": "field_all",
"store": true
},
"field2": {
"type": "text",
"copy_to": "field_all"
},
"field_all": {
"type": "text",
"store": true
}
},
"_source": {
"enabled": false
}
}
}
GET /test_index/_search
{
"stored_fields": ["field1", "field_all"]
}
doc_values
倒排索引可以提供全文检索能力,但是无法提供对排序和数据聚合的支持。如果没有doc_values, 聚合A字段流程如下:
- 通过倒排索引查询到匹配的docId列表
- 根据docId列表去各个分片获取到Doc的_source字段
- 解压缩_source字段获取json字符串里面的A字段
- 统一在内存中进行A字段的聚合
doc_values 本质上是一个序列化的列式存储结构,适用于聚合(aggregations)、排序(Sorting)、脚本(scripts access to field)等操作。当A字段开启doc_values时, 聚合A字段流程如下:
- 通过倒排索引查询到匹配的docId列表
- 根据docId列表去各个分片获取到Doc的doc_values里面的A字段, 由于doc_values是列存储,A字段在内存中是连续的,获取方便
- 统一在内存中进行A字段的聚合
默认情况下,ES几乎会为所有类型的字段存储doc_value,但是 text 或 text_annotated 等可分词字段不支持 doc values 。如果不需要对某个字段进行排序或者聚合,则可以关闭该字段的doc_value存储(doc_values: false),关闭后不能进行聚合和排序操作。
PUT /test_index
{
"mappings": {
"properties": {
"field1": {
"type": "keyword",
"copy_to": "field_all",
"store": true
},
"field2": {
"type": "keyword",
"copy_to": "field_all",
"doc_values": false
},
"field_all": {
"type": "text",
"store": true
}
},
"_source": {
"enabled": false
}
}
}
GET /test_index/_search
{
"query": {
"match_all": {}
},
"aggregations": {
"terms_field2": {
"terms": {
"field": "field1"
}
}
}
}
index
控制倒排索引,用于标识指定字段是否需要被索引。默认情况下是开启的,如果关闭了 index,则该字段的内容不会被 analyze 分词,也不会存入倒排索引,即意味着该字段无法被搜索。
PUT /test_index
{
"mappings": {
"properties": {
"field1": {
"type": "keyword",
"copy_to": "field_all",
"store": true,
"index": false
},
"field2": {
"type": "keyword",
"copy_to": "field_all",
"doc_values": false
},
"field_all": {
"type": "text",
"store": true
}
},
"_source": {
"enabled": true
}
}
}
GET /test_index/_search
{
"query": {
"match": {
"field1": "smith"
}
}
}
enabled
这是一个 index 和 doc_value 的总开关,如果 enabled 设置为false,则这个字段将会仅存在于 source 中,其对应的 index 和 doc_value 都不会被创建。这意味着,该字段将不可以被搜索、排序或者聚合,但可以通过 source 获取其原始值。