ES資料聚合

LL。。。發表於2024-10-18

1.什麼是資料聚合

聚合(aggregations)可以讓我們極其方便的實現對資料的統計、分析、運算。例如:

  • 什麼品牌的手機最受歡迎?

  • 這些手機的平均價格、最高價格、最低價格?

  • 這些手機每月的銷售情況如何?

實現這些統計功能的比資料庫的sql要方便的多,而且查詢速度非常快,可以實現近實時搜尋效果。

聚合常見的有三類:

  • 桶(Bucket聚合:用來對文件做分組

    • TermAggregation:按照文件欄位值分組,例如按照品牌值分組、按照國家分組

    • Date Histogram:按照日期階梯分組,例如一週為一組,或者一月為一組

  • 度量(Metric聚合:用以計算一些值,比如:最大值、最小值、平均值等

    • Avg:求平均值

    • Max:求最大值

    • Min:求最小值

    • Stats:同時求maxminavgsum

  • 管道(pipeline聚合:其它聚合的結果為基礎做進一步運算

注意:參加聚合的欄位必須是keyword、日期、數值、布林型別

2.DSL實現聚合

與之前的搜尋功能類似,我們依然先學習DSL的語法,再學習JavaAPI.

1.Bucket聚合

例如我們要統計所有商品中共有哪些商品分類,其實就是以分類(category)欄位對資料分組。category值一樣的放在同一組,屬於Bucket聚合中的Term聚合。

GET /items/_search
{
  "size": 0, 
  "aggs": {
    "category_agg": {
      "terms": {
        "field": "category",
        "size": 20
      }
    }
  }
}

語法說明:

  • size:設定size為0,就是每頁查0條,則結果中就不包含文件,只包含聚合

  • aggs:定義聚合

    • category_agg:聚合名稱,自定義,但不能重複

      • terms:聚合的型別,按分類聚合,所以用term

        • field:參與聚合的欄位名稱

        • size:希望返回的聚合結果的最大數量

2.帶條件聚合

但真實場景下,使用者會輸入搜尋條件,因此聚合必須是對搜尋結果聚合。那麼聚合必須新增限定條件。

例如,我想知道價格高於3000元的手機品牌有哪些,該怎麼統計呢?

GET /items/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "category": "手機"
          }
        },
        {
          "range": {
            "price": {
              "gte": 300000
            }
          }
        }
      ]
    }
  }, 
  "size": 0, 
  "aggs": {
    "brand_agg": {
      "terms": {
        "field": "brand",
        "size": 20
      }
    }
  }
}

3.Metric聚合

上節課,我們統計了價格高於3000的手機品牌,形成了一個個桶。現在我們需要對桶內的商品做運算,獲取每個品牌價格的最小值、最大值、平均值。

這就要用到Metric聚合了,例如stat聚合,就可以同時獲取minmaxavg等結果。

我們需要從需求中分析出搜尋查詢的條件和聚合的目標:

  • 搜尋查詢條件:

    • 價格高於3000

    • 必須是手機

  • 聚合目標:統計的是品牌,肯定是對brand欄位做term聚合

GET /items/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "category": "手機"
          }
        },
        {
          "range": {
            "price": {
              "gte": 300000
            }
          }
        }
      ]
    }
  }, 
  "size": 0, 
  "aggs": {
    "brand_agg": {
      "terms": {
        "field": "brand",
        "size": 20
      },
      "aggs": {
        "stats_meric": {
          "stats": {
            "field": "price"
          }
        }
      }
    }
  }
}

query部分就不說了,我們重點解讀聚合部分語法。

可以看到我們在brand_agg聚合的內部,我們新加了一個aggs引數。這個聚合就是brand_agg的子聚合,會對brand_agg形成的每個桶中的文件分別統計。

  • stats_meric:聚合名稱

    • stats:聚合型別,stats是metric聚合的一種

      • field:聚合欄位,這裡選擇price,統計價格
        java中程式碼實現
@Test
void testAgg() throws IOException {
    // 1.建立Request
    SearchRequest request = new SearchRequest("items");
    // 2.準備請求引數
    BoolQueryBuilder bool = QueryBuilders.boolQuery()
            .filter(QueryBuilders.termQuery("category", "手機"))
            .filter(QueryBuilders.rangeQuery("price").gte(300000));
    request.source().query(bool).size(0);
    // 3.聚合引數
    request.source().aggregation(
            AggregationBuilders.terms("brand_agg").field("brand").size(5)
    );
    // 4.傳送請求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 5.解析聚合結果
    Aggregations aggregations = response.getAggregations();
    // 5.1.獲取品牌聚合
    Terms brandTerms = aggregations.get("brand_agg");
    // 5.2.獲取聚合中的桶
    List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
    // 5.3.遍歷桶內資料
    for (Terms.Bucket bucket : buckets) {
        // 5.4.獲取桶內key
        String brand = bucket.getKeyAsString();
        System.out.print("brand = " + brand);
        long count = bucket.getDocCount();
        System.out.println("; count = " + count);
    }
}

相關文章