1.什麼是資料聚合
聚合(aggregations
)可以讓我們極其方便的實現對資料的統計、分析、運算。例如:
-
什麼品牌的手機最受歡迎?
-
這些手機的平均價格、最高價格、最低價格?
-
這些手機每月的銷售情況如何?
實現這些統計功能的比資料庫的sql要方便的多,而且查詢速度非常快,可以實現近實時搜尋效果。
聚合常見的有三類:
-
桶(
Bucket
)聚合:用來對文件做分組-
TermAggregation
:按照文件欄位值分組,例如按照品牌值分組、按照國家分組 -
Date Histogram
:按照日期階梯分組,例如一週為一組,或者一月為一組
-
-
度量(
Metric
)聚合:用以計算一些值,比如:最大值、最小值、平均值等-
Avg
:求平均值 -
Max
:求最大值 -
Min
:求最小值 -
Stats
:同時求max
、min
、avg
、sum
等
-
-
管道(
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
聚合,就可以同時獲取min
、max
、avg
等結果。
我們需要從需求中分析出搜尋查詢的條件和聚合的目標:
-
搜尋查詢條件:
-
價格高於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);
}
}