云计算百科
云计算领域专业知识百科平台

Java 中间件:Elasticsearch 聚合查询(Terms/Max/Min 聚合)

在这里插入图片描述

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Java中间件这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


文章目录

  • Java 中间件:Elasticsearch 聚合查询(Terms/Max/Min 聚合) 📊
    • 什么是 Elasticsearch 聚合?🔍
    • 准备工作:环境搭建与依赖配置 ⚙️
      • 1. Elasticsearch 服务
      • 2. Java 项目依赖
        • 方案一:使用 High Level REST Client(适用于 7.x)
        • 方案二:使用新版 Java API Client(推荐用于 8.x)
    • 数据模型设计:模拟电商订单 🛒
      • 创建索引(Java API Client 示例)
    • Terms 聚合:按类别分组统计 📦
      • 聚合原理
      • Java 实现(Java API Client)
      • 输出结果
      • 高级用法:排序与大小控制
    • Max/Min 聚合:求极值 📈
      • 场景示例
      • Java 实现(单独使用)
    • 组合聚合:Terms + Max/Min(子聚合)🔗
      • 聚合结构图解
      • Java 实现(子聚合)
      • 输出结果
    • 使用 High Level REST Client 的实现(兼容 7.x)🔄
    • 性能优化与注意事项 ⚡
      • 1. 避免高基数字段的 Terms 聚合
      • 2. 合理设置 `size`
      • 3. 使用 `docvalue_fields` 提升性能
      • 4. 避免在 Text 字段上聚合
      • 5. 监控聚合耗时
    • 实际应用场景举例 🌍
      • 场景一:用户行为分析
      • 场景二:IoT 设备监控
      • 场景三:金融风控
    • 错误排查与常见问题 ❌
      • Q1: 聚合返回空结果?
      • Q2: Max/Min 返回 null?
      • Q3: Terms 聚合结果不全?
      • Q4: 客户端报 `CoercionException`?
    • 扩展:与其他聚合组合使用 🧩
    • 总结与展望 🎯

Java 中间件:Elasticsearch 聚合查询(Terms/Max/Min 聚合) 📊

在现代分布式系统和大数据应用中,Elasticsearch 已经成为不可或缺的搜索与分析引擎。它不仅提供强大的全文检索能力,还通过其灵活而高效的聚合(Aggregation)功能,支持对海量数据进行多维度统计、分组、指标计算等操作。对于 Java 开发者而言,掌握 Elasticsearch 的聚合查询机制,尤其是 Terms 聚合、Max 聚合 和 Min 聚合,是构建高性能数据分析平台的关键技能。

本文将深入探讨 Elasticsearch 聚合查询的核心概念,并结合 Java 客户端(High Level REST Client 及新版 Java API Client)提供完整的代码示例,帮助你从零开始理解并实现这些聚合操作。无论你是初学者还是有一定经验的开发者,都能从中获得实用的知识和最佳实践。


什么是 Elasticsearch 聚合?🔍

Elasticsearch 聚合是一种对搜索结果进行分组、统计和分析的机制。它类似于 SQL 中的 GROUP BY、MAX()、MIN() 等操作,但功能更强大、性能更高,尤其适合处理非结构化或半结构化数据。

聚合分为三大类:

  • Bucket Aggregations(桶聚合):将文档分组到不同的“桶”中,如按字段值分组(Terms)、按数值范围分组(Range)、按时间间隔分组(Date Histogram)等。
  • Metric Aggregations(指标聚合):对文档中的数值字段进行计算,如求最大值(Max)、最小值(Min)、平均值(Avg)、总和(Sum)等。
  • Pipeline Aggregations(管道聚合):对其他聚合的结果进行二次计算,如移动平均、导数等。
  • 本文重点聚焦于 Terms(桶聚合) 与 Max/Min(指标聚合) 的组合使用,这是实际业务中最常见的场景之一。

    💡 小知识:聚合操作是在查询阶段之后执行的,因此你可以先通过 query 过滤出感兴趣的文档,再对这些文档进行聚合分析。


    准备工作:环境搭建与依赖配置 ⚙️

    在开始编码前,我们需要确保开发环境已正确配置。

    1. Elasticsearch 服务

    首先,确保本地或远程已运行 Elasticsearch 服务(建议 7.x 或 8.x 版本)。你可以通过以下命令检查服务是否正常:

    curl http://localhost:9200

    应返回类似如下 JSON 响应:

    {
    "name" : "node-1",
    "cluster_name" : "elasticsearch",
    "version" : {
    "number" : "8.9.0",

    }
    }

    2. Java 项目依赖

    我们以 Maven 项目为例。根据你使用的 Elasticsearch 版本,选择对应的客户端:

    方案一:使用 High Level REST Client(适用于 7.x)

    ⚠️ 注意:Elasticsearch 官方已在 8.0 后弃用 High Level REST Client,推荐新项目使用 Java API Client。

    <dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.17.10</version>
    </dependency>

    方案二:使用新版 Java API Client(推荐用于 8.x)

    <dependency>
    <groupId>co.elastic.clients</groupId>
    <artifactId>elasticsearch-java</artifactId>
    <version>8.9.0</version>
    </dependency>

    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
    </dependency>

    🔗 官方文档参考:Elasticsearch Java API Client

    本文将同时提供两种客户端的代码示例,但重点讲解新版 Java API Client,因其更现代、类型安全且面向未来。


    数据模型设计:模拟电商订单 🛒

    为了演示聚合查询,我们设计一个简单的电商订单索引 orders,包含以下字段:

    字段名类型说明
    orderId keyword 订单 ID
    customerId keyword 客户 ID
    productName text / keyword 商品名称
    category keyword 商品类别(如 “Electronics”, “Books”)
    price double 商品价格
    orderDate date 下单时间

    我们将插入几条测试数据:

    { "orderId": "O001", "customerId": "C100", "productName": "iPhone 14", "category": "Electronics", "price": 999.0, "orderDate": "2023-10-01T10:00:00Z" }
    { "orderId": "O002", "customerId": "C101", "productName": "MacBook Pro", "category": "Electronics", "price": 2499.0, "orderDate": "2023-10-02T11:30:00Z" }
    { "orderId": "O003", "customerId": "C100", "productName": "Effective Java", "category": "Books", "price": 45.0, "orderDate": "2023-10-03T09:15:00Z" }
    { "orderId": "O004", "customerId": "C102", "productName": "iPad Air", "category": "Electronics", "price": 599.0, "orderDate": "2023-10-04T14:20:00Z" }
    { "orderId": "O005", "customerId": "C101", "productName": "Clean Code", "category": "Books", "price": 40.0, "orderDate": "2023-10-05T16:45:00Z" }

    创建索引(Java API Client 示例)

    import co.elastic.clients.elasticsearch.ElasticsearchClient;
    import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
    import co.elastic.clients.transport.endpoints.BooleanResponse;

    public void createOrdersIndex(ElasticsearchClient client) throws IOException {
    BooleanResponse response = client.indices().create(
    CreateIndexRequest.of(c -> c
    .index("orders")
    .mappings(m -> m
    .properties("orderId", p -> p.keyword(k -> k))
    .properties("customerId", p -> p.keyword(k -> k))
    .properties("productName", p -> p.text(t -> t.fields("keyword", f -> f.keyword(k -> k))))
    .properties("category", p -> p.keyword(k -> k))
    .properties("price", p -> p.double_(d -> d))
    .properties("orderDate", p -> p.date(d -> d))
    )
    )
    );
    System.out.println("Index created: " + response.acknowledged());
    }


    Terms 聚合:按类别分组统计 📦

    Terms 聚合是最常用的桶聚合,它根据字段的唯一值对文档进行分组。例如,我们可以按 category 字段将订单分为 “Electronics” 和 “Books” 两类。

    聚合原理

    Terms 聚合会遍历所有文档,提取指定字段的值,然后将相同值的文档归入同一个“桶”。每个桶包含:

    • key:字段值(如 “Electronics”)
    • doc_count:该值出现的文档数量

    Java 实现(Java API Client)

    import co.elastic.clients.elasticsearch._types.aggregations.TermsAggregation;
    import co.elastic.clients.elasticsearch.core.SearchRequest;
    import co.elastic.clients.elasticsearch.core.SearchResponse;
    import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;

    public void termsAggregationByCategory(ElasticsearchClient client) throws IOException {
    SearchRequest request = SearchRequest.of(s -> s
    .index("orders")
    .size(0) // 不返回具体文档,只返回聚合结果
    .aggregations("categories", a -> a
    .terms(t -> t
    .field("category")
    .size(10) // 最多返回10个桶
    )
    )
    );

    SearchResponse<Void> response = client.search(request, Void.class);
    var categories = response.aggregations().get("categories").sterms();

    for (StringTermsBucket bucket : categories.buckets().array()) {
    System.out.println("Category: " + bucket.key() + ", Count: " + bucket.docCount());
    }
    }

    输出结果

    Category: Electronics, Count: 3
    Category: Books, Count: 2

    高级用法:排序与大小控制

    默认情况下,Terms 聚合按 doc_count 降序排列。你也可以按字母顺序排序:

    .terms(t -> t
    .field("category")
    .order(o -> o.key(SortOrder.Asc)) // 按 key 升序
    )

    📌 注意:size 参数控制返回的桶数量,默认为 10。若需返回更多,可增大该值,但会影响性能。


    Max/Min 聚合:求极值 📈

    Max 聚合和 Min 聚合属于指标聚合,用于计算数值字段的最大值和最小值。

    场景示例

    • 找出最贵的商品(Max price)
    • 找出最便宜的商品(Min price)

    Java 实现(单独使用)

    public void maxMinPriceAggregation(ElasticsearchClient client) throws IOException {
    SearchRequest request = SearchRequest.of(s -> s
    .index("orders")
    .size(0)
    .aggregations("max_price", a -> a.max(m -> m.field("price")))
    .aggregations("min_price", a -> a.min(m -> m.field("price")))
    );

    SearchResponse<Void> response = client.search(request, Void.class);

    Double maxPrice = response.aggregations().get("max_price").max().value();
    Double minPrice = response.aggregations().get("min_price").min().value();

    System.out.println("Max Price: " + maxPrice); // 2499.0
    System.out.println("Min Price: " + minPrice); // 40.0
    }


    组合聚合:Terms + Max/Min(子聚合)🔗

    真实业务中,我们往往需要在分组的基础上计算每组的极值。例如:

    “每个商品类别的最高价格和最低价格是多少?”

    这就需要用到 子聚合(Sub-Aggregation):在 Terms 聚合的每个桶内,再嵌套 Max 和 Min 聚合。

    聚合结构图解

    #mermaid-svg-koIivI0Ga7JlZ3pK{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-koIivI0Ga7JlZ3pK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-koIivI0Ga7JlZ3pK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-koIivI0Ga7JlZ3pK .error-icon{fill:#552222;}#mermaid-svg-koIivI0Ga7JlZ3pK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-koIivI0Ga7JlZ3pK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-koIivI0Ga7JlZ3pK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-koIivI0Ga7JlZ3pK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-koIivI0Ga7JlZ3pK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-koIivI0Ga7JlZ3pK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-koIivI0Ga7JlZ3pK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-koIivI0Ga7JlZ3pK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-koIivI0Ga7JlZ3pK .marker.cross{stroke:#333333;}#mermaid-svg-koIivI0Ga7JlZ3pK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-koIivI0Ga7JlZ3pK p{margin:0;}#mermaid-svg-koIivI0Ga7JlZ3pK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-koIivI0Ga7JlZ3pK .cluster-label text{fill:#333;}#mermaid-svg-koIivI0Ga7JlZ3pK .cluster-label span{color:#333;}#mermaid-svg-koIivI0Ga7JlZ3pK .cluster-label span p{background-color:transparent;}#mermaid-svg-koIivI0Ga7JlZ3pK .label text,#mermaid-svg-koIivI0Ga7JlZ3pK span{fill:#333;color:#333;}#mermaid-svg-koIivI0Ga7JlZ3pK .node rect,#mermaid-svg-koIivI0Ga7JlZ3pK .node circle,#mermaid-svg-koIivI0Ga7JlZ3pK .node ellipse,#mermaid-svg-koIivI0Ga7JlZ3pK .node polygon,#mermaid-svg-koIivI0Ga7JlZ3pK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-koIivI0Ga7JlZ3pK .rough-node .label text,#mermaid-svg-koIivI0Ga7JlZ3pK .node .label text,#mermaid-svg-koIivI0Ga7JlZ3pK .image-shape .label,#mermaid-svg-koIivI0Ga7JlZ3pK .icon-shape .label{text-anchor:middle;}#mermaid-svg-koIivI0Ga7JlZ3pK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-koIivI0Ga7JlZ3pK .rough-node .label,#mermaid-svg-koIivI0Ga7JlZ3pK .node .label,#mermaid-svg-koIivI0Ga7JlZ3pK .image-shape .label,#mermaid-svg-koIivI0Ga7JlZ3pK .icon-shape .label{text-align:center;}#mermaid-svg-koIivI0Ga7JlZ3pK .node.clickable{cursor:pointer;}#mermaid-svg-koIivI0Ga7JlZ3pK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-koIivI0Ga7JlZ3pK .arrowheadPath{fill:#333333;}#mermaid-svg-koIivI0Ga7JlZ3pK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-koIivI0Ga7JlZ3pK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-koIivI0Ga7JlZ3pK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-koIivI0Ga7JlZ3pK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-koIivI0Ga7JlZ3pK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-koIivI0Ga7JlZ3pK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-koIivI0Ga7JlZ3pK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-koIivI0Ga7JlZ3pK .cluster text{fill:#333;}#mermaid-svg-koIivI0Ga7JlZ3pK .cluster span{color:#333;}#mermaid-svg-koIivI0Ga7JlZ3pK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-koIivI0Ga7JlZ3pK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-koIivI0Ga7JlZ3pK rect.text{fill:none;stroke-width:0;}#mermaid-svg-koIivI0Ga7JlZ3pK .icon-shape,#mermaid-svg-koIivI0Ga7JlZ3pK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-koIivI0Ga7JlZ3pK .icon-shape p,#mermaid-svg-koIivI0Ga7JlZ3pK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-koIivI0Ga7JlZ3pK .icon-shape rect,#mermaid-svg-koIivI0Ga7JlZ3pK .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-koIivI0Ga7JlZ3pK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-koIivI0Ga7JlZ3pK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-koIivI0Ga7JlZ3pK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Search Query

    Terms Aggregation on 'category'

    Bucket: Electronics

    Bucket: Books

    Max Aggregation on 'price'

    Min Aggregation on 'price'

    Max Aggregation on 'price'

    Min Aggregation on 'price'

    Java 实现(子聚合)

    public void termsWithMaxMinAggregation(ElasticsearchClient client) throws IOException {
    SearchRequest request = SearchRequest.of(s -> s
    .index("orders")
    .size(0)
    .aggregations("categories", a -> a
    .terms(t -> t.field("category").size(10))
    .aggregations("max_price", aa -> aa.max(m -> m.field("price")))
    .aggregations("min_price", aa -> aa.min(m -> m.field("price")))
    )
    );

    SearchResponse<Void> response = client.search(request, Void.class);
    var categories = response.aggregations().get("categories").sterms();

    for (StringTermsBucket bucket : categories.buckets().array()) {
    String category = bucket.key();
    Double maxPrice = bucket.aggregations().get("max_price").max().value();
    Double minPrice = bucket.aggregations().get("min_price").min().value();

    System.out.printf("Category: %s, Max Price: %.2f, Min Price: %.2f%n",
    category, maxPrice, minPrice);
    }
    }

    输出结果

    Category: Electronics, Max Price: 2499.00, Min Price: 599.00
    Category: Books, Max Price: 45.00, Min Price: 40.00

    ✅ 这正是我们想要的:每个类别内部的价格极值。


    使用 High Level REST Client 的实现(兼容 7.x)🔄

    虽然官方推荐使用新版客户端,但许多老项目仍在使用 High Level REST Client。以下是等效实现:

    import org.elasticsearch.action.search.SearchRequest;
    import org.elasticsearch.action.search.SearchResponse;
    import org.elasticsearch.client.RequestOptions;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.elasticsearch.search.aggregations.AggregationBuilders;
    import org.elasticsearch.search.aggregations.bucket.terms.Terms;
    import org.elasticsearch.search.aggregations.metrics.Max;
    import org.elasticsearch.search.aggregations.metrics.Min;

    public void termsWithMaxMin_HLRC(RestHighLevelClient client) throws IOException {
    SearchRequest searchRequest = new SearchRequest("orders");
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    sourceBuilder.size(0);

    // 构建 Terms 聚合,并添加子聚合
    sourceBuilder.aggregation(
    AggregationBuilders.terms("categories")
    .field("category")
    .subAggregation(AggregationBuilders.max("max_price").field("price"))
    .subAggregation(AggregationBuilders.min("min_price").field("price"))
    );

    searchRequest.source(sourceBuilder);
    SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

    Terms categories = response.getAggregations().get("categories");
    for (Terms.Bucket bucket : categories.getBuckets()) {
    String category = bucket.getKeyAsString();
    Max maxPrice = bucket.getAggregations().get("max_price");
    Min minPrice = bucket.getAggregations().get("min_price");

    System.out.printf("Category: %s, Max: %.2f, Min: %.2f%n",
    category, maxPrice.getValue(), minPrice.getValue());
    }
    }

    🔗 更多关于 High Level REST Client 的信息,请参考 Elasticsearch 7.x Java REST Client 文档


    性能优化与注意事项 ⚡

    聚合查询虽然强大,但在大数据量下可能带来性能挑战。以下是一些最佳实践:

    1. 避免高基数字段的 Terms 聚合

    如果对 customerId(假设有百万级唯一值)做 Terms 聚合,会导致内存爆炸。此时应:

    • 使用 composite 聚合进行分页
    • 或限制 size 并配合 include/exclude 过滤

    2. 合理设置 size

    默认 size=10,若业务需要更多桶,逐步增加,避免一次性拉取过多。

    3. 使用 docvalue_fields 提升性能

    确保聚合字段启用了 doc_values(默认开启),这是列式存储,专为聚合优化。

    4. 避免在 Text 字段上聚合

    Text 字段用于全文检索,不可直接聚合。应使用 .keyword 子字段(如 productName.keyword)。

    5. 监控聚合耗时

    通过 Kibana 或 _nodes/stats API 监控聚合请求的 CPU 和内存使用。


    实际应用场景举例 🌍

    场景一:用户行为分析

    • 按 userType(普通/会员)分组,统计每次会话的最长/最短停留时间(Max/Min sessionDuration)

    场景二:IoT 设备监控

    • 按 deviceId 分组,找出每台设备今日的最高/最低温度(Max/Min temperature)

    场景三:金融风控

    • 按 transactionType 分组,识别每类交易的最大/最小金额,用于异常检测

    📊 这些场景都可通过 Terms + Max/Min 子聚合 优雅实现。


    错误排查与常见问题 ❌

    Q1: 聚合返回空结果?

    • 检查字段是否存在且类型正确(如 price 是否为 double)
    • 确认索引中有数据:GET /orders/_search
    • 确保未被 query 过滤掉所有文档

    Q2: Max/Min 返回 null?

    • 字段值全为 null 或 missing
    • 字段类型不匹配(如字符串无法求极值)

    Q3: Terms 聚合结果不全?

    • 默认 size=10,增加 size 或使用 composite 聚合分页

    Q4: 客户端报 CoercionException?

    • 字段映射与实际数据类型不一致,检查 mapping

    扩展:与其他聚合组合使用 🧩

    Terms 聚合可与多种聚合组合,形成复杂分析:

    #mermaid-svg-wFfundElaBMd3XJ8{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wFfundElaBMd3XJ8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wFfundElaBMd3XJ8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wFfundElaBMd3XJ8 .error-icon{fill:#552222;}#mermaid-svg-wFfundElaBMd3XJ8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wFfundElaBMd3XJ8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wFfundElaBMd3XJ8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wFfundElaBMd3XJ8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wFfundElaBMd3XJ8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wFfundElaBMd3XJ8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wFfundElaBMd3XJ8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wFfundElaBMd3XJ8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wFfundElaBMd3XJ8 .marker.cross{stroke:#333333;}#mermaid-svg-wFfundElaBMd3XJ8 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wFfundElaBMd3XJ8 p{margin:0;}#mermaid-svg-wFfundElaBMd3XJ8 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-wFfundElaBMd3XJ8 .cluster-label text{fill:#333;}#mermaid-svg-wFfundElaBMd3XJ8 .cluster-label span{color:#333;}#mermaid-svg-wFfundElaBMd3XJ8 .cluster-label span p{background-color:transparent;}#mermaid-svg-wFfundElaBMd3XJ8 .label text,#mermaid-svg-wFfundElaBMd3XJ8 span{fill:#333;color:#333;}#mermaid-svg-wFfundElaBMd3XJ8 .node rect,#mermaid-svg-wFfundElaBMd3XJ8 .node circle,#mermaid-svg-wFfundElaBMd3XJ8 .node ellipse,#mermaid-svg-wFfundElaBMd3XJ8 .node polygon,#mermaid-svg-wFfundElaBMd3XJ8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wFfundElaBMd3XJ8 .rough-node .label text,#mermaid-svg-wFfundElaBMd3XJ8 .node .label text,#mermaid-svg-wFfundElaBMd3XJ8 .image-shape .label,#mermaid-svg-wFfundElaBMd3XJ8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-wFfundElaBMd3XJ8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wFfundElaBMd3XJ8 .rough-node .label,#mermaid-svg-wFfundElaBMd3XJ8 .node .label,#mermaid-svg-wFfundElaBMd3XJ8 .image-shape .label,#mermaid-svg-wFfundElaBMd3XJ8 .icon-shape .label{text-align:center;}#mermaid-svg-wFfundElaBMd3XJ8 .node.clickable{cursor:pointer;}#mermaid-svg-wFfundElaBMd3XJ8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wFfundElaBMd3XJ8 .arrowheadPath{fill:#333333;}#mermaid-svg-wFfundElaBMd3XJ8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wFfundElaBMd3XJ8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wFfundElaBMd3XJ8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wFfundElaBMd3XJ8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wFfundElaBMd3XJ8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wFfundElaBMd3XJ8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wFfundElaBMd3XJ8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wFfundElaBMd3XJ8 .cluster text{fill:#333;}#mermaid-svg-wFfundElaBMd3XJ8 .cluster span{color:#333;}#mermaid-svg-wFfundElaBMd3XJ8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wFfundElaBMd3XJ8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wFfundElaBMd3XJ8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-wFfundElaBMd3XJ8 .icon-shape,#mermaid-svg-wFfundElaBMd3XJ8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wFfundElaBMd3XJ8 .icon-shape p,#mermaid-svg-wFfundElaBMd3XJ8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wFfundElaBMd3XJ8 .icon-shape rect,#mermaid-svg-wFfundElaBMd3XJ8 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wFfundElaBMd3XJ8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wFfundElaBMd3XJ8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wFfundElaBMd3XJ8 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Terms on category

    Avg price

    Sum sales

    Top Hits: 最贵商品详情

    Cardinality: 唯一客户数

    例如,获取每个类别中最贵商品的完整信息:

    .aggregations("top_expensive", aa -> aa
    .topHits(t -> t
    .sort(s -> s.field(f -> f.field("price").order(SortOrder.Desc)))
    .size(1)
    )
    )


    总结与展望 🎯

    通过本文,我们深入学习了 Elasticsearch 中 Terms 聚合、Max 聚合 和 Min 聚合 的核心概念与 Java 实现。关键要点包括:

    • ✅ Terms 聚合用于分组,Max/Min 用于求极值
    • ✅ 通过子聚合实现在分组内计算指标
    • ✅ 掌握新版 Java API Client 与旧版 High Level REST Client 的写法
    • ✅ 了解性能优化与常见陷阱

    Elasticsearch 的聚合能力远不止于此。随着你对 Histogram、Date Histogram、Percentiles、Scripted Metric 等高级聚合的探索,你将能构建更强大的实时分析系统。

    🌟 最后建议:在生产环境中,始终对聚合查询进行压测,并结合业务需求合理设计索引 mapping 和查询逻辑。

    Happy aggregating! 🚀


    🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Java 中间件:Elasticsearch 聚合查询(Terms/Max/Min 聚合)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!