背景
Elasticsearch 版本发布的很勤, API 客户端的用法各个版本之间差异也是很大。尤其是 Elasticsearch 8.x 版本直接废弃了 RestHighLevelClient 对象。 Query 和 Aggregation 的 Builder 的用法也有变化。
本文记录项目升级 Elasticsearch API 到 8.x 版本时聚合 API 遇到的问题及解决办法。8.x 的 client jar 包 elasticsearch-java,里面的包名称路径都变了。资料又少,耗了一周可算搞定了最麻烦的聚合问题。
Elasticsearch 8.x 版本API
多层级聚合的方法,几乎搜不到资料。最后是在CSDN 的 AI搜索中找到的:提问是:
Elastic8.x 的 API 中 subAggregation的等价方案是什么?
新的 Java 客户端使用了完全不同的构建方式,它更加类型安全。我们需要使用静态工厂方法来创建聚合。以下是给出的 terms 聚合嵌套 avg 聚合 的例子。
bash
在这里插入代码片// 构建聚合
Aggregation byCategory = Aggregation.of(a -> a
.terms(TermsAggregation.of(t -> t.field("category")))
.aggregations("avg_price", Aggregation.of(avg -> avg
.average(AverageAggregation.of(av -> av.field("price"))))
)
);
添加多个子聚合
bash
Aggregation termAgg = Aggregation.of(a -> {
Aggregation.Builder.ContainerBuilder containerBuilder = a.terms(TermsAggregation.of(t -> t.field(field)));
// 逐个添加子聚合到 Terms 聚合上
for (Aggregation child : children) {
// TODO 设置子聚合名称
String childAggName = "";
containerBuilder.aggregations(childAggName, child);
}
// 返回最终结果
return containerBuilder;
});
测试代码:
bash
TermsAggregationBuilder baseAgg = AggregationBuilders.terms("field1").name("group_field1");
baseAgg.subAggregation(AggregationBuilders.max("max_field2").field("field2"));
baseAgg.subAggregation(AggregationBuilders.min("min_field2").field("field2"));
Aggregation finalAgg = baseAgg.aggregation();
System.out.println(finalAgg);
对 字段1聚合,同时包含两个子聚合聚合字段2的最大值和最小值:
bash
{
"aggregations": {
"max_field2": {
"max": {
"field": "field2"
}
},
"min_field2": {
"min": {
"field": "field2"
}
}
},
"terms": {
"field": "field1"
}
}
请求体聚合
bash
// 8.x SearchRequest 构建方法使用 Builder
SearchRequest.Builder requestBuilder = new SearchRequest.Builder();
requestBuilder.index(Arrays.asList(indices));
requestBuilder.withJson(new StringReader(builder.toString()));
// 检查是否具有聚合属性并追加到请求体
List<AggregationBuilder> aggregations = builder.getAggregations();
if (aggregations != null) {
for(AggregationBuilder aggBuilder :aggregations) {
String aggName = aggBuilder.getName();
Aggregation aggValue = aggBuilder.aggregation();
requestBuilder.aggregations(aggName, aggValue);
}
}
这里的 AggregationBuilder 是为了适配高版本的 API,自定义的一个构建器,针对不同类型的聚合,对应等价创建出 8.x 版本的 Aggregation 对象,同时包含一个聚合名称参数:
bash
public class AggregationBuilder {
// 聚合名称 name
// 子聚合列表 children
// 当前聚合对象 aggregation
....
}
最终聚合 JSON 对象,请求 SearchRequest 对象中 N 个聚合的情况:
bash
{
"aggregations": {
"max_field2": {
"max": {
"field": "field2"
}
},
"min_field2": {
"min": {
"field": "field2"
}
}
},
}
启示录
Elasticsearch 8.x 的子聚合用法的 lambda 方式创建时,中间变量返回 Aggregation.Builder.ContainerBuilder
类型,它的 aggregations
函数可以反复添加新的聚合,从而形成嵌套子聚合的方法。
虽然没有 Elasticsearch 6.x 客户端的 subAggregation
方法好用,但是还是可以等价实现的。