zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

ElasticSearch DateHistogram

2023-06-13 09:15:30 时间

es日期直方图统计

需求

需要实现以下SQL产生的效果

select date_format(create_time, '%Y-%m-%d') as date,
       count(class_id)                      as num
from news_info where create_time between #{start_time} 
and #{end_time}
group by date;

SQL执行结果示意图1

不带日期范围搜索条件

select date_format(create_time, '%y-%m-%d') as date,
       count(class_id)                      as num
from news_info
group by date;

SQL执行结果示意图2

带日期范围搜索条件

select date_format(create_time, '%y-%m-%d') as date,
       count(class_id)                      as num
from news_info
where create_time between '2021-08-25' and '2021-09-15'
group by date;

RestHighLevelClient API

导入依赖

跟随SpringBoot依赖版本,<spring-boot.version>2.6.4</spring-boot.version>

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
   </dependency>

demo代码

public void testAggs() throws IOException {
    //构建搜索请求
    SearchRequest searchRequest = new SearchRequest("news_info");
    //构建日期直方图聚合生成器
    DateHistogramAggregationBuilder dateHistogramAggregationBuilder =
            AggregationBuilders
                    //自定义名称
                    .dateHistogram("1天间隔")
                    //分组字段
                    .field("create_time")
                    //日期格式
                    .format(EsConstants.YYYY_MM_DD);
    
    //dateHistogramInterval(),这个方法已经过时,推荐使用dateHistogramAggregationBuilder.fixedInterval()
    dateHistogramAggregationBuilder
            //间隔:1天
            .fixedInterval(DateHistogramInterval.days(1))
            //最小返回0
            .minDocCount(0);

    //构造搜索源生成器,配置聚合生成器
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().aggregation(dateHistogramAggregationBuilder);
    //配置搜索源生成器
    searchRequest.source(sourceBuilder);

    //使用RestHighLevelClient搜索
    SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);

    //根据自定义名称获取直方图
    Histogram histogram = search.getAggregations().get("1天间隔");
    //遍历获取数据
    List<? extends Histogram.Bucket> buckets = histogram.getBuckets();
    for (Histogram.Bucket bucket : buckets) {
        System.out.println("date:" + bucket.getKeyAsString() + " ---> " + " num:" + bucket.getDocCount());
    }
}

结果展示

[示例图]

… 中间省略(输出内容太多,只截取开头与结尾)

产生以下问题

使用demo,的确是可以查询出所有的分组数据,不存在的日期也可以补0输出,但是我想自己控制查询日期的范围,好像无法做到。 于是,百度 + 查询官网,发现有个dateHistogramAggregationBuilder.extendedBounds()方法可以设置查询日期的范围。

单独使用extendedBounds

dateHistogramAggregationBuilder
                //间隔:1天
                .fixedInterval(DateHistogramInterval.days(1))
                //最小返回0
                .minDocCount(0)
                .timeZone(ZoneId.systemDefault())
                //扩展边界
                .extendedBounds(new LongBounds("2021-08-25","2021-09-15"))
        ;

结果展示

前部分:

后部分: 与[示例图2]一样,一直输出到2022-05-17。

结论

按照date分组查询统计对应的数值

查询从开始日期开始~数据库中最后一个分组有值的日期

输出到最后一个分组统计(num)有值的日期截止。

单独使用hardBounds

dateHistogramAggregationBuilder
                //间隔:1天
                .fixedInterval(DateHistogramInterval.days(1))
                //最小返回0
                .minDocCount(0)
                .timeZone(ZoneId.systemDefault())
                //硬边界
                .hardBounds(new LongBounds("2021-08-25","2021-09-15"))
        ;

结果展示

结论

按照date分组查询统计对应的数值

查询从开始日期~截止日期的范围

输出该范围内最后一个分组统计(num)有值的日期截止。

问题解决

hardBounds + extendedBounds 一起使用

public void testAggs() throws IOException {
        //构建搜索请求
        SearchRequest searchRequest = new SearchRequest("news_info");
        //构建日期直方图聚合生成器
        DateHistogramAggregationBuilder dateHistogramAggregationBuilder =
                AggregationBuilders
                        //自定义名称
                        .dateHistogram("1天间隔")
                        //分组字段
                        .field("create_time")
                        //日期格式
                        .format(EsConstants.YYYY_MM_DD);
        
        //dateHistogramInterval(),这个方法已经过时,推荐使用dateHistogramAggregationBuilder.fixedInterval()
        dateHistogramAggregationBuilder
                //间隔:1天
                .fixedInterval(DateHistogramInterval.days(1))
                //最小返回0
                .minDocCount(0)
                //时区
                .timeZone(ZoneId.systemDefault())
                //硬边界
                .hardBounds(new LongBounds("2021-08-25","2021-09-15"))
                //扩展边界
                .extendedBounds(new LongBounds("2021-08-25","2021-09-15"));

        //构造搜索源生成器,配置聚合生成器
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().aggregation(dateHistogramAggregationBuilder);
        //配置搜索源生成器
        searchRequest.source(sourceBuilder);

        //使用RestHighLevelClient搜索
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);

        //根据自定义名称获取直方图
        Histogram histogram = search.getAggregations().get("1天间隔");
        //遍历获取数据
        List<? extends Histogram.Bucket> buckets = histogram.getBuckets();
        for (Histogram.Bucket bucket : buckets) {
            System.out.println("date:" + bucket.getKeyAsString() + " ---> " + " num:" + bucket.getDocCount());
        }
    }

结果展示

结论

按照date分组查询统计对应的数值

**查询从开始日期~**截止日期的范围

输出该范围内所有日期,无值的补0。


额外话:其实直接获取全部的数据也是可以做到需求所要的效果:反正es查询性能很强大,可以直接查询全部数据,然后通过Java8的Stream的filter来进行日期范围条件过滤。

警告⚠️

由于昨天的测试数据有问题,导致描述不准确,以下是今天的测试结果:

首先一点:扩展边界必须在硬边界内(两个边界的startTimeendTime可以相同,如果设置不同,那么扩展边界的startTimeendTime,必须在硬边界的startTime~endTime范围之间)

测试:扩展边界处于硬边界范围内

测试1:扩展边界的startTime,endTime与硬边界保持一致

dateHistogramAggregationBuilder
                //硬边界
                  .hardBounds(new LongBounds("2022-08-07", "2022-08-10"))
                //扩展边界 
                .extendedBounds(new LongBounds("2022-08-07", "2022-08-10"))

结果1

测试2:扩展边界的startTime 大于硬边界,endTime小于等于硬边界

dateHistogramAggregationBuilder
                //硬边界
                  .hardBounds(new LongBounds("2022-08-07", "2022-08-10"))
                //扩展边界 
                .extendedBounds(new LongBounds("2022-08-08", "2022-08-10"))

结果2(发现与结果1一致)

测试3:扩展边界的startTime 大于等于硬边界,endTime小于硬边界

dateHistogramAggregationBuilder
                //硬边界
                  .hardBounds(new LongBounds("2022-08-07", "2022-08-10"))
                //扩展边界 
                .extendedBounds(new LongBounds("2022-08-08", "2022-08-09"))

结果3

不论endTime = 2022-08-09,2022-08-08,2022-08-07,结果3都是一样的。

测试:扩展边界处于硬边界范围外

报错:重点就是**“reason”:“Extended bounds have to be inside hard bounds”**

{“error”:{“root_cause”:[{“type”:“illegal_argument_exception”,“reason”:“Extended bounds have to be inside hard bounds, hard bounds: [2022-08-08–2022-08-10], extended bounds: [2022-08-07–2022-08-09]”}],“type”:“search_phase_execution_exception”,“reason”:“all shards failed”,“phase”:“query”,“grouped”:true,“failed_shards”:[{“shard”:0,“index”:“im_message”,“node”:“_13QIC8wRO2prdBPFLE0DA”,“reason”:{“type”:“illegal_argument_exception”,“reason”:“Extended bounds have to be inside hard bounds, hard bounds: [2022-08-08–2022-08-10], extended bounds: [2022-08-07–2022-08-09]”}}],“caused_by”:{“type”:“illegal_argument_exception”,“reason”:“Extended bounds have to be inside hard bounds, hard bounds: [2022-08-08–2022-08-10], extended bounds: [2022-08-07–2022-08-09]”,“caused_by”:{“type”:“illegal_argument_exception”,“reason”:“Extended bounds have to be inside hard bounds, hard bounds: [2022-08-08–2022-08-10], extended bounds: [2022-08-07–2022-08-09]”}}},“status”:400}

结论

感谢IT界的eason哥帮我找到了官网链接:(我其实找到过这个地方,但是细心看所以错过了,害,下次注意) 直方图文档介绍