elasticsearch aggregations_ElasticSearch中composite聚合的使用

简介composite

      composite是一个多桶(multi-bucket)聚合,它从不同的聚合源创建composite buckets聚合,与其他multi-bucket聚合不同,composite聚合可用于高效地对多级聚合中的所有 bucket 进行分页。这种聚合提供了一种方法来流特定聚合的所有 bucket,类似于 scroll 对文档所做的操作。

     composite buckets 是由为每个document 数据提取/创建的值的组合构建的,每个组合被视为组合 bucket。如下为官方给的例子:

{"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

验证多字段聚合可行性

      上面的例子为官方例子,我们需要自己弄个例子检查可行性,我们现在创建一个index,索引名为composite_test,mapping如下

{"area" : {"type" : "keyword"          },"userid" : {"type" : "keyword"          },"sendtime" : {"type" : "date","format" : "yyyy-MM-dd HH:mm:ss"          }}

       我们创建好了index,index中一共有三个字段,area,userid,sendtime三个字段

数据库实现方式

     为了方便比较,我们也使用MySQL数据建一个一模一样的表,方便对比聚合出来的数据是否正确,表名为composite_test。

我们首先对数据表composite_test插入5条记录,分别如下

196dbb2221e815f7ad18f81023e36d04.png

使用group by对三个字段进行聚合,以下为在数据库中的实现:

SELECT COUNT(1),area,userid,sendtime FROM composite_test GROUP BY area,userid,sendtime

结果如下:

6bf59dc5615a96ba9a0c5431454ed847.png

以上为数据库的聚合实现

ES实现方式

    所以我们使用ES进行多字段聚合的时候如果结果和以上的一样则是正确的,我们也一样往ES composite_test索引中 插入相同的5条数据,在kibana上执行以下命令插入

POST composite_test/_bulk{ "index" : {"_type" :"_doc"}}{"area":"33","userid":"400015","sendtime":"2019-01-17 00:00:00"}{ "index" : {"_type" : "_doc"}}{"area":"33","userid":"400015","sendtime":"2019-01-17 00:00:00"}{ "index" : {"_type" : "_doc"}}{"area":"35","userid":"400016","sendtime":"2019-01-18 00:00:00"}{ "index" : { "_type" : "_doc"}}{"area":"35","userid":"400016","sendtime":"2019-01-18 00:00:00"}{ "index" : {"_type" : "_doc"}}{"area":"33","userid":"400017","sendtime":"2019-01-17 00:00:00"}

查询ES,我们发现已经存在了这5条数据了。

be9513049030e979ed9c94fe57764f33.png

 接下来我们先使用composite的DSL查询:

GET composite_test/_search{"size": 0, "aggs" : {"my_buckets": {"composite" : {"" : [              { "area": { "terms": {"field": "area" } } },               { "userid": { "terms": {"field": "userid" } } },               { "sendtime": { "date_histogram": { "field": "sendtime","interval": "1d","format": "yyyy-MM-dd"} } }             ]         }        }    }}

DSL中我们分别对area、userid、sendtime做多字段聚合

结果如下

[        {"key" : {"area" : "33","userid" : "400015","sendtime" : "2019-01-17"          },"doc_count" : 2        },        {"key" : {"area" : "33","userid" : "400017","sendtime" : "2019-01-17"          },"doc_count" : 1        },        {"key" : {"area" : "35","userid" : "400016","sendtime" : "2019-01-18"          },"doc_count" : 2        }]

        从以上响应的json数组中我们不难看出,该聚合聚合出来的数据是和数据库聚合出来的数据是一致的

        所以 composite是可以使用在多字段聚合上的。论证完可行性,我们接下来使用java来实现,实话说这一块我是看源代码才会使用的,网上资料基本为0,而且官方java使用文档里也没用,的确是与遇到了不少坑,写出来方便以后使用能快速回忆。

java使用composite聚合

首先创建Maven项目,加入ElasticSearch依赖:

org.elasticsearch.clientelasticsearch-rest-high-level-client6.5.4

关键代码如下

SearchRequest searchRequest = new SearchRequest("composite_test"); searchRequest.types("_doc");SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.size(0);/********************以下组装聚合的三个字段****************************/List> sources = new ArrayList<>();DateHistogramValuesSourceBuilder sendtime = new DateHistogramValuesSourceBuilder("sendtime")                .field("sendtime")                .dateHistogramInterval(DateHistogramInterval.days(1))                .format("yyyy-MM-dd").order(SortOrder.DESC).missingBucket(false);        sources.add(sendtime);TermsValuesSourceBuilder userid = new TermsValuesSourceBuilder("userid").field("userid").missingBucket(true);        sources.add(userid);        TermsValuesSourceBuilder dttype = new TermsValuesSourceBuilder("area").field("area").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 list =  parsedComposite.getBuckets();Map data = new HashMap<>();for(ParsedBucket parsedBucket:list){  data.clear();  for (Map.Entry m :  parsedBucket.getKey().entrySet()) {    data.put(m.getKey(),m.getValue());  }  data.put("count",parsedBucket.getDocCount());      System.out.println(data);}/*************************************/

控制台打印如下

{area=35, count=2, sendtime=2019-01-18, userid=400016}{area=33, count=2, sendtime=2019-01-17, userid=400015}{area=33, count=1, sendtime=2019-01-17, userid=400017}

       数据正确,方法可用,其实这个方法是RestHighLevelClient替我们封装了composite生成DSL。

       这里注意一下missingBucket的设置,这个的意思是如果该字段没值,为true的时候会返回null,为false不返回整条数据,注意这里是整条数据,而不是单单这个字段而已。

低版本使用ES多字段聚合

       以上就是composite的验证和在java中的使用方法,建议在6.5+版本使用,但这个时候小伙伴可能会问,如果我是6.5以下的版本呢,这里也有一个方法,可以使用ES的子聚合(sub-aggregation),直接上代码:

SearchRequest searchRequest = new SearchRequest("composite_test"); searchRequest.types("_doc");SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.size(0);/********************以下组装聚合的三个字段****************************/AggregationBuilder sendtime=AggregationBuilders.dateHistogram("sendtime").field("sendtime").format("yyyy-MM-dd").interval(86400000);AggregationBuilder area=AggregationBuilders.terms("area").field("area");AggregationBuilder userid=AggregationBuilders.terms("userid").field("userid");//实现功能关键点area.subAggregation(userid);sendtime.subAggregation(area);/*********************执行查询******************************/searchSourceBuilder.aggregation(sendtime);searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);/********************取出数据*******************/Aggregations aggregations = searchResponse.getAggregations();//取出数据aggHandle(aggregations);/*************************************/

 运行响应的数据为

{area=33, count=2, sendtime=2019-01-17, userid=400015}, {area=33, count=1, sendtime=2019-01-17, userid=400017}, {area=35, count=2, sendtime=2019-01-18, userid=400016}

所以使用子聚合也能多值聚合数据,其中实现的关键点在于

area.subAggregation(userid);sendtime.subAggregation(area);

相当于把聚合之后的数据在做一次聚合

Elasticsearch ,可以使用聚合Aggregation)实现对文档进行聚合统计,其包括出现次数的统计。下面是一个示例: 假设我们有一个名为 "sales" 的索引,包含以下文档: ``` { "product": "A", "price": 10.0, "timestamp": "2021-08-01T10:00:00Z" } { "product": "B", "price": 15.0, "timestamp": "2021-08-01T10:05:00Z" } { "product": "A", "price": 12.0, "timestamp": "2021-08-01T10:10:00Z" } { "product": "C", "price": 20.0, "timestamp": "2021-08-01T10:15:00Z" } { "product": "A", "price": 8.0, "timestamp": "2021-08-01T10:20:00Z" } { "product": "B", "price": 18.0, "timestamp": "2021-08-01T10:25:00Z" } ``` 现在,我们想要统计每个产品出现的次数,可以使用以下聚合查询: ``` { "aggs": { "products": { "terms": { "field": "product" } } } } ``` 其,"aggs" 是聚合查询的关键字,"products" 是我们给这个聚合起的名字,"terms" 表示我们要按照某个字段进行分组,"field" 指定了我们要按照哪个字段进行分组。 运行上述查询后,得到的结果如下: ``` { "aggregations": { "products": { "buckets": [ { "key": "A", "doc_count": 3 }, { "key": "B", "doc_count": 2 }, { "key": "C", "doc_count": 1 } ] } } } ``` 其,"key" 表示产品名称,"doc_count" 表示该产品出现的次数。 如果想要对出现次数进行排序,可以使用以下聚合查询: ``` { "aggs": { "products": { "terms": { "field": "product", "order": { "_count": "desc" } } } } } ``` 其,"order" 表示按照什么字段进行排序,"_count" 表示按照出现次数进行排序,"desc" 表示降序排列。 运行上述查询后,得到的结果如下: ``` { "aggregations": { "products": { "buckets": [ { "key": "A", "doc_count": 3 }, { "key": "B", "doc_count": 2 }, { "key": "C", "doc_count": 1 } ] } } } ``` 其,产品 A 出现的次数最多,排在第一位。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值