一、AggregationBuilder
RestHighLevelClient中分组需要用到AggregationBuilder作为分组条件的构建。我们可以看到AggregationBuilder接口有很多实现类,比如TermsAggregationBuilder、AvgAggregationBuilder、MaxAggregationBuilder、SumAggregationBuilder等常用的统计函数。
二、样例代码
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 以batchId为分组条件,terms为分组后的字段名称,field为将被分组的字段名称
TermsAggregationBuilder aggregation = AggregationBuilders.terms("batchId").field("batchId.keyword")
// 分组求和integral字段,并将求和后的字段名改为score
// subAggregation为子聚合,即在batchId分组后的小组内聚合
.subAggregation(AggregationBuilders.sum("score").field("integral"))
.subAggregation(AggregationBuilders.topHits("details").size(1));
sourceBuilder.aggregation(aggregation);
BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
sourceBuilder.query(boolBuilder);
SearchRequest searchRequest = new SearchRequest("table");
searchRequest.source(sourceBuilder);
SearchResponse search = client.search(searchRequest);
// 和之前不同的是这里需要getAggregations获取聚合后的数据
Aggregations aggregations = search.getAggregations();
// 从分组后的数据中拿到batchId的数据,这里以batchId分组,则分组后的数据都在batchId里
ParsedStringTerms terms = aggregations.get("batchId");
// 获取到分组后的所有bucket
List<? extends Terms.Bucket> buckets = terms.getBuckets();
for (Terms.Bucket bucket : buckets) {
// 解析bucket 因为一级聚合为以batchId分组,二级聚合为求和,所以这里还需要getAggregations获取求和的数据
Aggregations bucketAggregations = bucket.getAggregations();
// 这里我是通过debug才找到返回的参数类型的,我不知道在哪找得到这个东西,所以我们拿到了ParsedTopHits,这里我们是取了一个,所以这个值的数组长度为1
ParsedTopHits topHits = bucketAggregations.get("details");
// 因为求和和下面的topHits都是AggregationBuilders.terms("batchId").field("batchId.keyword")的subAggreation,所以都属于batchId组内
// 获取到求和的数据信息
ParsedSum sum = bucketAggregations.get("integral");
// 因为topHits中命中的hits肯定至少有一个,要不然也不会成组,所以这里直接获取第一个,并解析成map
Map<String, Object> sourceAsMap = topHits.getHits().getHits()[0].getSourceAsMap();
// 将求和后的integral覆盖到原数据中
sourceAsMap.put("integral", sum.value());
// 打印出统计后的数据
System.out.println(sourceAsMap);
}
三、说明
3.1 subAggregation
为什么要用subAggregation(AggregationBuilders.topHits("details").size(1))
这里有个坑,如果只用batchId分组并用intrgal求和,那么返回值中只会返回batchId与integral求和后的值这两个字段。这里举个例子,现在统计全国所有学校的总人数,那么我们以school分组,求和了人数。但是我们仍然想知道每个学校属于哪个地区的,这里聚合后就不会返回这些信息,让人很难受。
此时我们就可以使用subAggregation(AggregationBuilders.topHits("details").size(1))
来获取一个通用的数据,拿到学校所属的地区名称或者其他学校的上级属性。
3.2 AggregationBuilders层级
我们注意到subAggregation方法是AggregationBuilder的,所以在构建AggregationBuilders的时候,每一个子AggregationBuilder都可以有子AggregationBuilder,如下
TermsAggregationBuilder aggregation = AggregationBuilders.terms("batchId").field("batchId.keyword")
.subAggregation(
AggregationBuilders.terms("folder").field("folder")
.subAggregation(AggregationBuilders.sum("integral").field("integral"))
);
这种就是组内再聚合套组内,但是得注意字段的问题,即若你以batchId分组后,那么组内只有batchId与其他的字段,就和第一个注意点一样,导致组内聚合拿不到相关的字段,导致报错。