ElasticSearch的Aggregation聚合查询

251 阅读3分钟

“携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

Aggregation聚合查询

聚合(Aggregation),允许在数据上生成复杂的统计,很像sql中的GROUP BY

概念

Buckets(桶) 满足某个条件的文档集合 Metrics(指标) 为某个桶中的文档计算得到的统计信息

select count(columnName)
from table
group by columnName

count(columnName)相当于一个指标,group by columnName相当于一个桶

一个桶就是满足特定条件的一个文档集合:

一名员工要么属于男性桶,或者女性桶。
城市Albany属于New York州这个桶。
日期2014-10-28属于十月份这个桶。

随着聚合被执行,每份文档中的值会被计算来决定它们是否匹配了桶的条件。如果匹配成功,那么该文档会被置入该桶中,同时聚合会继续执行。

桶也能够嵌套在其它桶中,能让你完成层次或者条件划分这些需求。

ES中有很多类型的桶,让你可以将文档通过多种方式进行划分(按小时,按最流行的词条,按年龄区间,按地理位置,以及更多)。但是从根本上,它们都根据相同的原理运作:按照条件对文档进行划分。

指标(Metrics)

桶能够让我们对文档进行有意义的划分,但是最终我们还是需要对每个桶中的文档进行某种指标计算。分桶是达到最终目的的手段:提供了对文档进行划分的方法,从而让你能够计算需要的指标。

多数指标仅仅是简单的数学运算(比如,min,mean,max以及sum),它们使用文档中的值进行计算。在实际应用中,指标能够让你计算例如平均薪资,最高出售价格,或者百分之95的查询延迟。

将两者结合起来

一个聚合就是一些桶和指标的组合。一个聚合可以只有一个桶,或者一个指标,或者每样一个。在桶中甚至可以有多个嵌套的桶。比如,我们可以将文档按照其所属国家进行分桶,然后对每个桶计算其平均薪资(一个指标)。

因为桶是可以嵌套的,我们能够实现一个更加复杂的聚合操作:

将文档按照国家进行分桶。(桶) 然后将每个国家的桶再按照性别分桶。(桶) 然后将每个性别的桶按照年龄区间进行分桶。(桶) 最后,为每个年龄区间计算平均薪资。(指标) 此时,就能够得到每个<国家,性别,年龄>组合的平均薪资信息了。它可以通过一个请求,一次数据遍历来完成!

常见的聚合查询

(1)统计某个字段的数量
ValueCountBuilder vcb= AggregationBuilders.count("count_uid").field("uid");
(2)去重统计某个字段的数量(有少量误差)
CardinalityBuilder cb= AggregationBuilders.cardinality("distinct_count_uid").field("uid");
(3)聚合过滤
FilterAggregationBuilder fab= AggregationBuilders.filter("uid_filter").filter(QueryBuilders.queryStringQuery("uid:001"));
(4)按某个字段分组
TermsBuilder tb= AggregationBuilders.terms("group_name").field("name");
(5)求和
SumBuilder sumBuilder= AggregationBuilders.sum("sum_price").field("price");
(6)求平均
AvgBuilder ab= AggregationBuilders.avg("avg_price").field("price");
(7)求最大值
MaxBuilder mb= AggregationBuilders.max("max_price").field("price");
(8)求最小值
MinBuilder min= AggregationBuilders.min("min_price").field("price");
(9)按日期间隔分组
DateHistogramBuilder dhb= AggregationBuilders.dateHistogram("dh").field("date");
(10)获取聚合里面的结果
TopHitsBuilder thb= AggregationBuilders.topHits("top_result");
(11)嵌套的聚合
NestedBuilder nb= AggregationBuilders.nested("negsted_path").path("quests");
(12)反转嵌套
AggregationBuilders.reverseNested("res_negsted").path("kps ");

代码中的使用:

此场景中goods是一个数组的形式,想要统计name包含"张"的文档中goods.goodsNum的总数

GET /search/_search
{
 "aggs": {
     "goodsCount": {
         "value_count": {
             "field": "goods.goodsNum.keyword"
          }
     }
 },
 "query": {
     "match": {
         "name": "张"
     }
 }
}

java代码如下:

ValueCountAggregationBuilder field = AggregationBuilders.count("goodsCount").field("goods.goodsNum.keyword");
query.withAggregations(field);
 
Aggregations aggregations = (Aggregations)result.getAggregations().aggregations();
Map<String, Aggregation> map = aggregations.asMap();
ValueCount goodsCount = (ValueCount)map.get("goodsCount");
long value = goodsCount.getValue();