思维导图
前置
在第4章,我们提到了 keyword
(一笔带过)。在本章,我们将介绍 ES 的字段类型。全面的带大家了解 ES 各个字段类型的使用场景。
字段类型
ES 支持以下字段类型(仅介绍开发中常用,更多内容请自行阅读 官方文档)。
Keyword
基本介绍
手动设置字段类型为 keyword
PUT /test3
{"mappings": {"properties": {"tags": {"type": "keyword"}}}
}
写入数据
PUT /test3/_doc/1
{"tags": "hello world"
}
keyword
其实就是字符串,输入什么,存储就是什么。
适用场景
keyword
适用于 排序、聚合、term(精确查询)
查询场景中。
例如
GET /test3/_search
{"query": {"term": {"tags": {"value": "hello"}}}
}
查询优化
有 2 个对查询优化重要的点:
- 数字类型(int, long)如果不需要使用范围查询,则建议设置为 keyword
- term 查询在 keyword 上的搜索速度总是快于数字类型。
Text
基本介绍
与 keyword 相对的则是 text。在第三章,我们介绍了全文搜索 match 的用法。你可能会好奇,为啥默认写入的数据就可以使用全文搜索。因为当输入是无规则字符串时,字段类型就是 text。(别着急,默认的字段类型,一会我们就会详细介绍)
手动设置字段类型为 text
# 先删除索引
DELETE test3PUT /test3
{"mappings": {"properties": {"tags": {"type": "text"}}}
}
适用场景
text 适用场景:全文搜索
text 字段会对输入进行分词。
例如
PUT /test3/_doc/1
{"tags": "hello world"
}
tags 会被分词存储为 hello、world 2个词。
当然,具体被分词为什么,其实跟我们设置的分词器有关(后续讲解,这里先有个概念)。
不适用场景
text 不适用场景:排序、聚合、脚本。
如果你在 text 字段上,进行排序、聚合,或者脚本操作,都会收到以下异常。
Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [name] in order to load field data by uninverting the inverted index. Note that this can use significant memory.
例如:
GET /test3/_search
{"sort": [{"tags": {"order": "desc"}}]
}# 聚合
GET /test3/_search
{"size": 0,"aggs": {"popular_tags": {"terms": {"field": "tags"}}}
}# 脚本操作
GET /test3/_search
{"query": {"script": {"script": "doc['tags'].value == 'hello'"}}
}
illegal_argument_exception 异常解决方式
要解决该异常,有2种方法
- 使用多字段类型,即在该字段上面再建一个 keyword 类型(强烈建议)
DELETE test3PUT test3
{"mappings": {"properties": {"tags": {"type": "text","fields": {"keyword": {"type": "keyword"}}}}}
}
排序、聚合时,则使用 tags.keyword
。需要全文索引时,依然可以使用 tags
字段。
GET /test3/_search
{"sort": [{"tags.keyword": {"order": "desc"}}]
}
- text 字段上启用
fielddata
(不建议!不建议!不建议!)
DELETE test3PUT /test3/_mapping
{"properties": {"tags": {"type": "text","fielddata": true}}
}
PS:在 text 字段上启用 fielddata,会消耗非常大的内存!!!
Date
手动指定字段类型为 date
PUT /test3
{"mappings": {"properties": {"ctime": {"type": "date"}}}
}
未指定 format
参数时,默认的值为 strict_date_optional_time||epoch_millis
该默认值接收以下数据
# 秒时间戳
PUT /test3/_doc/1
{"ctime": 1721135125
}
# 毫秒时间戳
PUT /test3/_doc/2
{"ctime": 1721135125000
}
# datetime
PUT /test3/_doc/3
{"ctime":"2024-07-16T12:10:30Z"
}
# date
PUT /test3/_doc/4
{"ctime": "2024-07-15"
}# 对数据排序
GET test3/_search
{"sort": { "ctime": "asc"}
}
format 参数
我们可以手动指定允许的数据格式。例如
PUT /test3
{"mappings": {"properties": {"ctime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis||epoch_second"}}}
}
存储时间仅为秒时间戳
如果时间为秒时间戳,可以考虑使用 epoch_second
PUT my-index-000001
{"mappings": {"properties": {"ctime": {"type": "date","format": "strict_date_optional_time||epoch_second"}}}
}PUT /test3/_doc/1
{"ctime": 1721135125
}# 以下查询,时间会被格式化
GET test3/_search
{"fields": [ {"field": "ctime"}]
}
数字类型
在复习一下,如果不需要范围查询,建议使用 keyword
存储(后续在进阶篇会讲原理)。
scaled_float
ES 除了支持常见的数字类型。如:long、integer、short、byte、double、float
还针对浮点数,有一个优化的类型 scaled_float
。
如果我们能够得知我们的浮点数最多有多少个小数点。使用该类型,在空间存储上会比浮点数更好。
PUT /test3
{"mappings": {"properties": {"sf": {"type": "scaled_float","scaling_factor": 100}}}
}
上面的意思为:存储时,* 100。即,将浮点数变为整数。
Boolean
PUT /test3
{"mappings": {"properties": {"enable": {"type": "boolean"}}}
}
- false, “false”, “” (empty string) 均被认为是 false
- true, “true” 均被认为是 true
POST /test3/_doc
{"enable": false
}
POST /test3/_doc
{"enable": "false"
}
POST /test3/_doc
{"enable": ""
}
POST /test3/_doc
{"enable": true
}
POST /test3/_doc
{"enable": "true"
}GET /test3/_search
{"query": {"term": {"enable": {"value": false}}}
}
Object
写入一个 manager 对象
PUT test3/_doc/1
{ "region": "US","manager": { "age": 30,"name": { "first": "John","last": "Smith"}}
}
在 ES 内部,该文档被索引为一个简单的键值对列表,大致如下
{"region": "US","manager.age": 30,"manager.name.first": "John","manager.name.last": "Smith"
}
例如,我们可以查询 manager.age=30
的文档
GET /test3/_search
{"query": {"term": {"manager.age": {"value": 30}}}
}
上述文档的显式映射如下
PUT /test3
{"mappings": {"properties": { "region": {"type": "keyword"},"manager": { "properties": {"age": { "type": "integer" },"name": { "properties": {"first": { "type": "text" },"last": { "type": "text" }}}}}}}
}
Array
- 不支持混合数据类型的数组
POST /test3/_doc
{"arr": ["12", 12, false]
}
- 无法查询数组中的每个对象
PUT test3/_doc/1
{"group" : "fans","user" : [ {"first" : "John","last" : "Smith"},{"first" : "Alice","last" : "White"}]
}# 查询 user.first=Alice & user.last=White。你可能会使用以下写法,但实际上并不能正确工作GET test3/_search
{"query": {"bool": {"must": [{ "match": { "user.first": "Alice" }},{ "match": { "user.last": "Smith" }}]}}
}
如果你的索引结构是这么设计的,并且有这样的需求,可能需要考虑下如何做优化了。例如,将表铺平。
PS:虽然 nested
嵌套类型可以解决该问题,但开发中会尽可能的把数据结构铺平,从而避免使用 nested
嵌套类型。这里不对 nested
过多介绍,因为开发中真的很不推荐使用。
- 开发中仅推荐基本数据类型数组,不推荐对象数组,如果你有第2 点描述的查询需求
PUT test3
{"mappings": {"properties": {"arr": {"type": "keyword"}}}
}PUT test3/_doc/1
{"arr": ["1", "2", "3"]
}GET test3/_search
{"query": {"term": {"arr": {"value": "1"}}}
}