Elasticsearch 除了精确值查询外,还支持 range query,即范围查询,它们的查询介于一定范围之内的值,适用于数字、日期及字符串类型。需要注意的是,使用 range 查询只能查询一个字段,不能作用在多个字段上。
ES 范围查询 API
es 的 range 查询作用类似与 SQL 中的范围查询,SQL 中范围查询可以表示为如下:
SELECT document
FROM product
WHERE price BETWEEN 188 AND 699
上述示例在 es 中表示为如下:
{
"query": {
"range": {
"price": {
"gte": 188,
"lte": 699
}
}
}
}
Elasticsearch 范围查询(range query)支持的参数有以下几种:
gt
- 大于(greater than),即
>
,查询范围的最小值,也就是下界,但是不包含临界值。 gte
- 大于或等于(greater than or equal to),即
>=
,查询范围的最小值,也就是下界,但和gt
的区别在于包含临界值。 lt
- 小于(less than),即
<
,查询范围的最大值,也就是上界,但是不包含临界值。 lte
- 小于或等于(less than or equal to),即
<=
,查询范围的最大值,也就是上界,但和lt
的区别在于包含临界值。
数字范围
在 es 绝大多数范围查询的场景中都是针对数字型字段的 range query,例如,想要查询价格大于 58,小于等于 88 的书籍,即 58 < price <= 88,构造查询语句如下:
GET bookes/_search
{
"query": {
"range": {
"price": {
"gt": 58,
"lte": 88
}
}
}
}
如果想要范围无界(比如,价格只需满足 >58 即可),只须省略其中一边的限制:
GET bookes/_search
{
"query": {
"range": {
"price": {
"gt": 58
}
}
}
}
日期范围
范围查询除了应用在数字型字段上,同样可以应用在日期型字段上,也是比较常用。
查询出版日期在 2015 年 1 月 1 日和 2019 年 12 月 31 之间的书籍,对 publish_time 字段进行范围查询,命令如下:
{
"query": {
"range": {
"publish_time": {
"gte": "2015-01-01 00:00:00",
"lte": "2019-12-31 23:59;59",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
此外,range 查询支持对日期字段进行计算操作,比方说,如果我们想查找时间戳在过去三小时内的所有文档:
{
"query": {
"range": {
"timestamp": {
"gt" : "now-1h"
}
}
}
}
日期计算还可以被应用到某个具体的时间,并非只能是一个像 now 这样的占位符。只要在某个日期后加上一个双管符号(||
)并紧跟一个日期数学表达式就能做到:
{
"query": {
"range": {
"timestamp": {
"gt": "2020-01-01 00:00:00",
"lt": "2020-01-01 00:00:00||+1M"
}
}
}
}
上述示例表示要查询时间戳从 2020 年 1 月 1 日到,加 1 月后,即 2020 年 2 月 1 日 零时之间的数据。
字符串范围
range 查询也可以应用在字符串类型的字段上,字符串范围可采用字典顺序(lexicographically)或字母顺序(alphabetically)。
在倒排索引中的词项就是采取字典顺序(lexicographically)排列的,这也是字符串范围可以使用这个顺序来确定的原因。
如果我们想查找从 a 到 b (不包含)的字符串,同样可以使用 range 查询语法:
{
"query": {
"range": {
"title": {
"gte": "a",
"lt": "b"
}
}
}
}
值得注意的是,字符串类型的范围查询会比数字型或日期型上字段慢很多,因为 Elasticsearch 实际上是在为范围内的每个词项都执行 term 范围过滤计算。字符串范围在唯一值比较少(low cardinality)的字段上可以快速工作,但是唯一词项越多,字符串范围的计算会越慢。