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

郭慕荣博客园
原文地址:https://www.cnblogs.com/jelly12345/p/15237509.html