此文章已收录至项目 Developer-Knowledge-Base
官方文档:这里这里
binary
:接受二进制值作为 Base64 编码的字符串。默认情况下,该字段不存储,也不可搜索,不能包含换行符 \n
boolean
:布尔类型,可以接受 true
或 false
,可以使用字符串和直接到布尔类型,空字符串为 false
,包含:true
,false
,"true"
,"false"
,""
keyword
:关键字类型,不进行分词,直接索引,支持模糊、支持精确匹配,支持聚合、排序操作,用于筛选数据。最大支持的长度为——32766 个 UTF-8 类型的字符。number
:数字类型,文档链接
long
integer
short
byte
double
float
half_float
scaled_float
unsigned_long
Dates
:日期类型
date
:可以是格式化后的日期字符串,也可以是时间戳,例如 2015-01-01
, 2015-01-01T12:10:30Z
,1420070400001
date_nanos
:支持纳秒的日期格式,在 es 内部是存的长整型alias
:别名类型object
:对象类型,是一个 json
对象flattened
:将对象作为单个字段值存储nested
:嵌套数据类型,可以看成是一个特殊的对象类型,可以让对象数组独立检索join
:同一个文档,但具有父子关系的,类似于树range
:范围类型,可以用来表示数据的区间
integer_range
float_range
long_range
double_range
date_range
ip_range
文档链接
大部分内容来自 链接
JSON 没有日期数据类型,因此 Elasticsearch 中的日期可以是:
date
类型在 Elasticsearch
展示的格式有下面几种:
"2015-01-01"
或者 "2015/01/01 12:10:30"
milliseconds-since-the-epoch
,翻译一下就是自 1970-01-01 00:00:00 UTC
以来经过的毫秒数。int
型的整数,意义是 seconds-since-the-epoch
, 是指自 1970-01-01 00:00:00 UTC
以来经过的秒数。后两种的描述里都包含 UTC,什么是 UTC 呢?
UTC(Universal Time Coordinated) 叫做世界统一时间,中国大陆和 UTC 的时差是 + 8 ,也就是 UTC+8。
不论 date 是什么展示格式,在 Elasticsearch 内部存储时都是转换成 UTC,并且把时区也会计算进去,从而得到 milliseconds-since-the-epoch 并作为存储的格式。
在查询日期时,会执行下面的过程:
Date 的格式化类型是可以通过 format 来指定的,如果没有指定,就会使用默认的格式:
"strict_date_optional_time||epoch_millis"
这表示什么意思呢?
先来弄懂 strict_date_optional_time
A generic ISO datetime parser where the date is mandatory and the time is
optional. Full details here.
这是 elasticsearch 官网的解释,表示只要是 ISO datetime parser 可以正常解析的都是 strict_date_optional_time。都有哪些语法呢?
date-opt-time = date-element ['T' [time-element] [offset]]
date-element = std-date-element | ord-date-element | week-date-element
std-date-element = yyyy ['-' MM ['-' dd]]
ord-date-element = yyyy ['-' DDD]
week-date-element = xxxx '-W' ww ['-' e]
time-element = HH [minute-element] | [fraction]
minute-element = ':' mm [second-element] | [fraction]
second-element = ':' ss [fraction]
fraction = ('.' | ',') digit+
其中中括号内的都是可选的,可填可不填。以 std-date_element 举个例子
2018-11-19
2018
2018-11
上面 3 种格式都满足要求。
除了 strict_date_optional_time ,还可以是 epoch_millis 格式,即 epoch 以来的毫秒数。
举个例子
PUT my_index
{
"mappings": {
"_doc": {
"properties": {
"date": {
"type": "date"
}
}
}
}
}
PUT my_index/_doc/1
{ "date": "2015-01-01" }
PUT my_index/_doc/2
{ "date": "2015-01-01T12:10:30Z" }
PUT my_index/_doc/3
{ "date": 1420070400001 }
GET my_index/_search
{
"sort": { "date": "asc"}
上面的 PUT 请求中的 date 数据均满足默认的要求。
同一个 date 字段可以指定多个 date 格式,只要使用 ||
分隔就可以了。在索引,都会对 date 格式挨个进行匹配,直到找到匹配的格式为止。
如果存储时 date 格式为 milliseconds-since-the-epoch ,在查询时会将其转换为指定的第一个 date 格式。
举个例子,有兴趣的同学可在 sense 中动手实践下。
PUT my_index
{
"mappings": {
"doc": {
"properties": {
"date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
}
PUT /my_index/doc/1
{ "date": "2018-09-24 19:23:45" }
PUT /my_index/doc/2
{ "date": "2018-09-25" }
GET my_index/_search
{
"query": {
"match_all": {}
}
}
date_nanos
类型,支持纳秒date 类型支持到毫秒,如果特殊情况下用到纳秒得用 date_nanos 这个类型。
和普通的 date 类型一样,可以存 strict_date_optional_time||epoch_millis 这些格式的,不过在 es 内部是存的长整型是纳秒单位的
可以存一个纳秒的试试
PUT my_index/_doc/2
{ "date": "2015-01-01T12:10:30.123456789Z" }
keyword 类型字段可以设置 ignore_above 属性 (默认是 10) ,表示最大的字段值长度,超出这个长度的字段将不会被索引,但是会存储。也就是说,如果你超过了这个长度,模糊查询这个字段是查不到的,但查询全部是可以查询到的
官方文档
可以给字段起个别名,在搜索的时候可以使用别名搜索
官网示例
PUT trips
{
"mappings": {
"properties": {
"distance": {
"type": "long"
},
"route_length_miles": {
"type": "alias",
"path": "distance"
},
"transit_mode": {
"type": "keyword"
}
}
}
}
GET _search
{
"query": {
"range" : {
"route_length_miles" : {
"gte" : 39
}
}
}
}
path
就是目标字段的名称,不过有一些限制
字段别名只能指向一个字段,不能同时指向多个字段
不能在写入数据的时候使用字段别名,因为本身字段别名是虚拟的,不存在的,所以不支持写入,同样也不能用于 copy_to
因为字段的别名是不存在 _source
中的,所以搜索请求时的过滤字段也是不会生效的
官方文档
JSON 字符串允许嵌套对象,一个文档可以嵌套多个、多层对象。可以通过对象类型来存储二级文档,不过由于 Lucene 并没有内部对象的概念,ES 会将原 JSON 文档扁平化,例如文档:
PUT my-index-000001/_doc/1
{
"region": "US",
"manager": {
"age": 30,
"name": {
"first": "John",
"last": "Smith"
}
}
}
实际上 ES 会将其转换为以下格式,并通过 Lucene 存储,即使 name 是 object 类型
{
"region": "US",
"manager.age": 30,
"manager.name.first": "John",
"manager.name.last": "Smith"
}
上面的文档映射关系是
PUT my-index-000001
{
"mappings": {
"properties": {
"region": {
"type": "keyword"
},
"manager": {
"properties": {
"age": { "type": "integer" },
"name": {
"properties": {
"first": { "type": "text" },
"last": { "type": "text" }
}
}
}
}
}
}
}
不用设置 type:object
,因为这是默认值
官方文档:链接
可以去看看这篇:CSDN Elasticsearch:Flattened 数据类型映射
默认情况下,对象中的每个子字段都是单独映射和索引的。如果子字段的名称或类型事先不知道,然后动态映射。但 flattened
可以提供其中整个对象映射为单个字段
他将整个对象映射为单个字段。给定一个对象,映射将解析出它的叶子节点的值,并将它们作为关键字索引到一个字段中
为了更好地处理包含大量或未知数量字段的文档而引入的 Elasticsearch 扁平化数据类型 flattened datatype
。 默认情况下,Elasticsearch 会在提取文档时自动映射文档中包含的字段。虽然这是开始使用 Elasticsearch 的最简单方法,但随着时间的推移,它往往会导致字段爆炸,并且 Elasticsearch 的性能将受到“内存不足(out of memory)”错误和索引和查询数据时性能不佳的影响。
这种被称为“mapping explosions”的情况实际上很常见。这就是 Flattened 数据类型旨在解决的问题。
官网例子
PUT bug_reports
{
"mappings": {
"properties": {
"title": {
"type": "text"
},
"labels": {
"type": "flattened"
}
}
}
}
POST bug_reports/_doc/1
{
"title": "Results are not sorted correctly.",
"labels": {
"priority": "urgent",
"release": ["v1.2.5", "v1.3.0"],
"timestamp": {
"created": 1541458026,
"closed": 1541457010
}
}
}
如果直接查询顶级节点的话,将会搜索所有叶子节点的值
POST bug_reports/_search
{
"query": {
"term": {"labels": "urgent"}
}
}
如果需要查询对象中特定的属性的话,需要通过对象表达式来查询
POST bug_reports/_search
{
"query": {
"term": {"labels.release": "v1.3.0"}
}
}
查询时,不能使用通配符引用字段键
嵌套数据类型,可以看成是一个特殊的对象类型,可以让对象数组独立检索
PUT my-index-000001/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "wu",
"last" : "px"
},
{
"first" : "hu",
"last" : "xy"
},
{
"first" : "wu",
"last" : "mx"
}
]
}
如果 user
是对象数据类型,在内部会转换为
{
"group" : "fans",
"user.first" : [ "wu", "hu", "wu"],
"user.last" : [ "px", "xy" ,"mx"]
}
可以看出转换后的 JSON 文档中 first 和 last 的关联丢失了,如果尝试搜索 first 为 wu,last 为 xy 的文档,那么成功会检索出上述文档,但是 wu 和 xy 在原 JSON 文档中并不属于同一个 JSON 对象,应当是不匹配的,即检索不出任何结果。
嵌套类型就是为了解决这种问题的,嵌套类型将数组中的每个 JSON 对象作为独立的隐藏文档来存储,每个嵌套的对象都能够独立地被搜索,所以上述案例中虽然表面上只有 1 个文档,但实际上是存储了 4 个文档。
可以去看文章: Elasticsearch:Join 数据类型
在 Elasticsearch 中,Join 可以让我们创建 parent/child 关系。Elasticsearch 不是一个 RDMS。通常 join 数据类型尽量不要使用,除非不得已
例子
PUT my_index
{
"mappings": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"question": "answer"
}
}
}
}
}
在这里我们定义了一个叫做 my_index 的索引。在这个索引中,我们定义了一个 field,它的名字是 my_join_field。它的类型是 join 数据类型。同时我们定义了单个关系:question 是 answer 的 parent。
要使用 join 来 index 文档,必须在 source 中提供关系的 name 和文档的可选 parent。例如,以下示例在 question 上下文中创建两个 parent 文档
PUT my_index/_doc/1?refresh
{
"text": "This is a question",
"my_join_field": {
"name": "question"
}
}
PUT my_index/_doc/2?refresh
{
"text": "This is another question",
"my_join_field": {
"name": "question"
}
}
这里采用 refresh 来强制进行索引,以便接下来的搜索。在这里 name 标识 question,说明这个文档时一个 question 文档。
索引 parent 文档时,您可以选择仅将关系的名称指定为快捷方式,而不是将其封装在普通对象表示法中:
PUT my_index/_doc/1?refresh
{
"text": "This is a question",
"my_join_field": "question"
}
PUT my_index/_doc/2?refresh
{
"text": "This is another question",
"my_join_field": "question"
}
例如,以下示例显示如何索引两个 child 文档:
PUT my_index/_doc/3?routing=1?refresh (1)
{
"text": "This is an answer",
"my_join_field": {
"name": "answer", (2)
"parent": "1" (3)
}
}
PUT my_index/_doc/4?routing=1?refresh
{
"text": "This is another answer",
"my_join_field": {
"name": "answer",
"parent": "1"
}
}
在上面的(1)处,我们必须使用 routing,这样能确保 parent 和 child 是在同一个 shard 里。我们这里 routing 为 1,这是因为 parent 的 id 为 1,在(3)处定义。(2) 处定义了该文档 join 的名称。
在 Elasticsearch 中有一种数据类型叫做 range 的数据类型。它目前支持的类型如下:
类型 | 说明 |
---|---|
integer_range | 一个带符号的 32 位整数范围,最小值为 -2 的 31 次方,最大值为 2 的 31 次方减 1 |
float_range | 一系列单精度 32 位 IEEE 754 浮点值 |
long_range | 一系列带符号的 64 位整数,最小值为 -2 的 63 次方,最大值为 2 的 63 次方 -1 |
double_range | 一系列双精度 64 位 IEEE 754 浮点值。 |
date_range | 自系 EPOCH 以来经过的一系列日期值,表示为无符号的 64 位整数毫秒。 |
ip_range | 支持 IPv4 或 IPv6(或混合)地址的一系列 ip 值。 |
示例
PUT range_index
{
"settings": {
"number_of_shards": 2
},
"mappings": {
"properties": {
"expected_attendees": {
"type": "integer_range"
},
"time_frame": {
"type": "date_range",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
插入文档
PUT range_index/_doc/1?refresh
{
"expected_attendees" : {
"gte" : 10,
"lte" : 20
},
"time_frame" : {
"gte" : "2015-10-31 12:00:00",
"lte" : "2015-11-01"
}
}
gte
是开始,lte
是结束,其实就是大于号小于号
查询
GET range_index/_search
{
"query": {
"term": {
"expected_attendees": {
"value": "10"
}
}
}
}
因为 10 刚好是在我们之前的文档定义的 10-20 区间,所以可以查询到我们刚刚插入的文档