@dungan
2019-09-23T11:58:43.000000Z
字数 4080
阅读 118
Elasticsearch
理解聚合最简单的方式就是可以把它粗略的看做 SQL 的 GROUP BY 操作和 SQL 的聚合函数。你可以在执行搜索后在一个返回结果中同时返回搜索结果和聚合结果。可以一次拿到所有的结果,避免网络切换,就此而言,这是一个非常强大和高效功能。
ES 中的聚合分类 :
- 对一个数据集求最大(max)、最小(min)、和(sum)、平均值(avg)、计数(count) 等指标的聚合称为
指标聚合(metric)。- 对查询出的数据进行分组(group by) 称为
桶聚合(bucketing)。- ES 还提供了另外两种聚合类型
矩阵聚合(matrix)和管道聚合(pipleline)。
由于聚合时会一并输出匹配到的搜索结果,如果你只关心聚合结果,可以通过设置 size=0 来不显示 hits 搜索结果。
{"size": 0,"aggs": {"max_product_id": {"max": {"field": "product_id"}}}}
# 获取已审核商品中id最小的那个{"query": {"term": {"status": 1}},"size": 0,"aggs": {"min_product_id": {"min": {"field": "product_id"}}}}
# 获取已审核商品价格的平均值{"query": {"term": {"status": 1}},"size": 0,"aggs": {"avg_price": {"avg": {"field": "price"}}}}
{"size": 0,"aggs": {"uniq_phone_count": {"cardinality": {"field": "phone"}}}}
因为 age 值有可能是 null,而某些时刻我们需要过滤 age=null 的这类文档。
{"aggs": {"age_count": {"value_count": {"field": "age"}}}}
stats 可以一次性获取字段的 count max min avg sum 值。
{"size": 0,"aggs": {"age_stats": {"stats": {"field": "age"}}}}
例如,你想看公司员工年纪占比 50% 的是哪个年龄段。
{"aggs": {"age_percents": {"percentiles": {"field": "age"}}}}...# 可以看到 <= 31 岁的人员占了公司员工的一半{"aggregations": {"age_percents": {"values": {"1.0": 20,"5.0": 21,"25.0": 25,"50.0": 31,"75.0": 35.00000000000001,"95.0": 39,"99.0": 40}}}}...
ES 默认情况下提供了一组常用的百分占比数值
[1, 5, 25, 50, 75, 95, 99]。
如果想自定义百分占比,可以使用 percents,例如你想看到占比 36.7% 的年龄段是哪个,就可以这样设置。
{"size":0,"aggs": {"age_percents": {"percentiles": {"field": "age","percents" : [36.7, 55, 99]}}}}
和上面相反,例如我们想看年龄 <= 30 岁的人在公司的百分占比,那么这种方式就非常适合我们。
# 这里我们统计年龄分别小于 25 岁和 30 岁的人在总人数中的百分占比{"aggs": {"gge_perc_rank": {"percentile_ranks": {"field": "age","values": [25,30]}}}}
聚合计算的值可以取字段的值,也可是脚本(script)计算的结果。
# 这里通过脚本为平均值加上10{"size": 0,"aggs": {"avg_price": {"avg": {"script": {"source": "doc.price.value + 10"}}}}}
# 指定 `field`,在脚本中用 `_value` 取字段的值{"size":0,"aggs": {"next_product_id": {"max": {"field": "product_id","script": {"source": "_value + 1"}}}}}
# 按商品类型统计商品数量{"size":0,"aggs": {"group_by_type": {"terms": {"field": "type","size":3}}}}
size 指定返回多少个分组。
选取符合过滤条件的文档进行聚合,也就是先过滤再聚合。
我们在 aggs 的时候,指定要 filter 的条件。
# 查询出已审核通过的商品,然后再统计每种商品类型的数量{"size": 0,"aggs": {"filter_product_type": {"filter": {"term": {"audit": 1}},"aggs": {"group_by_product_type": {"terms": {"field": "product_type"}}}}}}
当然通过 query 过滤,我们一样也可以拿到统计结果。
{"size": 0,"query": {"term": {"audit": 1}},"aggs": {"group_by_product_type": {"terms": {"field": "product_type"}}}}
分桶聚合中嵌入指标聚合 这是一个适用于所有聚合操作的通用模式,你可以任意嵌套聚合,从你的数据中提取你需要的主题汇总。
聚合数据支持排序,例如下面的 average_comment_num。
//统计按文章类型分组后每类文章的平均评论数,聚合数据按平均评论数升序展示{"size":0,"aggs": {"group_by_article_type": {"terms": {"field": "article_type","order":{"average_comment_num": "asc"}},"aggs": {"average_comment_num": {"avg": {"field": "comment_num"}}}}}}
{"size": 0,"aggs": {"jobs": {"terms": {"field": "job.keyword","size": 10},"aggs": {"age_range": {"range": {"field": "age","ranges": [{"to": 20},{"from": 20,"to": 30},{"from": 30}]}}}}}}
// 按照指定的`价格范围区间`进行分组,然后在每组内再按照`商品类型`进行分组,最后再计算每组`商品类型`的平均价格{"size": 0,"aggs": {"group_by_price": {"range": {"field": "price","ranges": [{"from": 0,"to": 20},{"from": 20,"to": 40},{"from": 40,"to": 50}]},"aggs": {"group_by_product_type": {"terms": {"field": "product_type"},"aggs": {"average_price": {"avg": {"field": "price"}}}}}}}}
# 这里我们获取八,九月份的订单量{"size": 0,"aggs": {"orders_date_range": {"range": {"field": "created_at","format": "yyyy-MM-dd","ranges": [{"from": "2019-09-01","to": "2019-09-30","key": "September"},{"from": "2019-08-01","to": "2019-08-31","key": "August"}]}}}}# 输出如下{"took": 3,"timed_out": false,"_shards": {"total": 5,"successful": 5,"skipped": 0,"failed": 0},"hits": {"total": 34,"max_score": 0.0,"hits": []},"aggregations": {"date_range": {"buckets": [{"key": "August","from": 1.5646176E12,"from_as_string": "2019-08-01","to": 1.5672096E12,"to_as_string": "2019-08-31","doc_count": 12},{"key": "September","from": 1.567296E12,"from_as_string": "2019-09-01","to": 1.5698016E12,"to_as_string": "2019-09-30","doc_count": 5}]}}}
默认情况下范围分组的 key 是
2019-09-01-2019-09-30这种格式,你可以通过在每个分组设置一个key来自定义 key 名。