Elasticsearch聚合查询详解(附带实例)
在实际项目中,Elasticsearch 用得多的还是聚合查询、分组查询以及结果高亮显示。
在解释什么是聚合之前,首先需要了解两个重要概念:
概括起来,聚合就是由一个或者多个桶、一个或者多个指标组合而成的数据查询统计功能。一个聚合查询可以只有一个桶或者一个指标,或者每样一个。在桶中甚至可以有多个嵌套的桶。比如,我们可以将文档按照其所属国家/地区进行分桶,再按照年龄分桶,最后对每个桶计算其平均薪资(一个指标)。
具体使用流程如下:
下面通过示例演示 AggregationBuilders 的数据统计功能:

图 1 Aggregation 统计查询的运行结果
通过输出发现,category 字段匹配的关键字被加上高亮的样式。我们可以通过 getHighlightField 获取并处理高亮显示的字段。
例如,我们可以按照类别(Category)对所有的书进行分桶,然后对每个桶计算其平均价格(一个指标)。
下面通过示例验证 AggregationBuilders 的分组聚合统计功能。

图 2 Aggregation 聚合查询的运行结果
通过输出发现,查询结果按照 category 字段分组聚合,然后统计每一组的数量。其实与数据库的分组比较类似,都是通过某个字段分组,然后进行统计。
上面是简单计算每个分组的数量,我们也可以先聚合,再计算每个 Buckets 的最大值、最小值等统计数据。对上面的示例做一些修改:
单击 Run Test 或在方法上右击,选择 Run 'testAggregationQuery3',运行单元测试方法,验证聚合统计的功能,运行结果如下图所示:

图 3 Aggregation聚合统计查询的运行结果
通过输出发现,查询结果按照 category 字段分组聚合,然后统计每个分组价格的最大值、最小值和平均值。
什么是聚合
聚合(Aggregation)是 Elasticsearch 非常重要的功能,它允许用户在数据上生成复杂的分析统计。它很像 SQL 中的 GROUP BY,但是比 GROUP BY 的功能更强大。在解释什么是聚合之前,首先需要了解两个重要概念:
- Buckets(桶):满足某个条件的文档集合。我们可以将文档数据按照一个或多个条件进行划分,比如按小时、按年龄区间、按地理位置等。当然,还有更加复杂的聚合,比如先将文档按天进行分桶,再将每天的桶按小时分桶。
- Metrics(指标):对某个桶中的文档计算得到的统计信息,比如使用 min、mean、max、sum 等计算得到的统计信息。
概括起来,聚合就是由一个或者多个桶、一个或者多个指标组合而成的数据查询统计功能。一个聚合查询可以只有一个桶或者一个指标,或者每样一个。在桶中甚至可以有多个嵌套的桶。比如,我们可以将文档按照其所属国家/地区进行分桶,再按照年龄分桶,最后对每个桶计算其平均薪资(一个指标)。
Elasticsearch实现统计查询
Elasticsearch 提供了 AggregationBuilders 类用于统计信息,比如最小值(min)、最大值(max)、总和(sum)、数量(count)、平均值(vag)等。具体使用流程如下:
- 使用 QueryBuilders 创建数据查询条件。
- 使用 AggregationBuilders 创建统计指标。
- 构建 NativeSearchQuery,合并数据查询条件和统计指标。
- 执行 search 并输出统计结果。
下面通过示例演示 AggregationBuilders 的数据统计功能:
@Test public void testAggregationQuery() { QueryBuilder queryBuilder= QueryBuilders.boolQuery() .must(QueryBuilders.rangeQuery("price").gte(50)); // 1. 数量 ValueCountAggregationBuilder countAggregationBuilder= AggregationBuilders.count("count_id").field("id"); // 2. 求和 SumAggregationBuilder sumAggregationBuilder= AggregationBuilders.sum("sum_price").field("price"); // 3. 求最小值 MinAggregationBuilder minAggregationBuilder=AggregationBuilders.min("min_price").field("price"); // 4. 求最大值 MaxAggregationBuilder maxAggregationBuilder=AggregationBuilders.max("max_price").field("price"); // 5. 求平均值 AvgAggregationBuilder avgAggregationBuilder=AggregationBuilders.avg("avg_price").field("price"); // 全部统计指标 StatsAggregationBuilder statsAggregationBuilder=AggregationBuilders.stats("stats_price").field("price"); NativeSearchQuery nativeSearchQuery=new NativeSearchQueryBuilder() .withQuery(queryBuilder) .addAggregation(countAggregationBuilder) .addAggregation(sumAggregationBuilder) .addAggregation(minAggregationBuilder) .addAggregation(maxAggregationBuilder) .addAggregation(avgAggregationBuilder) .addAggregation(statsAggregationBuilder).build(); SearchHits<Book> bookSearchHits=elasticsearchRestTemplate.search(nativeSearchQuery,Book.class,IndexCoordinates.of(Book_INDEX)); Map<String, Aggregation> aggregationMap = bookSearchHits.getAggregations().getAsMap(); ValueCount count = (ValueCount)aggregationMap.get("count_id"); Avg avg = (Avg)aggregationMap.get("avg_price"); Sum sum=(Sum) aggregationMap.get("sum_price"); Min min=(Min) aggregationMap.get("min_price"); Max max=(Max) aggregationMap.get("max_price"); Stats stats=(Stats) aggregationMap.get("stats_price"); System.out.println("count:"+count.getValue()); System.out.println("sum:"+sum.getValue()); System.out.println("min:"+min.getValue()); System.out.println("max:"+max.getValue()); System.out.println("avg:"+avg.getValue()); System.out.println("sum:"+stats.getSumAsString()+" "+ "min:"+stats.getMinAsString()+" "+ "max:"+stats.getMaxAsString()+" "+ "avg:"+stats.getAvgAsString()+" "+ "count:"+stats.getCount()); };单击 Run Test 或在方法上右击,选择 Run 'testAggregationQuery',运行单元测试方法,验证 StringQuery 查询的效果,运行结果如下图所示:

图 1 Aggregation 统计查询的运行结果
通过输出发现,category 字段匹配的关键字被加上高亮的样式。我们可以通过 getHighlightField 获取并处理高亮显示的字段。
Elasticsearch实现聚合查询
聚合是一些桶和指标的组合。一个聚合可以只有一个桶或者一个指标,或者每样一个。在桶中甚至可以有多个嵌套的桶。例如,我们可以按照类别(Category)对所有的书进行分桶,然后对每个桶计算其平均价格(一个指标)。
下面通过示例验证 AggregationBuilders 的分组聚合统计功能。
@Test public void testAggregationQuery2() { Query query = new NativeSearchQueryBuilder() .addAggregation(AggregationBuilders.terms("category").field("category.keyword")) .build(); SearchHits<Book> searchHits = elasticsearchRestTemplate.search(query, Book.class); searchHits.getAggregations().asList().forEach(aggregation -> { ((Terms) aggregation).getBuckets() .forEach(bucket -> System.out.println("分组:"+bucket.getKeyAsString()+":"+ bucket.getDocCount())); }); };单击 Run Test 或在方法上右击,选择 Run 'testAggregationQuery2',运行单元测试方法,验证分组查询功能,运行结果如下图所示:

图 2 Aggregation 聚合查询的运行结果
通过输出发现,查询结果按照 category 字段分组聚合,然后统计每一组的数量。其实与数据库的分组比较类似,都是通过某个字段分组,然后进行统计。
上面是简单计算每个分组的数量,我们也可以先聚合,再计算每个 Buckets 的最大值、最小值等统计数据。对上面的示例做一些修改:
@Test public void testAggregationQuery3() { QueryBuilder queryBuilder= QueryBuilders.boolQuery() .must(QueryBuilders.rangeQuery("price").gte(5)); TermsAggregationBuilder aggregationBuilder=AggregationBuilders.terms("category").field("category.keyword"); AggregationBuilder minAggregation=AggregationBuilders.min("min_price").field("price"); AggregationBuilder maxAggregation=AggregationBuilders.max("max_price").field("price"); AggregationBuilder avgAggregation=AggregationBuilders.avg("avg_price").field("price"); aggregationBuilder.subAggregation(minAggregation) .subAggregation(maxAggregation) .subAggregation(avgAggregation); NativeSearchQuery nativeSearchQuery=new NativeSearchQueryBuilder().withQuery(queryBuilder) .addAggregation(aggregationBuilder).build(); SearchHits<Book> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Book.class); Map<String, Aggregation> aggregationMap = searchHits.getAggregations().getAsMap(); Aggregation categorys = aggregationMap.get("category"); for (Terms.Bucket bucket: ((Terms) categorys).getBuckets()){ Aggregations aggregations=bucket.getAggregations(); System.out.print("bucket:"+bucket.getKeyAsString()+" "); Min min= aggregations.get("min_price"); Max max= aggregations.get("max_price"); Avg avg= aggregations.get("avg_price"); System.out.println("min:"+min.getValue()+" "+"max:"+max.getValue()+" "+"avg:"+avg.getValue()); } };在上面的示例中,通过 AggregationBuilders 的 mix、max、avg 等方法构建聚合查询的数据统计信息。
单击 Run Test 或在方法上右击,选择 Run 'testAggregationQuery3',运行单元测试方法,验证聚合统计的功能,运行结果如下图所示:

图 3 Aggregation聚合统计查询的运行结果
通过输出发现,查询结果按照 category 字段分组聚合,然后统计每个分组价格的最大值、最小值和平均值。
相关文章
- Elasticsearch介绍(非常详细)
- Elasticsearch是什么(非常详细)
- Elasticsearch到底是什么(非常详细)
- Elasticsearch分页查询的2种实现方式
- Elasticsearch使用QueryBuilder查询(附带实例)
- 一文彻底了解Elasticsearch(新手必看)
- Spring Boot集成Elasticsearch(新手必看)
- Spring Boot操作Elasticsearch(增删改查,新手必看)
- ElasticsearchRestTemplate操作Elasticsearch(增删改查,新手必看)
- Python violinplot():绘制小提琴图(附带实例)