composite多桶聚合总结
简介composite
composite是一个多桶聚合,它从不同的源创建复合桶,与其他多桶聚合不同,复合聚合可用于高效地对多级聚合中的所有桶进行分页。这种聚合提供了一种方法来流特定聚合的所有桶,类似于滚动对文档所做的操作。
组合桶是由为每个文档提取/创建的值的组合构建的,每个组合被视为组合桶。如下为官方给的例子:
{ "keyword": ["foo", "bar"], "number": [23, 65, 76] }
如果我们同时对keyword和number两个字段进行聚合会得出以下的结果:
{ "keyword": "foo", "number": 23 } { "keyword": "foo", "number": 65 } { "keyword": "foo", "number": 76 } { "keyword": "bar", "number": 23 } { "keyword": "bar", "number": 65 } { "keyword": "bar", "number": 76 }
看到上面的例子是不是恍然大悟, 就像类似sql中的多group by 多字段,可以对多个字段进行聚合,这非常适用于对于多维度出报表的需求,我这里建议使用的版本为6.5+,因为6.5版本以下此功能还处于测试阶段,设计和代码没有正式的GA功能成熟,并且没有担保。当然我这里也会提供6.5版本以下如何进行多聚合字段的使用。
首先上官方文档地址:
https://www.elastic.co/guide/en/elasticsearch/reference/6.5/search-aggregations-bucket-composite-aggregation.html
其实官方文档已经把该功能说得很详细,如果你只是单纯写DSL实现的话看官方文档就可以,我接下来就介绍如何调用他的javaAPI来使用,当然如果阅读源码能力强的话也可以直接看官方在github的test,地址如下:
https://github.com/elastic/elasticsearch/tree/master/server/src/test/java/org/elasticsearch/search/aggregations/bucket/composite
从以上响应的json数组中我们不难看出,该聚合聚合出来的数据是和数据库聚合出来的数据是一致的,所以 composite是可以使用在多字段聚合上的。论证完可行性,我们接下来使用java来实现,实话说这一块我是看源代码才会使用的,网上资料基本为0,而且官方java使用文档里也没用,的确是与遇到了不少坑,写出来方便以后使用能快速回忆。
package com.springboot.elasticsearch.study.springbootelasticsearch; import lombok.extern.slf4j.Slf4j; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.bucket.composite.*; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.SortOrder; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Author: guodong * @Date: 2021/9/6 16:43 * @Version: 1.0 * @Description: */ @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class CompositeTest { @Resource private RestHighLevelClient client ; @Test public void testCompositeAggregationBuilder01() throws Exception{ SearchRequest searchRequest = new SearchRequest("items"); searchRequest.types("item"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.size(0); List<CompositeValuesSourceBuilder<?>> sources = new ArrayList<>(); DateHistogramValuesSourceBuilder sendtime = new DateHistogramValuesSourceBuilder("birthDay") .field("birthDay") .dateHistogramInterval(DateHistogramInterval.DAY) .format("yyyy-MM-dd").order(SortOrder.DESC).missingBucket(false); sources.add(sendtime); TermsValuesSourceBuilder userid = new TermsValuesSourceBuilder("title").field("title.text").missingBucket(true); sources.add(userid); TermsValuesSourceBuilder dttype = new TermsValuesSourceBuilder("category").field("category.keyword").missingBucket(true); sources.add(dttype); CompositeAggregationBuilder composite = new CompositeAggregationBuilder("my_buckets", sources); composite.size(1000); /*********************执行查询******************************/ searchSourceBuilder.aggregation(composite); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); /********************取出数据*******************/ Aggregations aggregations = searchResponse.getAggregations(); ParsedComposite parsedComposite = aggregations.get("my_buckets"); List<ParsedComposite.ParsedBucket> list = parsedComposite.getBuckets(); Map<String,Object> data = new HashMap<>(); for(ParsedComposite.ParsedBucket parsedBucket:list){ data.clear(); for (Map.Entry<String, Object> m : parsedBucket.getKey().entrySet()) { data.put(m.getKey(),m.getValue()); } data.put("count",parsedBucket.getDocCount()); System.out.println(data); } } }
数据正确,方法可用,其实这个方法是RestHighLevelClient替我们封装了composite生成DSL。这里注意一下missingBucket的设置,这个的意思是如果该字段没值,为true的时候会返回null,为false不返回整条数据,注意这里是整条数据,而不是单单这个字段而已。
参考博客:
https://blog.csdn.net/neweastsun/article/details/108225541
https://blog.csdn.net/qq_18895659/article/details/86540548