SpringCloud:ES 数据聚合

781 阅读4分钟

ES 数据聚合

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

1. 什么是聚合

  • 聚合是对文档数据的统计、分析、计算

2. 聚合的常见种类有哪些

我们可以通过官方文档去查看所有的聚合种类www.elastic.co/guide/en/el…

  • 桶(Bucket)聚合:用来对文档分组
    • TermAggregation:按照文档字段值分组,例如按照国家分组、按照性别分组
    • Data Histogram:按照日期阶梯分组,例如一周为一组,一月为一组
  • 度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等
    • Avg:求平均值
    • Max:求最大值
    • Min:求最小值
    • Stats:同时求 Max、Min、Avg、Sum等的组合求值
  • 管道(PipeLine)聚合:其他聚合的结果作为基础做聚合,即组合聚合

**注意:**参加聚合的字段必须是 keyword、日期、数值、布尔类型

3. DSL 实现聚合

接下来我们同通过对现有数据按照酒店品牌对数据分组。此时可以根据酒店品牌的名称做聚合,也就是 Bucket 聚合。

3.1 Bucket 聚合语法

  • 语法如下:

    # 聚合文档
    GET /hotel/_search
    {
      "size": 0,  // 设置size为0,结果中不包含文档,只包含聚合结果
      "aggs": { // 定义聚合
        "brandAgg": { //给聚合起个名字
          "terms": { // 聚合的类型,按照品牌值聚合,所以选择term
            "field": "brand", // 参与聚合的字段
            "size": 20 // 希望获取的聚合结果数量
          }
        }
      }
    }
    

    **注意:**聚合文档名字是为了让后续管道聚合能用上

  • 结果如图:

    image-20220801165808453

3.2 聚合结果排序

默认情况下,Bucket 聚会会统计所有的 Bucket 内的文档数量,记作 count,并且按照 count 降序排序。默认降序排序

我们可以通过指定 order 数据,自定义聚合的排序方式:

  • 基本语法

    # 聚合文档,自定义排序规则
    GET /hotel/_search
    {
      "size": 0,
      "aggs": {
        "brandAgg": {
          "terms": {
            "field": "brand",
            "size": 20,
            "order": {
              "_count": "asc" // 按照_count升序排列
            }
          }
        }
      }
    }
    
  • 结果如图:

    image-20220801170521116

3.3 限定聚合范围

默认情况下,Bucket 聚合会对索引库的所有文档做聚合,但是在真实环境下,用户输入限制条件限定聚合范围,后台返回聚合结果。下面就来演示一下添加限定条件。

限定 Bucket 聚合范围

  • 基本语法

    我们可以限定聚合的文档返回,只要添加 query 条件即可

    # 聚合文档,限定聚合范围
    GET /hotel/_search
    {
      "query": {
        "range": {
          "price": {
            "lte": 200 // 只对200元以下的文档聚合
          }
        }
      }, 
      "size": 0,
      "aggs": {
        "brandAgg": {
          "terms": {
            "field": "brand",
            "size": 20
          }
        }
      }
    }
    
  • 结果如图:

    image-20220801171829854

    现在就是将价格在200一下的酒店做了 Bucket 聚合。

3.4 Metric 聚合语法

我们通过 Bucket 聚合统计了每个品牌的酒店数量,将它们一一化作不同的桶。接下来我们对每个不同品牌名的桶内数据进行计算,例如:结合每个相同酒店获取它们的评分的 max、min、avg 等值。

我的理解就是先按照品牌分组,再获取每组用户评分的最高分、最低分、平均分等数据image-20220803154616809

这里就要使用 Metric 聚合的 stats 聚合:就可以获取 min、max、avg 等结果。

  • 语法如下:

    这个 stats 聚合是在 brandAgg 的聚合内部,就是对基础的 Bucket 聚合的结果分别进行 stats 的 max、min、avg 等数值的计算。

    # 嵌套聚合 matric
    GET /hotel/_search
    {
      "size": 0,
      "aggs": {
        "brandAgg": {
          "terms": {
            "field": "brand",
            "size": 20
          },
          "aggs": { // 是brands聚合的子聚合,也就是分组后对每组分别计算
            "score_stats": { // 聚合名称
              "stats": { // 聚合类型,这里stats可以计算min、max、avg等
                "field": "score" // 聚合字段,这里是score
              }
            }
          }
        }
      }
    }
    
  • 结果如图

    image-20220801173845378

如果需要根据 Metric 聚合的结果进行排序。我们有以下语法

  • 语法如下

    # 嵌套聚合 matric,自定义排序
    GET /hotel/_search
    {
      "size": 0,
      "aggs": {
        "brandAgg": {
          "terms": {
            "field": "brand",
            "size": 20,
            "order": {
              "scoreAgg.avg": "desc"
            }
          },
          "aggs": {
            "scoreAgg": {
              "stats": {
                "field": "score"
              }
            }
          }
        }
      }
    }
    

    image-20220803155507718

3.5 小结

aggs代表聚合,与query同级,此时query的作用是?

  • 限定聚合的的文档范围

聚合必须的三要素:

  • 聚合名称
  • 聚合类型
  • 聚合字段

聚合可配置属性有:

  • size:指定聚合结果数量
  • order:指定聚合结果排序方式
  • field:指定聚合字段

4. RestAPI 实现聚合

4.1 API 语法

聚合条件和 query 田间同级别,所以许可要用到 request.source() 来指定聚合条件。

  • 聚合条件的语法

    image-20220803161710518

  • 聚合的结果和查询的结果不同,所以需要重新写一个结果解析,不过依旧是按照 JSON 逐层解析:

    image-20220803161839037

  • 实际代码:

    /**
     * Bucket 聚合案例
     * @throws IOException 输入输出流异常
     */
    @Test
    void AggregationTest() throws IOException {
        // 1. 准备 request
        SearchRequest request = new SearchRequest("hotel");
    
        // 2. 准备 DSL
        // 2.1 设置 size
        request.source().size(0);
        // 2.2 聚合
        request.source().aggregation(
                AggregationBuilders.terms("brandAgg").
                        field("brand").
                        size(10)
        );
        // 3. 发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4. 解析结果
        Aggregations aggregations = response.getAggregations();
        // 4.1 根据聚合名称获取聚合结果
        Terms brandTerms = aggregations.get("brandAgg");
        // 4.2 获取 Buckets
        List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
        // 4.3 遍历
        for (Terms.Bucket bucket : buckets) {
            String key = bucket.getKeyAsString();
            System.out.println(key);
        }
    }