背景
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 聚合 的例子。
在這里插入代碼片// 構建聚合
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")))))
);
添加多個子聚合
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;});
測試代碼:
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的最大值和最小值:
{"aggregations": {"max_field2": {"max": {"field": "field2"}},"min_field2": {"min": {"field": "field2"}}},"terms": {"field": "field1"}
}
請求體聚合
// 8.x SearchRequest 構建方法使用 BuilderSearchRequest.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 對象,同時包含一個聚合名稱參數:
public class AggregationBuilder {// 聚合名稱 name// 子聚合列表 children// 當前聚合對象 aggregation....
}
最終聚合 JSON 對象,請求 SearchRequest 對象中 N 個聚合的情況:
{"aggregations": {"max_field2": {"max": {"field": "field2"}},"min_field2": {"min": {"field": "field2"}}},
}
啟示錄
Elasticsearch 8.x 的子聚合用法的 lambda 方式創建時,中間變量返回 Aggregation.Builder.ContainerBuilder
類型,它的 aggregations
函數可以反復添加新的聚合,從而形成嵌套子聚合的方法。
雖然沒有 Elasticsearch 6.x 客戶端的 subAggregation
方法好用,但是還是可以等價實現的。