聚合查询
相关概念
桶(Buckets):满足特定条件的文档集合。可以理解为 SQL 中的 GROUP BY。例如:“北京”是一个桶,“上海”也是一个桶。
指标(Metrics):对桶内的文档进行的统计计算。可以理解为 SQL 中的 COUNT(), SUM(), AVG() 等。例如:计算北京这个桶里有多少人(计数),平均薪资是多少(求平均值)。
聚合查询:其实就是通和指标的各种组合嵌套的统计查询
聚合查询的语法结构
一个标准的聚合查询 (aggs) 通常与查询 (query) 同级,用于在全数据集或查询结果集上进行聚合分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| GET /your_index/_search { "size": 0, "query": { ... // 例如 match, term, range 等查询 }, "aggs": { // 聚合查询的主体,也可以简写为 "aggregations" "给你的聚合结果起个名字": { // 例如 "avg_salary", "group_by_city" "聚合类型": { // 例如 "terms", "avg", "date_histogram" "聚合体": ... // 不同类型有不同的参数,如 "field": "salary" } }, "另一个聚合": { ... "aggs": { // 在当前聚合的桶内,继续进行嵌套聚合 "子聚合的名字": { "子聚合类型": { ... } } } } } }
|
举例:
目标:先找出所有“Electronics”类别的商品(查询),然后按品牌(brand)分组(桶聚合),并计算每个品牌的平均价格(指标聚合)。
**查询请求 (Request)**:
json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| GET /sales/_search { "size": 0, "query": { "term": { "category": "electronics" } }, "aggs": { "group_by_brand": { "terms": { "field": "brand.keyword" }, "aggs": { "avg_price": { "avg": { "field": "price" } } } } } }
|
**预期的聚合结果 (Response)**:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| { "took": 5, "timed_out": false, "_shards": { ... }, "hits": { // 命中文档部分(因为 size=0,所以只有总数) "total": { "value": 100, // 一共有100个电子类商品文档 "relation": "eq" }, "max_score": null, "hits": [] // 具体的文档列表为空 }, "aggregations": { // !!!聚合结果部分 !!! "group_by_brand": { // 对应请求中 "group_by_brand" 聚合的结果 "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ // 这是一个桶聚合,所以核心是一个 buckets 数组 { "key": "Apple", "doc_count": 35, "avg_price": { "value": 899.99 } }, { "key": "Samsung", "doc_count": 30, "avg_price": { "value": 749.99 } }, { "key": "Sony", "doc_count": 20, "avg_price": { "value": 649.99 } }, ... // 其他桶 ] } } }
|
group_by_brand、avg_price这两个聚合名称在结果里也会按层次出现,解析结果是需要识别自行业务处理。
聚合查询分类
假设我们有一个索引 sales,存储着商品销售记录,包含以下字段:
product (keyword): 产品名称,如 “iPhone 15”, “Coffee Maker”
category (keyword): 产品类别,如 “Electronics”, “Beverages”
price (double): 价格
sold_date (date): 销售日期
region (keyword): 销售地区,如 “North”, “South”
指标聚合(Metrics Aggregations)
对数据集进行计算,输出一个或多个值。
平均值(Avg)
- 描述:计算一个字段的平均值。
- 示例:计算所有产品的平均售价
1 2 3 4 5 6 7 8 9 10 11
| GET /sales/_search { "size": 0, "aggs": { "avg_price": { "avg": { "field": "price" } } } }
|
结果
1 2 3 4 5 6 7 8
| { ... "aggregations" : { "avg_price" : { "value" : 245.72 // 这就是计算出的平均值 } } }
|
求和(Sum)
- 描述:计算一个字段的总和。
- 示例:计算所有产品的销售总额。
1 2 3 4 5 6 7 8 9 10 11
| GET /sales/_search { "size": 0, "aggs": { "total_sales": { "sum": { "field": "price" } } } }
|
最大值(Max)与最小值(Min)
- 描述:找出一个字段的最大或最小值。
- 示例:找出最贵的产品价格。
json
1 2 3 4 5 6 7 8 9 10 11
| GET /sales/_search { "size": 0, "aggs": { "max_price": { "max": { "field": "price" } } } }
|
统计(Stats)
- 描述:一次性返回 count, max, min, avg, sum 五个基本指标。
- 示例:获取价格的基本统计信息。
总结
- 指标聚合是计算,输出的是数值。
avg, sum, max, min 是单值指标聚合。
stats 是多值指标聚合,非常方便,一次获取所有常用统计值。
桶聚合(Bucket Aggregations)
将文档分组到不同的桶中
词条聚合(Terms)
- 描述:按某个字段的确切值进行分组,是最常用的桶聚合。
- 示例:按产品类别进行分组,看每个类别有多少商品。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| GET /sales/_search { "size": 0, "aggs": { "group_by_category": { "terms": { "field": "category", "size": 20 } } } }
{ ... "aggregations" : { "group_by_category" : { "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 120, // 除了返回的桶之外,其他所有类别的文档总数 "buckets" : [ // 桶的集合,默认按 doc_count 降序排列 { "key" : "Electronics", "doc_count" : 85 }, { "key" : "Beverages", "doc_count" : 63 }, { "key" : "Books", "doc_count" : 42 } ] } } }
|
范围聚合(Range)
- 描述:按数值范围创建桶。
- 示例:按价格区间分组(0-50, 50-100, 100-500, 500+)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| GET /sales/_search { "size": 0, "aggs": { "price_ranges": { "range": { "field": "price", "ranges": [ { "to": 50 }, { "from": 50, "to": 100 }, { "from": 100, "to": 500 }, { "from": 500 } ] } } } }
{ ... "aggregations" : { "price_ranges" : { "buckets" : [ { "key" : "*-50.0", "to" : 50.0, "doc_count" : 128 }, { "key" : "50.0-100.0", "from" : 50.0, "to" : 100.0, "doc_count" : 204 }, { "key" : "100.0-500.0", "from" : 100.0, "to" : 500.0, "doc_count" : 312 }, { "key" : "500.0-*", "from" : 500.0, "doc_count" : 56 } ] } } }
|
日期直方图聚合(Date Histogram)
- 描述:按固定的时间间隔(如每天、每月)创建桶。处理时间序列数据必备。
- 示例:统计每月有多少销售额。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| GET /sales/_search { "size": 0, "aggs": { "sales_per_month": { "date_histogram": { "field": "sold_date", "calendar_interval": "month", "format": "yyyy-MM" } } } }
{ ... "aggregations" : { "sales_per_month" : { "buckets" : [ { "key_as_string" : "2024-01", "key" : 1704067200000, "doc_count" : 150 }, { "key_as_string" : "2024-02", "key" : 1706745600000, "doc_count" : 183 }, { "key_as_string" : "2024-03", "key" : 1709251200000, "doc_count" : 210 } ] } } }
|
组合聚合:桶内嵌套指标
查询:计算每个产品类别的平均价格。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| GET /sales/_search { "size": 0, "aggs": { "group_by_category": { "terms": { "field": "category" }, "aggs": { "avg_price_in_category": { "avg": { "field": "price" } } } } } }
{ ... "aggregations" : { "group_by_category" : { "buckets" : [ { "key" : "Electronics", "doc_count" : 85, "avg_price_in_category" : { "value" : 599.99 } }, { "key" : "Beverages", "doc_count" : 63, "avg_price_in_category" : { "value" : 5.99 } }, { "key" : "Books", "doc_count" : 42, "avg_price_in_category" : { "value" : 25.49 } } ] } } }
|
在每个类别桶(key)的内部,除了 doc_count,还多出了一个以子聚合名称(avg_price_in_category)命名的对象,其中包含了计算出的指标 value
复杂组合聚合:多层嵌套
查询:先按地区分组,再按月份分组,然后计算每个地区-月组合的销售总额。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| GET /sales/_search { "size": 0, "aggs": { "group_by_region": { "terms": { "field": "region" }, "aggs": { "sales_over_time": { "date_histogram": { "field": "sold_date", "calendar_interval": "month", "format": "yyyy-MM" }, "aggs": { "monthly_revenue": { "sum": { "field": "price" } } } } } } } }
{ ... "aggregations" : { "group_by_region" : { "buckets" : [ { "key" : "North", "doc_count" : 400, "sales_over_time" : { "buckets" : [ { "key_as_string" : "2024-01", "key" : 1704067200000, "doc_count" : 120, "monthly_revenue" : { "value" : 35880.50 } }, { "key_as_string" : "2024-02", "key" : 1706745600000, "doc_count" : 145, "monthly_revenue" : { "value" : 42123.20 } } ] } }, { "key" : "South", "doc_count" : 350, "sales_over_time" : { "buckets" : [ { "key_as_string" : "2024-01", "doc_count" : 110, "monthly_revenue" : { "value" : 22500.00 } } ] } } ] } } }
|
- “分层下钻”:先分大组(桶),再在组内进行计算(指标)或再分小组(桶)。
- 可以无限嵌套,但性能会随着嵌套深度增加而下降。
- 这是构建复杂数据分析报表的基础。
这个结果完美展示了数据的“分层下钻”结构:
- 第一层:
region 桶(North, South)。
- 在每一个
region 桶内,是第二层:date_histogram 桶(2024-01, 2024-02…)。
- 在每一个
日期 桶内,是最终计算的指标:该月该地区的销售
-