ES聚合查询案例

wen java案例 1

本文目录导读:

ES聚合查询案例

  1. 核心概念(必懂)
  2. 基础环境准备(Kibana Dev Tools)
  3. 实战案例(由浅入深)
  4. 高级技巧与注意事项

这是一个非常实用的话题,Elasticsearch 的聚合(Aggregation)功能非常强大,可以从数据中提取各种统计信息,而不仅仅是简单的搜索。

下面我将从基础概念实战案例,为你系统性地讲解 ES 聚合查询,并提供可直接运行的案例。


核心概念(必懂)

在写案例之前,先了解这几个关键词:

  1. aggs:查询请求中的聚合关键字。
  2. metric指标聚合,用来计算数值,如 sumavgmaxmincardinality(去重计数)。
  3. bucket分桶聚合,类似 SQL 中的 GROUP BY,将文档分组到不同的桶中,如 terms(按字段值分)、date_histogram(按日期分)、range(按范围分)。
  4. sub-aggs子聚合,在桶内再次进行聚合,可以嵌套。

基础环境准备(Kibana Dev Tools)

为了方便演示,我们先建立索引并插入测试数据(假设是一个电商订单系统)。

# 1. 创建索引并指定映射(mapping)
PUT /orders
{
  "mappings": {
    "properties": {
      "order_id":    { "type": "keyword" },
      "customer":    { "type": "keyword" },
      "product":     { "type": "keyword" },
      "category":    { "type": "keyword" },
      "price":       { "type": "float" },
      "quantity":    { "type": "integer" },
      "order_date":  { "type": "date" }
    }
  }
}
# 2. 批量插入测试数据
POST /_bulk
{ "index": { "_index": "orders" } }
{ "order_id": "1001", "customer": "Alice", "product": "iphone 15", "category": "Electronics", "price": 999, "quantity": 1, "order_date": "2024-01-15" }
{ "index": { "_index": "orders" } }
{ "order_id": "1002", "customer": "Bob", "product": "MacBook Pro", "category": "Electronics", "price": 1999, "quantity": 1, "order_date": "2024-01-20" }
{ "index": { "_index": "orders" } }
{ "order_id": "1003", "customer": "Alice", "product": "Desk Chair", "category": "Furniture", "price": 150, "quantity": 2, "order_date": "2024-02-05" }
{ "index": { "_index": "orders" } }
{ "order_id": "1004", "customer": "Charlie", "product": "Standing Desk", "category": "Furniture", "price": 450, "quantity": 1, "order_date": "2024-02-10" }
{ "index": { "_index": "orders" } }
{ "order_id": "1005", "customer": "Bob", "product": "Gaming Mouse", "category": "Electronics", "price": 99, "quantity": 3, "order_date": "2024-03-18" }
{ "index": { "_index": "orders" } }
{ "order_id": "1006", "customer": "David", "product": "Coffee Maker", "category": "Appliances", "price": 80, "quantity": 1, "order_date": "2024-03-22" }

实战案例(由浅入深)

案例 1:统计销售额与平均价格(Metric)

需求:计算所有订单的总销售额、平均单价、最大和最小订单金额。

  • 销售额 = 单价 * 数量,这里我们先计算总价(需要用 script 计算),或者简化逻辑直接对 pricesum
GET /orders/_search
{
  "size": 0, 
  "aggs": {
    "total_sales": {
      "sum": { "field": "price" } 
    },
    "avg_price": {
      "avg": { "field": "price" }
    },
    "max_price": {
      "max": { "field": "price" }
    },
    "min_price": {
      "min": { "field": "price" }
    }
  }
}

返回结果

{
  "aggregations": {
    "total_sales": { "value": 3777.0 },
    "avg_price":   { "value": 629.5 },
    "max_price":   { "value": 1999.0 },
    "min_price":   { "value": 80.0 }
  }
}

技巧"size": 0 代表不返回具体文档,只返回聚合结果,提高性能。


案例 2:按商品类目分组,统计每个类目的销售额(Terms Bucket)

需求:类似于 SQL 的 SELECT category, SUM(price) FROM orders GROUP BY category

GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category",
        "size": 10  
      },
      "aggs": {
        "sales_amount": {
          "sum": { "field": "price" }
        }
      }
    }
  }
}

返回结果(部分):

"by_category": {
  "buckets": [
    { "key": "Electronics", "doc_count": 3, "sales_amount": { "value": 3097.0 } },
    { "key": "Furniture",   "doc_count": 2, "sales_amount": { "value": 600.0 } },
    { "key": "Appliances",  "doc_count": 1, "sales_amount": { "value": 80.0 } }
  ]
}

案例 3:按月份统计销售趋势(Date Histogram)

需求:查看每月销售额的趋势。

GET /orders/_search
{
  "size": 0,
  "aggs": {
    "sales_over_time": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "month"
      },
      "aggs": {
        "total_revenue": {
          "sum": {
            "script": {
              "source": "doc['price'].value * doc['quantity'].value"
            }
          }
        }
      }
    }
  }
}

说明:这里使用了 script 来计算 单价 * 数量,更加真实。

返回结果(部分):

"sales_over_time": {
  "buckets": [
    { "key_as_string": "2024-01-01", "key": 1704067200000, "doc_count": 2, "total_revenue": { "value": 2998.0 } },
    { "key_as_string": "2024-02-01", "key": 1706745600000, "doc_count": 2, "total_revenue": { "value": 750.0 } },
    { "key_as_string": "2024-03-01", "key": 1709251200000, "doc_count": 2, "total_revenue": { "value": 377.0 } }
  ]
}

案例 4:Top N 客户(排序案例)

需求:找出消费最多的前 3 名客户。

GET /orders/_search
{
  "size": 0,
  "aggs": {
    "top_customers": {
      "terms": {
        "field": "customer",
        "size": 3,
        "order": { "total_spent": "desc" }
      },
      "aggs": {
        "total_spent": {
          "sum": {
            "script": { "source": "doc['price'].value * doc['quantity'].value" }
          }
        }
      }
    }
  }
}

返回:客户 Bob(1999+297=2296)排第一,Alice(999+300=1299)排第二。


案例 5:价格分布区间(Range Bucket)

需求:统计价格在 0-100,100-500,500 以上的订单数量。

GET /orders/_search
{
  "size": 0,
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "key": "Under $100",  "to": 100 },
          { "key": "$100-$500",   "from": 100, "to": 500 },
          { "key": "Over $500",   "from": 500 }
        ]
      }
    }
  }
}

返回Under $100 有 2 单(99,80),$100-$500 有 2 单(150,450),Over $500 有 2 单(999,1999)。


案例 6:嵌套聚合与百分比(子聚合 + 去重计数)

需求:统计每个类目下的在线商品数量(去重)和销量总和

GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": { "field": "category" },
      "aggs": {
        "unique_products": {
          "cardinality": { "field": "product" }
        },
        "total_quantity": {
          "sum": { "field": "quantity" }
        }
      }
    }
  }
}

高级技巧与注意事项

  1. sizeshard_size:默认 terms 聚合只返回前 10 个桶,如果要统计全局 Top 10,建议设置 shard_size(如 10000),防止分片返回数据不全导致统计偏差。
  2. 过滤与聚合:可以利用 filter 在聚合前缩小范围,聚合 aggs 和查询 query 是并行的,query 会先于 aggs 执行,aggs 的结果是基于 query 的结果集计算的。
    // 例子:只统计 2024 年 1 月之后的电子类目销售
    GET /orders/_search
    {
      "query": { "bool": { "filter": [{ "range": { "order_date": { "gte": "2024-01-01" } } }] } },
      "size": 0,
      "aggs": {
        "electronics_sales": {
          "filter": { "term": { "category": "Electronics" } },
          "aggs": { "total": { "sum": { "field": "price" } } }
        }
      }
    }
  3. sum 需要开启 doc_values:默认启用,但如果是 text 字段需要改为 keyword 类型才能聚合。
  4. 性能优化:聚合非常耗费内存,如果聚合字段不需要分词,尽量使用 keyword 类型,避免在 text 字段上聚合。

案例覆盖了 ES 聚合的 80% 核心场景:

  • 求和平均值 -> metric 聚合
  • 分类统计 -> terms 聚合
  • 时间趋势 -> date_histogram 聚合
  • 范围分布 -> range 聚合
  • 去重统计 -> cardinality 聚合

掌握这些,配合嵌套(nested 或子聚合),你基本上可以应对大部分 BI 报表和业务分析需求了。

如果你有具体的业务场景(搜索某关键词后的商品点击占比”),可以告诉我,我可以给出更精准的 DSL 语法。

抱歉,评论功能暂时关闭!