Elasticsearch 参考指南(日期直方图聚合)

csmnjk 2019-06-28

日期直方图聚合

类似于直方图的多桶聚合,但它只能应用于日期值,由于在Elasticsearch内部将日期表示为long值,因此也可以在日期上使用普通的histogram,但准确性会受到影响,原因是基于时间的间隔不是固定的(想想闰年和一个月的天数),因此,我们需要对基于时间的数据提供特殊支持。从功能的角度来看,这个直方图支持与普通直方图相同的特性,主要的区别是间隔可以由日期/时间表达式指定。

请求桶间隔一个月。

POST /sales/_search?size=0
{
    "aggs" : {
        "sales_over_time" : {
            "date_histogram" : {
                "field" : "date",
                "interval" : "month"
            }
        }
    }
}

时间间隔可用的表达式:year1y)、quarter1q)、month1M)、week1w)、day1d)、hour1h)、minute1M)、second1s)。

时间值也可以通过时间单位解析所支持的缩写来指定,注意,不支持小数时间值,但是你可以通过转移到另一个时间单位来解决这个问题(例如,1.5h可以被指定为90m),还要注意,大于天的时间间隔不支持任意值,但只能是一个单位大(例如,1y是有效的,2y不是)。

POST /sales/_search?size=0
{
    "aggs" : {
        "sales_over_time" : {
            "date_histogram" : {
                "field" : "date",
                "interval" : "90m"
            }
        }
    }
}

在内部,日期被表示为64位数字,表示从纪元开始算起的时间戳,这些时间戳作为桶的key返回,key_as_string是与使用format参数指定的格式转换成格式化日期字符串相同的时间戳:

如果没有指定format,那么它将使用字段映射中指定的第一个日期格式。
POST /sales/_search?size=0
{
    "aggs" : {
        "sales_over_time" : {
            "date_histogram" : {
                "field" : "date",
                "interval" : "1M",
                "format" : "yyyy-MM-dd" 
            }
        }
    }
}

"format" : "yyyy-MM-dd" => 支持表达日期格式模式

响应:

{
    ...
    "aggregations": {
        "sales_over_time": {
            "buckets": [
                {
                    "key_as_string": "2015-01-01",
                    "key": 1420070400000,
                    "doc_count": 3
                },
                {
                    "key_as_string": "2015-02-01",
                    "key": 1422748800000,
                    "doc_count": 2
                },
                {
                    "key_as_string": "2015-03-01",
                    "key": 1425168000000,
                    "doc_count": 2
                }
            ]
        }
    }
}

时区

在Elasticsearch中日期时间被存储在UTC,默认情况下,所有桶和四舍五入都是在UTC中完成的,可以使用time_zone参数指示桶使用不同的时区。

时区可以指定为ISO 8601 UTC偏移量(例如+01:00-08:00),也可以指定为时区id,这是TZ数据库(如America/Los_Angeles)中使用的标识符。

考虑下面的示例:

PUT my_index/_doc/1?refresh
{
  "date": "2015-10-01T00:30:00Z"
}

PUT my_index/_doc/2?refresh
{
  "date": "2015-10-01T01:30:00Z"
}

GET my_index/_search?size=0
{
  "aggs": {
    "by_day": {
      "date_histogram": {
        "field":     "date",
        "interval":  "day"
      }
    }
  }
}

如果没有指定时区,则使用UTC,这将导致将这两个文档放入同一天的桶中,该桶从2015年10月1日午夜UTC开始:

{
  ...
  "aggregations": {
    "by_day": {
      "buckets": [
        {
          "key_as_string": "2015-10-01T00:00:00.000Z",
          "key":           1443657600000,
          "doc_count":     2
        }
      ]
    }
  }
}

如果指定了-01:00time_zone,那么午夜从UTC午夜前一小时开始:

GET my_index/_search?size=0
{
  "aggs": {
    "by_day": {
      "date_histogram": {
        "field":     "date",
        "interval":  "day",
        "time_zone": "-01:00"
      }
    }
  }
}

现在第一个文档落入2015年9月30日的桶,第二个文档落入2015年10月1日的桶:

{
  ...
  "aggregations": {
    "by_day": {
      "buckets": [
        {
          "key_as_string": "2015-09-30T00:00:00.000-01:00", 
          "key": 1443574800000,
          "doc_count": 1
        },
        {
          "key_as_string": "2015-10-01T00:00:00.000-01:00", 
          "key": 1443661200000,
          "doc_count": 1
        }
      ]
    }
  }
}

key_as_string值表示在指定时区中每天的午夜。

当使用遵循DST(夏令时)更改的时区时,与这些更改发生的时刻接近的桶的大小可能与使用interval预期的大小略有不同。例如,考虑在CET时区启动DST:2016年3月27日凌晨2点,时钟拨快1小时至当地时间凌晨3点,当使用day作为interval时,覆盖当天的桶将只保存23个小时的数据,而其他桶通常是24小时。对于较短的间隔,如12h,也是如此,在这里,当DST转变发生时,我们在3月27日早上只有一个11小时的桶。

偏移量

offset参数用于通过指定的正(+)或负偏移(-)的持续时间来更改每个桶的起始值,例如1h为1小时或1d为一天,有关更多可能的持续时间选项,请参阅时间单位。

例如,当使用day的间隔时,每个桶从午夜运行到午夜,将offset参数设置为+6h将更改每个桶从早上6点运行到早上6点:

PUT my_index/_doc/1?refresh
{
  "date": "2015-10-01T05:30:00Z"
}

PUT my_index/_doc/2?refresh
{
  "date": "2015-10-01T06:30:00Z"
}

GET my_index/_search?size=0
{
  "aggs": {
    "by_day": {
      "date_histogram": {
        "field":     "date",
        "interval":  "day",
        "offset":    "+6h"
      }
    }
  }
}

上面的请求将文档分组为从早上6点开始的桶,而不是从午夜开始的单个桶:

{
  ...
  "aggregations": {
    "by_day": {
      "buckets": [
        {
          "key_as_string": "2015-09-30T06:00:00.000Z",
          "key": 1443592800000,
          "doc_count": 1
        },
        {
          "key_as_string": "2015-10-01T06:00:00.000Z",
          "key": 1443679200000,
          "doc_count": 1
        }
      ]
    }
  }
}
在进行time_zone调整之后,计算每个桶的起始offset

keyed响应

keyed标志设置为true将把唯一的字符串键与每个桶关联起来,并以hash而不是数组的形式返回范围:

POST /sales/_search?size=0
{
    "aggs" : {
        "sales_over_time" : {
            "date_histogram" : {
                "field" : "date",
                "interval" : "1M",
                "format" : "yyyy-MM-dd",
                "keyed": true
            }
        }
    }
}

响应:

{
    ...
    "aggregations": {
        "sales_over_time": {
            "buckets": {
                "2015-01-01": {
                    "key_as_string": "2015-01-01",
                    "key": 1420070400000,
                    "doc_count": 3
                },
                "2015-02-01": {
                    "key_as_string": "2015-02-01",
                    "key": 1422748800000,
                    "doc_count": 2
                },
                "2015-03-01": {
                    "key_as_string": "2015-03-01",
                    "key": 1425168000000,
                    "doc_count": 2
                }
            }
        }
    }
}

脚本

与普通直方图一样,支持文档级别脚本和值级别脚本,还可以使用order设置控制返回的桶的顺序,并基于min_doc_count设置过滤返回的桶(默认情况下,将返回匹配文档的第一个桶和最后一个桶之间的所有桶)。这个直方图还支持extended_bounds设置,它允许扩展直方图的界限超过数据本身(关于为什么要这样做的更多信息,请参阅此处的解释)。

缺失值

missing参数定义了如何处理缺失值的文档,默认情况下,它们将被忽略,但也可以将它们视为有值来处理。

POST /sales/_search?size=0
{
    "aggs" : {
        "sale_date" : {
             "date_histogram" : {
                 "field" : "date",
                 "interval": "year",
                 "missing": "2000/01/01" 
             }
         }
    }
}

publish_date字段中没有值的文档将落入具有值为2000-01-01的文档的同一个桶。

顺序

默认情况下,返回的桶按key升序排序,不过可以使用order设置控制顺序行为,支持与Terms聚合相同的order功能。

在6.0.0中已弃用

使用_key而不是_time来根据它们的日期/键来排序桶。

使用脚本通过星期几聚合

在某些情况下,日期直方图不能帮助我们,例如,当我们需要通过星期几聚合结果时,在这种情况下,为了克服这个问题,我们可以使用一个返回星期几的脚本:

POST /sales/_search?size=0
{
    "aggs": {
        "dayOfWeek": {
            "terms": {
                "script": {
                    "lang": "painless",
                    "source": "doc['date'].value.dayOfWeek"
                }
            }
        }
    }
}

响应:

{
  ...
  "aggregations": {
    "dayOfWeek": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "7",
          "doc_count": 4
        },
        {
          "key": "4",
          "doc_count": 3
        }
      ]
    }
  }
}

响应将包含以星期为键的所有桶:1为星期一,2为星期二...7为星期日。

相关推荐