映射类型
Elasticsearch 字段类型的核心类型有字符串类型、数字类型、日期类型、布尔类型、二进制类型、范围类型等。
一级分类 | 二级分类 | 具体类型 |
---|---|---|
核心类型 | 字符串类型 | text、keyword |
数字类型 | long、integer、short、byte、double、float、half_float、scaled_float | |
日期类型 | date | |
布尔类型 | boolean | |
二进制类型 | binary | |
范围类型 | range |
字符串类型
text
需要被全文搜索的
如:邮件内容、产品描述、新闻内容,
设置 text 类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分词器分成一个一个词项;
text 类型的字段不用于排序,且很少用于聚合
text 类型字段在映射中需要指定相关的分析器,通过 index 选项指定
index 选项可以设置为 analyzed(默认)、not_analyzed 或 no
analyzed
默认情况下,index 被设置为 analyzed,并产生了如下行为:分析器将所有字符转为小写,并将字符串分解为单词
当期望每个单词完整匹配时,请使用这种选项。举个例子,如果用户搜索 “elasticsearch”,他们希望在结果列表里看到 “Principles and Practice of Elasticsearch”。
not_analyzed
将 index 设置为 not_analyzed,将会产生相反的行为:分析过程被略过,整个字符串被当作单独的词条进行索引
当进行精准的匹配时,请使用这个选项,如搜索标签。你可能希望 “big data” 出现在搜索 “big data” 的结果中,而不是出现在搜索 “data” 的结果中。同样,对于多数的词条计数聚集,也需要这个。如果想知道最常出现的标签,可能需要 “big data” 作为一整个词条统计,而不是 “big” 和 “data” 分开统计。
no
index 设置为 no,索引会忽略,也没有词条产生,因此无法在那个字段上进行搜索。
当无须在这个字段上搜索时,这个选项节省了存储空间,也缩短了索引和搜索的时间。
例如,可以存储活动的评论。尽管存储和展示这些评论是很有价值的,但是可能并不需要搜索它们。在这种情况下,关闭那个字段的索引,使得索引的过程更快,并节省了存储空间。
keyword
keyword 类型适用于索引结构化的字段,比如 email 地址、主机名、状态码和标签,通常用于过滤、排序、聚合
类型为
keyword 的字段只能通过精确值搜索到
数字类型
数字类型支持 byte、short、integer、long、float、double、half_float 和 scaled_float
数字类型及取值范围
| 类型 | 取值范围 |
| —- | —- |
| long | -2^63 至 2^63-1 |
| integer | -2^31 至 2^31-1 |
| short | -32768 至 32767 |
| byte | -128 至 127 |
| double | 64 位双精度 IEEE 754 浮点类型 |
| float 32 | 位单精度 IEEE 754 浮点类型 |
| half_float | 16 位半精度 IEEE 754 浮点类型 |
| scaled_float | 缩放类型的浮点数 |
对于数字类型的字段,在满足需求的情况下,要尽可能选择范围小的数据类型
字段的长度越短,索引和搜索的效率越高
处理浮点数时,优先考虑使用 scaled_float 类型
映射示例PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"number_of bytes": {
"type": "integer"
},
"time_in_seconds": {
"type": "float"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
}
}
}
}
}
scaled_float 是通过缩放因子把浮点数变成 long 类型,比如价格只需要精确到分,price 字段的取值为 57.34,设置放大因子为 100,存储起来就是 5734。所有的 API 都会把 price 的取值当作浮点数,
Elasticsearch 底层存储的是整数类型,因为压缩整数比压缩浮点数更加节省存储空间
日期类型
JSON 中没有日期类型,所以在 Elasticsearch 中的日期可以是以下几种形式:
格式化日期的字符串,如 “2015-01-01” 或 “2015/01/01 12:10:30”。
代表 milliseconds-since-the-epoch 的长整型数(epoch 指的是一个特定的时间:1970-01-01 00:00:00 UTC)。
代表 seconds-since-the-epoch 的整型数。
Elasticsearch 内部会把日期转换为 UTC(世界标准时间),并将其存储为表示 milliseconds-since-the-epoch 的长整型数,这样做的原因是和字符串相比,数值在存储和处理时更快。日期格式可以自定义,如果没有自定义,默认格式:"strict_date_optional_time||epoch_millis"
映射示例PUT my_index
{"mappings": {"my_type": {"properties": {"dt": {"type": "date"}}}}}
写入示例
PUT my_index/my_type/1
{"dt": "2019-11-14"}
PUT my_index/my_type/2
{"dt": "2019-11-14T13:16:302"}
PUT my_index/my_type/3
{"dt": 1573664468000}
默认情况下,以上 3 个文档的日期格式都可以被解析,内部存储的是毫秒计时的长整型数。
布尔类型
可接受的值为true、false、”true”、”false”
映射示例PUT my_index
{ "mappings": {"my_type": {"properties": {"is_published": {"type": "boolean" } }} }}
写入示例
PUT my_index/my_type/1
{ "is_published": true}
PUT my_index/my_type/2
{"is_published": "true"}
PUT my_index/my_type/3
{"is_published": false}
binary 类型
PUT my_index
{
"mappings": {
"my_type": {
"properties": {"name": {"type": "text"}, "blob": {"type": "binary"} }
}
}
}
写入示例
PUT my_index/my_type/1
{ "name": "Some binary blob", "blob": "U29t2SBiaW5hcnkgYmxvYg=="}
range 类型
range 类型的使用场景包括网页中的时间选择表单、年龄范围选择表单等,range 类型支持的类型和取值范围如下表
类型 | 范围 |
---|---|
integer_range | -2^31 至 2^31-1 |
float_range | 32-bit IEEE 754 |
long_range | -2^63 至 2^63-1 |
double_range | 64-bit IEEE 754 |
date_range | 64 位整数,毫秒计时 |
映射示例
PUT range_index
{
"mappings": {
"my_type": {
"properties": {
"expected_attendees": {
"type": "integer_range"
},
"time_frame": {
"type": "date_range",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
}
索引一条文档,expected_attendees 的取值为 10 到 20,time_frame 的取值是 2015-10-31 12:00:00 至 2015-11-01,命令如下:
PUT my_index/my_type/1
{
"expected_attendees": {
"gte": 10,
"lte": 20
},
"time_frame": {
"gte": "2019-10-31 12:00:00",
"lte": "2019-11-01"
}
}
复合类型
Elasticsearch 字段 - 复合类型
一级分类 二级分类 具体类型
核心类型 数组类型 array
对象类型 object
嵌套类型 nested
数组类型
Elasticsearch 没有专用的数组类型,默认情况下任何字段都可以包含0个或者多个值,但是一个数组中的值必须是同一种类型。例如:
字符数组:[“one”,”two”]
整型数组:[1,3]
嵌套数组:[1,[2,3]],等价于 [1,2,3]
对象数组:[{“city”:”amsterdam”,”country”:”nederland”},{“city”:”brussels”,”country”:”belgium”}]
动态添加数据时,数组的第一个值的类型决定整个数组的类型。混合数组类型是不支持的,比如:[1,”abc”]。数组可以包含 null 值,空数组 [ ] 会被当作 missing field 对待。
在文档中使用 array 类型不需要提前做任何配置,默认支持。例如写入一条带有数组类型的文档,命令如下:
PUT my_index/my_type/1
{
“message”: “some arrays in this document…”,
“tags”: [“elasticsearch”, “wow”],
“lists”: [{
“name”: “prog_list”,
“description”: “programming list”
},
{
“name”: “cool_list”,
“description”: “cool stuff list”
}]
}
搜索 lists 字段下的 name,命令如下:
GET my_index/_search
{
“query”: {
“match”: {
“lists.name”: “cool_list”
}
}
}
object 类型
JSON 本质上具有层级关系,文档包含内部对象,内部对象本身还包含内部对象,请看下面的例子:
{
“region”: “US”,
“manager”: {
“age”: 30,
“name”: {
“first”: “John”,
“last”: “Smith”
}
}
}
上面的文档中,整体是一个 JSON 对象,JSON 中包含一个 manager 对象,manager 对象又包含名为 name 的内部对象。写入到 Elasticsearch 之后,文档会被索引成简单的扁平 key-value 对,格式如下:
{
“region”: “US”,
“manager.age”: 30,
“manager.name.first”: “John”,
“manager.name.last”: “Smith”
}
上面文档结构的显式映射如下:
{
“mappings”: {
“my_type”: {
“properties”: {
“region”: {
“type”: “keyword”
},
“manager”: {
“properties”: {
“age”: {
“type”: “integer”
},
“name”: {
“properties”: {
“first”: {
“type”: “text”
},
“last”: {
“type”: “text”
}
}
}
}
}
}
}
}
}
nested 类型
nested 类型是 object 类型中的一个特例,可以让对象数组独立索引和查询。Lucene 没有内部对象的概念,所以 Elasticsearch 将对象层次扁平化,转化成字段名字和值构成的简单列表。
使用 Object 类型有时会出现问题,比如文档 my_index/my_type/1 的结构如下:
PUT my_index/my_type/1
{
“group”: “fans”,
“user”: [{
“first”: “John”,
“last”: “Smith”
}, {
“first”: “Alice”,
“last”: “White”
}]
}
user 字段会被动态添加为 Object 类型,最后会被转换为以下平整的形式:
{
“group”: “fans”,
“user.first”: [“alice”, “john”],
“user.last”: [“smith”, “white”]
}
user.first 和 user.last 扁平化以后变为多值字段,alice 和 white 的关联关系丢失了。执行以下搜索会搜索到上述文档:
GET my_index/_search
{
“query”: {
“bool”: {
“must”: [{
“match”: {
“user.first”: “Alice”
}
},
{
“match”: {
“user.last”: “Smith”
}
}]
}
}
}
事实上是不应该匹配的,如果需要索引对象数组并避免上述问题的产生,应该使用 nested 对象类型而不是 object 类型,nested 对象类型可以保持数组中每个对象的独立性。Nested 类型将数组中每个对象作为独立隐藏文档来索引,这意味着每个嵌套对象都可以独立被搜索,映射中指定 user 字段为 nested 类型:
PUT /my_index
{
“mappings”: {
“my_type”: {
“properties”: {
“user”: {
“type”: “nested”
}
}
}
}
}
再次执行上述查询语句,文档不会被匹配。
索引一个包含 100 个 nested 字段的文档实际上就是索引 101 个文档,每个嵌套文档都作为一个独立文档来索引。为了防止过度定义嵌套字段的数量,每个索引可以定义的嵌套字段被限制在 50 个。
特殊类型:nested
建立数据默认的数据类型是Object
nested类型是一种特殊的对象;允许对象数组彼此独立地进行索引和查询;嵌套类型
(nested数据类型),可以防止数据出现扁平化的错误
扁平化示例
错误示例
新增数据PUT my_index/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
执行查询GET my_index/_search
{
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "Smith" }}
]
}
}
}
预期结果:查询出来为空
实际结果:user中的两个对象都会显示
说明:不符合预期结果(业务逻辑)
解决方案:使用nested嵌套类型
正确示例
修改类型为nested类型PUT my_index
,type使用nested类型
{
"mappings": {
"_doc": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
}
执行查询GET my_index/_search
{
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "Smith" }}
]
}
}
}
预期结果:查询出来为空
实际结果:数据为空
说明:符合预期结果(业务逻辑)
nested查询语法
GET /my_index/_search
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{"match": {"user.first": "Alice"}},
{"match": {"user.last" : "White"}}
]
}
}
}
}
}
nested聚合查询语法
GET my_index/_search
{
"query": {
"match": { "group": "fans" }
},
"aggs": {
"fan": {
"nested": { "path": "user" }
}
}
}