ElasticSearch数据类型与映射

不同版本的 ElasticSearch API 可能有所区别,本文基于 7.17 版本。ElasticSearch 有三个重要的概念,索引、映射、文档,本文介绍 ElasticSearch 的映射。映射是用来用来定义文档及其字段的存储方式、索引方式的手段,与关系型数据库中的表结构类似,可以从映射信息中得知索引下有哪些字段,字段的数据类型以及有哪些约束信息。

映射信息

每个索引都有唯一的 mapping type,用于决定文档将如何被索引。mapping type由下面两部分组成:

Meta-fields:

元字段用于自定义如何处理文档的相关元数据,其中有身份元数据、索引元数据、文档元数据、路由元数据以及其他类型的元数据。

  • 身份元数据

一个文档除了有数据之外,它还包含了元数据(Metadata)。每创建一条数据时,都会对元数据进行写入等操作。

_index:文档所属索引 , 自动被索引,可被查询,聚合,排序使用,或者脚本里访问
_type:文档所属类型,自动被索引,可被查询,聚合,排序使用,或者脚本里访问
_id:文档的唯一标识, 建索引时候传入 ,不被索引, 可通过_uid被查询,脚本里使用,不能参与聚合或排序
_uid:由_type和_id字段组成,自动被索引 ,可被查询,聚合,排序使用,或者脚本里访问

  • 索引元数据

_all: 自动组合所有的字段值,以空格分割,可以指定分器词索引,但是整个值不被存储,所以此字段仅仅能被搜索,不能获取到具体的值。
_field_names:索引了每个字段的名字,可以包含null值,可以通过exists查询或missing查询方法来校验特定的字段。
_timestamp:可以手工指定时间戳值,也可以自动生成使用now()函数,除此之外还可以设置日期的格式化,忽略确实等功能。
_ttl:对于一些有生命周期的会话,会话的数据或者验证码是具有失效时间的,在 ElasticSearch 中可以通过ttl来很方便的设置存活时间,比如1小时,或者10分钟,超时过后,这个文档会被自动删除,需要注意的是这种方式并不适合按周或按天删除历史数据,这种需求可考虑使用索引级别的管理方式(名称+时间+定时删除索引)。

  • 文档元数据

_source : 一个文档的原生的json数据,不会被索引,用于获取提取字段值 ,启动此字段,索引体积会变大,如果既想使用此字段又想兼顾索引体积,可以开启索引压缩。
_size: 整个_source字段的字节数大小,可被禁用。

  • 路由元数据

_parent:在同一个索引中,可以通过_parent字段来给两个不同mapping type的数据建立父子关系,在查询时可以通过has_child, has_parent等查询,来聚合join数据,需要注意的是,父子type必须不能是一样的,否则会识别失败。
_routing: 一个文档可以被路由到指定的shard上。默认情况下,会使用文档的_id字段来参与路由规则,如果此文档有关联关系,则会以上级文档的_id作为路由规则,以确保文档之间的数据必须处于同一个shard上,以提高join效率。

Fields or properties:

映射类型包含与文档相关的字段、字段类型或属性的列表。

数据类型

文本类型

  • string

仅存在于早期版本的 ElasticSearch 中,自 6.x 版本开始移除支持,可使用 Text 或 Keyword 类型替代。

  • text

当一个字段需要用于分词搜索时,可使用 text 类型。需要注意的是 text 字段不能用于过滤、排序,也很少用于聚合。

  • keyword

当一个字段不需要分词只需要直接存储时,可使用 keyword 类型,这个类型常常被用来过滤、排序和聚合。

数字类型

  • byte:有符号的8位整数, 范围: [-128 ~ 127]
  • short:有符号的16位整数, 范围: [-32768 ~ 32767]
  • integer:有符号的32位整数, 范围: [−2^31 ~ 2^31-1]
  • long:有符号的64位整数,范围: [−2^63 ~ 2^63-1]
  • float:32位单精度浮点数
  • double:64位双精度浮点数
  • half_float:16位半精度IEEE 754浮点类型
  • scaled_float:带缩放类型的的浮点数, 比如price字段只需精确到分, 57.34缩放因子为100, 存储结果为5734

在定义字段映射的数据类型时,尽可能选择范围小的数据类型, 字段的长度越短, 索引和搜索的效率越高;优先考虑使用带缩放因子的浮点类型。

在定义数字类型的字段映射时还可以设置 coerce 强制转换参数(默认为true),开启强制转换后 ElasticSearch 会自动转换创建文档时传入的值,例:将数字字符转换为数字(在向ES添加数据时传递JSON,JSON中数字类型的值不带引号);映射类型定义为整型但传入浮点型数据则截断浮点数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# PUT http://127.0.0.1:9200/category/_mapping
{
"properties": {
"price":{
"type": "scaled_float",
"scaling_factor": 100
},
"stock_enable":{
"type": "integer",
"coerce": true
},
"stock_disable":{
"type": "integer",
"coerce": false
}
}
}

示例中定义了两个库存字段,分别开启/关闭了字段的强制类型转换,开启了强制类型转换的库存字段字符类型和数字类型的JSON均可传递(”stock_enable”: “10”),但关闭了强制类型转换的库存字段只能传递数字类型的JSON(”stock_disable”: 10) 。

1
2
3
4
5
6
#PUT http://127.0.0.1:9200/category/_doc/1
{
"price": "1057",
"stock_enable": "10",
"stock_disable": 10
}

日期类型

ElasticSearch 中的日期类型统一为 date,但日期的具体格式并未定义,需要在定义映射时一并传入指定格式,多种格式之间看使用双竖线 || 分隔,ElasticSearch 会依次尝试直到匹配格式。如果未指定格式,则使用默认格式 strict_date_optional_time||epoch_millis

日期类型需要指定格式与 JSON 没有日期数据类型也有一定的关系,JSON 中的日期可以是 “2022-07-25” 或 “2022/07/25 14:10:30”,也可以是类似毫秒时间戳的长整型数字,也可以是类似秒级时间戳的整型数字。

1
2
3
4
5
6
7
8
9
# PUT http://127.0.0.1:9200/info
{
"properties": {
"birth": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}

布尔类型

ElasticSearch 中的布尔类型统一为 boolean,在向索引添加文档时可以接受真假的布尔、字符串、数字类型。

真值: true, “true”, “on”, “yes”, “1”.

假值: false, “false”, “off”, “no”, “0”, “”(空字符串), 0.0, 0

二进制型

二进制类型是Base64编码字符串的二进制值, 不以默认的方式存储, 且不能被搜索,且Base64编码的二进制值不能嵌入换行符\n, 逗号0x2c等符号。有2个设置项:

(1) doc_values: 该字段是否需要存储到磁盘上, 方便以后用来排序、聚合或脚本查询. 接受true和false(默认);
(2) store: 该字段的值是否要和_source分开存储、检索, 意思是除了_source中, 是否要单独再存储一份. 接受true或false(默认).

范围类型

  • integer_range:支持范围 [−2^31 ~ 2^31-1]
  • long_range:支持范围 [−2^63 ~ 2^63-1]
  • float_range:支持范围 32位单精度浮点型
  • double_range:支持范围 64位双精度浮点型
  • date_range:支持范围 64位整数, 毫秒计时
  • ip_range:IP值的范围, 支持IPV4和IPV6, 或者这两种同时存在。如 192.168.1.0/24

数组类型

ElasticSearch 中没有专门的数组类型, 直接使用 [] 定义即可。

使用数组类型时,需要注意以下几点问题:

  1. 数组中所有的值必须是同一种数据类型, 不支持混合数据类型的数组。
  2. 动态添加数据时, 数组中第一个值的类型决定整个数组的类型。
  3. 数组可以包含null值, 空数组 [] 会被当做没有值的字段(missing field)

对象类型

JSON 文档是分层的,因此文档可以包含内部对象, 内部对象也可以包含内部对象。

  1. 文档示例

    1
    2
    3
    4
    5
    6
    7
    8
    #PUT http://127.0.0.1:9200/employee/developer/1
    {
    "name": "aofall",
    "address": {
    "region": "China",
    "location": {"province": "Fujian", "city": "Fuzhou"}
    }
    }
  2. 存储方式

    1
    2
    3
    4
    5
    6
    {
    "name": "aofall",
    "address.region": "China",
    "address.location.province": "Fujian",
    "address.location.city": "Fuzhou"
    }
  3. 映射结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #PUT http://127.0.0.1:9200/employee
    {
    "mappings": {
    "developer": {
    "properties": {
    "name": { "type": "text", "index": "true" },
    "address": {
    "properties": {
    "region": { "type": "keyword", "index": "true" },
    "location": {
    "properties": {
    "province": { "type": "keyword", "index": "true" },
    "city": { "type": "keyword", "index": "true" }
    }
    }
    }
    }
    }
    }
    }
    }

嵌套类型

与 JSON 一样,ElasticSearch 可以嵌套组合数组和对象结构,但嵌套的数组内的对象可能会丢失关联性。

地理数据/地理形状类型

地理点类型用于存储地理位置的经纬度对

地理形状类型是多边形的复杂形状

IP类型

IP类型的字段用于存储IPv4或IPv6的地址, 本质上是一个长整型字段。

计数数据类型

token_count类型用于统计字符串中的单词数量.

本质上是一个整数型字段, 接受并分析字符串值, 然后索引字符串中单词的个数