Elasticsearch 基础教程

Elasticsearch 高级教程

Elasticsearch 插件

Elasticsearch 笔记

Elasticsearch(es)范围查询及 Lucene 底层原理实现

Elasticsearch(es) 查询语句语法详解 Elasticsearch(es) 查询语句语法详解


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)的字段上可以快速工作,但是唯一词项越多,字符串范围的计算会越慢。