以下操作基于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
关注公众号,回复加群,邀请你进入技术内推、分享群,一起聊聊新鲜事儿