一、对日期字段的范围查询
在学习本文之前,请先参考【elasticsearch学习9–Term-level queries简介、term query与terms query】完成 blogs_index 索引的创建,同时批量导入如下数据:
POST _bulk
{"index":{"_index":"blogs_index","_type":"_doc","_id":"1"}}
{"id":1,"author":"大衣哥","title":"昨日博客","influence":{"gte":10,"lte":15},"createAt":"2020-05-24T10:56:23Z"}
{"index":{"_index":"blogs_index","_type":"_doc","_id":"2"}}
{"id":2,"author":"大衣哥","title":"今日博客","influence":{"gte":10,"lte":20},"createAt":"2020-05-25T10:56:23Z"}
{"index":{"_index":"blogs_index","_type":"_doc","_id":"3"}}
{"id":3,"author":"大衣哥","title":"一周内的博客","influence":{"gte":15,"lte":18},"createAt":"2020-05-18T10:56:23Z"}
{"index":{"_index":"blogs_index","_type":"_doc","_id":"4"}}
{"id":4,"author":"大衣哥","title":"一周外的博客","influence":{"gte":15,"lt":18},"createAt":"2020-05-17T10:56:23Z"}
{"index":{"_index":"blogs_index","_type":"_doc","_id":"5"}}
{"id":5,"author":"大衣哥","title":"上月博客","influence":{"gt":15,"lt":18},"createAt":"2020-04-26T10:56:23Z"}
gte 大于或等于
gt 大于
lte 小于或等于
lt 小于
1、Date Math 详解
Date Math 由两部分组成:
1)以固定的日期开头,这个固定的日期也可以是 now 或者是以“||”结尾的时间字符串;
例如:
2020-05-20 12:23:22
now
2020-05-20||(2020-05-20是字符串)
2)固定日期后面可接一个或多个数学表达式。
例如:
+1h:增加一小时
-1d:减少一天
/d:四舍五入到最近一天
支持的时间单位有:y-Years、M-Months、w-Weeks、d-Days、h-Hours、H-Hours、m-Minutes、s-Seconds
看几个Date Math 示例加以理解,比如当前时间:now = 2020-05-26 13:00:00
- now + 1h :now的毫秒值 + 1小时,结果为:2020-05-26 14:00:00
- now - 1h/d:now的毫秒值 - 1小时,再根据情况四舍五入到最近的一天的起始:2020-05-26 00:00:00 或者 结束:2020-05-26 23:59:59.999
- 2020-05-26||-1M/M:2020-05-26 的毫秒值 - 1个月,再根据情况四舍五入到最近的一月的起始:2020-04-01 00:00:00 或者 结束:2020-04-30 23:59:59.999
这里的根据情况就是下面要介绍的 date math to round。
2、date math to round
当使用date math 将日期四舍五入到最接近的日期、月份、小时等的时候,四舍五入的日期取决于范围的结尾是包含端点还是排除端点。
1)包含端点:按最大范围四舍五入
gte 大于或等于四舍五入的日期:2020-05-25||/M变为 2020-05-01,即包括整个月份。
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"gte" : "2020-05-25||/M"
}
}
}
}
结果分析:可以检索到文档1、2、3、4(检索结果过长,此处就不展示了,可自行验证)。等价于sql【where createAt >=“2020-05-01 00:00:00”】
lte 小于或等于四舍五入的日期:2020-05-25||/M变为2020-05-31T23:59:59.999,即包括整个月份。
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"lte" : "2020-05-01||/M"
}
}
}
}
结果分析:可以检索到所有文档(检索结果过长,此处就不展示了,可自行验证)。等价于sql【where createAt <=“2020-05-31 23:59:59.999”】
2)排除端点:按最小范围四舍五入
gt 大于四舍五入的日期:2020-05-01||/M 变为 2020-05-31T23:59:59.999,即排除整个月份。
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"gt" : "2020-05-01||/M"
}
}
}
}
结果分析:检索不到任何文档。等价于sql【where createAt > “2020-05-31T23:59:59.999”】
lt 少于四舍五入的日期:2020-05-31||/M变为2020-05-01,即排除整个月份。
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"lt" : "2020-05-31||/M"
}
}
}
}
结果分析:可以检索到文档5。等价于sql【where createAt < “2020-05-01 00:00:00”】
总结:
gte(大于等于)和lte(小于等于)会尽量包含,按最大范围四舍五入
gt(大于)和lt(小于)会尽量排除,按最小范围四舍五入
3、使用 Date Math 进行检索
ps:以下示例,基于now = 2020-05-25 23:00:00
1) 获取昨天发布的博客
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"gte" : "now-1d/d",
"lt" : "now/d"
}
}
}
}
结果分析:可以检索到文档1【“title”: “昨日博客”】,等价于sql【where createAt >= “2020-05-24 00:00:00” and createAt < “2020-05-25 00:00:00”】
2) 获取一周内发布的博客
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"gte" : "now-1w/d",
"lt" : "now/d"
}
}
}
}
结果分析:可以检索到文档1【“title”: “昨日博客”】和文档3【“title”: “一周内的博客”,】,等价于sql【where createAt >= “2020-05-18 00:00:00” and createAt < “2020-05-25 00:00:00”】
3) 获取本月内发布的博客
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"gte" : "now/M",
"lte" : "now/M"
}
}
}
}
结果分析:可以检索到文档1/2/3/4,等价于sql【where createAt >= “2020-05-01 00:00:00” and createAt < “2020-05-31 23:59:59.999”】
4、日期范围检索的其他研究
1)格式问题:format参数
对日期字段进行检索时,默认使用被检索字段的format,但是可以通过“format”参数覆盖
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"gte": "20/05/2020",
"lte": "2021",
"format": "dd/MM/yyyy||yyyy"
}
}
}
}
结果分析:可以检索到文档1和文档2。等价于sql【where createAt >= “2020-05-20T00:00:00Z” and createAt < “2021-01-01T00:00:00.000Z” 】
注意1:如果没有 “format”,上述DSL执行将报parse_exception 异常,因为date类型默认只支持3种格式,请参见【elasticsearch学习1–需要注意的数据格式】
注意2:如果日期缺少年,月和日的某些坐标,则丢失的部分将以unix time的开始(即1970年1月1日)填充 。
例如,当指定dd格式时,“gte” : 10 将使用翻译成1970-01-10T00:00:00.000Z。
2)时区问题:time_zone参数
将日期从另一个时区转换为UTC(世界协调时间)
POST /blogs_index/_search
{
"query": {
"range" : {
"createAt" : {
"gte": "2020-05-25T11:56:23",
"lte": "now",
"time_zone": "+02:00"
}
}
}
}
结果分析:“gte”: “2020-05-25T11:56:23”, 该日期将转换为2020-05-25T09:56:23 UTC。
所以可以检索到文档2(它的"createAt" :“2020-05-25T10:56:23Z”),如果没有 “time_zone”: “+02:00”,就检索不到任何文档。
now不受time_zone参数的影响,它始终是当前系统时间(以UTC为单位)。但是,在使用日期数学舍入时(例如,使用向下舍入到最近的日期now/d),time_zone将考虑提供的值。
二、 对类型为 range 字段的查询
relation参数控制这两个范围值【一个是文档field的value,一个是检索范围】如何匹配
先把文档influence字段的value列出来,方便大家对比数据:
文档1【“gte”:10,“lte”:15】
文档2【“gte”:10,“lte”:20】
文档3【“gte”:15,“lte”:18】
文档4【“gte”:15,“lt”:18】
文档5【“gt”:15,“lt”:18】
1、WITHIN:文档的范围字段要完全在检索关键词的范围里
POST /blogs_index/_search
{
"query" : {
"range" : {
"influence": {
"gte" : 12,
"lte" : 17,
"relation" : "within"
}
}
}
}
结果分析:可以检索到文档4和文档5,[15,18)和(15,18)完全在[12,17]范围里
2、CONTAINS:文档的范围字段完全包含检索关键词的范围
POST /blogs_index/_search
{
"query" : {
"range" : {
"influence": {
"gte" : 12,
"lte" : 17,
"relation" : "CONTAINS"
}
}
}
}
结果分析:可以检索到文档2,只有[10,20]完全包含[12,17]范围
3、INTERSECTS(默认值):文档的范围字段与检索关键词的范围有交集即可
POST /blogs_index/_search
{
"query" : {
"range" : {
"influence": {
"gte" : 12,
"lte" : 17,
"relation" : "CONTAINS"
}
}
}
}
结果分析:可以检索到所有文档,都有交集