ES多个字段group by操作

以下操作基于es6.8

第一种方式

这种方式查询出来的数据不是扁平化的,而是一层套一层的,比如字段一套字段二。

GET 索引name/索引type/_search
{
	"size": 0,
	"aggregations": {
		"字段一的结果命名": {
			"terms": {
				"field": "要group的字段"
			},
			"aggregations": {
				"字段二的结果命名": {
					"terms": {
						"field": "要group的字段"
					}
				}
			}
		}
	}
}

结果,one下面的buckets里面是two,每个two下面有自己的bukets,就是two的值和count。

java代码实现

    public void demo1(RestHighLevelClient client) {
        // group
        TermsAggregationBuilder oneBuilder = AggregationBuilders.terms("one").field("flowCode.keyword");
        TermsAggregationBuilder twoBuilder = AggregationBuilders.terms("two").field("stepExecuteId");
        oneBuilder.subAggregation(twoBuilder);
        // search
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(oneBuilder);
        // request
        SearchRequest searchRequest = new SearchRequest("index");
        searchRequest.types("type");
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            Terms oneTerm = (Terms) response.getAggregations().asMap().get("one");
            for (Terms.Bucket bucket : oneTerm.getBuckets()) {
                System.out.println("one下面的" + bucket.getKey() + ", count是: " + bucket.getDocCount());
                Terms twoTerm = (Terms) bucket.getAggregations().asMap().get("two");
                for (Terms.Bucket twoTermBucket : twoTerm.getBuckets()) {
                    System.out.println("two下面的" + twoTermBucket.getKey() + ", count是: " + twoTermBucket.getDocCount());
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

封装

封装一个通用的聚合查询并映射到java类中
 

package com.es.agg.demo;

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.index.query.BoolQueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class App2<T> {

    private RestHighLevelClient client;

    /**
     * group by 多字段统计
     *
     * @param index
     * @param type
     * @param queryBuilder
     * @param clazz
     * @param groupKeys
     * @return
     */
    public List<T> group(String index, String type, String resultKey, BoolQueryBuilder queryBuilder, Class<T> clazz, String... groupKeys) {
        TermsAggregationBuilder firstBuilder = null;
        TermsAggregationBuilder topAggregationBuilder = null;
        for (String groupKey : groupKeys) {
            TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms(groupKey).field(groupKey);
            if (topAggregationBuilder != null) {
                topAggregationBuilder.subAggregation(aggregationBuilder);
            } else {
                firstBuilder = aggregationBuilder;
            }
            topAggregationBuilder = aggregationBuilder;
        }
        SearchResponse response = this.group(index, type, queryBuilder, firstBuilder);
        List<EsModel> finishModels = new ArrayList<>();
        Terms terms = (Terms) response.getAggregations().getAsMap().get(groupKeys[0]);
        this.buildEsModel(groupKeys, resultKey, terms, null, 0, finishModels);
        return finishModels.stream().map(item -> item.build(clazz)).collect(Collectors.toList());
    }

    /**
     * 构建es group by model
     *
     * @param groupKeys
     * @param terms
     * @param topEsModel
     * @param level
     * @param finishModels
     */
    private void buildEsModel(String[] groupKeys, String resultKey, Terms terms, EsModel topEsModel, int level, List<EsModel> finishModels) {
        if (level == (groupKeys.length - 1)) {
            if (terms != null && terms.getBuckets() != null && !terms.getBuckets().isEmpty()) {
                for (Terms.Bucket bucket : terms.getBuckets()) {
                    EsModel parentEsModel = new EsModel(groupKeys[level], bucket.getKey().toString(), topEsModel);
                    EsModel childEsModel = new EsModel(resultKey, bucket.getDocCount(), parentEsModel);
                    finishModels.add(childEsModel);
                }
            }
        } else {
            if (terms != null && terms.getBuckets() != null && !terms.getBuckets().isEmpty()) {
                for (Terms.Bucket bucket : terms.getBuckets()) {
                    EsModel esModel = new EsModel(groupKeys[level], bucket.getKey().toString(), topEsModel);
                    Terms childTerms = (Terms) bucket.getAggregations().getAsMap().get(groupKeys[level + 1]);
                    buildEsModel(groupKeys, resultKey, childTerms, esModel, level + 1, finishModels);
                }
            } else {
                return;
            }
        }
    }

    public SearchResponse group(String index, String type, BoolQueryBuilder queryBuilder, TermsAggregationBuilder aggregationBuilder) {
        // 搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.aggregation(aggregationBuilder);
        searchSourceBuilder.size(0);
        // 基础设置
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.types(type);
        searchRequest.source(searchSourceBuilder);
        try {
            return client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

第二种方式

这种方式查出来的数据更扁平化,容易被接受

GET 索引名称/索引类型/_search
{
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "result": {
            "terms": {
                "script": {
                    "inline": "doc['字段1'].value +'分隔符'+ doc['字段2'].value "
                }
            }
        }
    }
}

可以看到,这次的数据很扁平化,只是中间有了分隔符,读取数据的时候 需要自己处理

java代码实现

    public void demo2(RestHighLevelClient client) {
        // script
        Script script = new Script(ScriptType.INLINE, "groovy", "doc['flowCode.keyword'].value+'-split-'+doc['stepExecuteId'].value", new HashMap<>());
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("result").script(script);
        // search
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.aggregation(aggregationBuilder);
        // request
        SearchRequest searchRequest = new SearchRequest("index");
        searchRequest.types("type");
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            Terms oneTerm = (Terms) response.getAggregations().asMap().get("result");
            for (Terms.Bucket bucket : oneTerm.getBuckets()) {
                // 此处自己split分隔处理
                System.out.println(bucket.getKey().toString());
                System.out.println(bucket.getDocCount());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

代码地址: https://github.com/qiaomengnan16/es-agg-demo

关注公众号,回复加群,邀请你进入技术内推、分享群,一起聊聊新鲜事儿

 

  • 11
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
根据提供的引用内容,可以使用Elasticsearch的Composite Aggregation来根据字段进行分组。Composite Aggregation可以对多个字段进行分组,并且可以通过指定一个或多个桶排序规则来对结果进行排序。除此之外,还可以使用其他配置选项如size、after_key等来更好地控制查询结果。\[2\] 例如,可以使用以下查询来根据字段进行分组: ``` GET /index/_search { "_source": \["id"\], "query": { "bool": { "filter": \[ { "range": { "createTime": { "gte": "start", "lte": "end" } } } \], "must": \[ { "match": { "key": "val" } } \] } }, "aggs": { "id_count": { "cardinality": { "field": "id" } }, "group_by_id": { "terms": { "field": "id", "order": { "uid_count": "desc" }, "size": 6000 }, "aggs": { "uid_count": { "cardinality": { "field": "uid" } }, "uid_count_filter": { "bucket_selector": { "buckets_path": { "uidCount": "uid_count" }, "script": "params.uidCount > 1" } } } }, "stats_deviceId": { "stats_bucket": { "buckets_path": "group_by_id._count" } } } } ``` 这个查询将根据字段"id"进行分组,并且对每个分组计算"uid"的唯一值数量。然后,使用"bucket_selector"过滤掉"uid"数量小于等于1的分组。最后,使用"stats_bucket"计算所有分组的总数。\[3\] #### 引用[.reference_title] - *1* [ES多个字段group by操作](https://blog.csdn.net/cainiao1412/article/details/120876972)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [elasticsearch 语句 group by多个字段](https://blog.csdn.net/qq_39264897/article/details/130596536)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值