聚合查詢是 Elasticsearch 中一種強大的數據分析工具,用于從索引中提取和計算有關數據的統計信息。聚合查詢可以執行各種聚合操作,如計數、求和、平均值、最小值、最大值、分組等,以便進行數據匯總和分析。
下面是一些常見的聚合查詢類型:
聚合查詢通常與查詢語句結合使用,可以在查詢結果的基礎上進行進一步的數據分析和統計。聚合查詢語法使用 JSON 格式,可以通過 Elasticsearch 的 REST API 或各種客戶端庫進行發送和解析。
聚合查詢支持嵌套,即一個聚合內部可以包含別的子聚合,從而實現非常復雜的數據挖掘和統計需求。
在ES中,用于進行聚合的字段可以是exact value也可以是分詞字段,對于分詞字段,可以使用特定的聚合操作來進行分組聚合,例如Terms Aggregation、Date Histogram Aggregation等。
對于text字段的聚合,可以通過開啟fielddata來實現,但通常不建議這樣做,因為fielddata會將聚合使用的數據結構從磁盤(doc_values)轉換為堆內存(field_data),在處理大量數據時容易導致內存溢出(OOM)問題。
如果需要在text字段上執行聚合,可以考慮在該字段上添加.keyword子字段,并使用該子字段進行聚合操作,以獲得更準確的結果。
在 Elasticsearch 中,聚合操作主要依賴于 doc_values 或 fielddata 來進行。
在設計索引時,需要根據字段類型和使用場景的不同,合理選擇是否啟用 Doc Values 或 Fielddata,以平衡性能和資源消耗的需求。
當執行聚合操作時,Elasticsearch 需要訪問所有匹配文檔的字段值。對于非文本字段,默認情況下Elasticsearch 使用 doc values 來實現。對于文本字段,必須首先啟用 fielddata。然而,由于 fielddata 占用大量內存,Elasticsearch 默認禁用了它。
如果你確實需要對一個文本字段啟用 fielddata(雖然大多數場景下不推薦這么做,因為可能導致內存消耗過大),你可以通過更新映射(mapping)來實現。
以下是如何在 my_field 字段上啟用 fielddata 的示例:
PUT my_index/_mapping{ "properties": { "my_field": { "type": "text", "fielddata": true } }}
注意:更改 fielddata 設置只會影響新的數據,已經索引的數據不會受到更改。如果你想讓更改生效,需要重新索引(reindex)你的數據
另外,一般情況下,建議使用 mapping 中的 keyword 類型來進行聚合、排序或腳本,而不是啟用 text 類型的 fielddata。這是因為 keyword 類型字段默認開啟了 doc values,比在 text 上啟用 fielddata 更加高效且節省內存。
在 Elasticsearch 中,一個字段有可能是 multi-fields(多字段)類型,這意味著同一份數據可以被索引為不同類型的字段。常見的情況就是,一個字段既被索引為 text 類型用于全文搜索,又被索引為 keyword 類型用于精確值搜索、排序和聚合。
當你在一個字段名后面加上 .keyword(例如 field.keyword),這說明你是在引用這個字段的 keyword 子字段。這個 keyword 子字段在索引時并不會被分詞器拆分成單獨的詞條,而是作為一個完整的字符串被存儲。這樣,你就可以對這個字段進行精確值匹配、排序或者聚合操作。
舉例來說,如果你有一個 message 字段并且想要對其進行聚合,你應該使用 message.keyword 而非 message。因為如果你直接對 message 進行聚合,Elasticsearch 就會嘗試對每一個獨立的詞條進行聚合,而不是對整個字段值進行聚合。
如果你的字段沒有 .keyword 子字段,那可能是在定義 mapping 時沒有包含這一部分,或者這個字段的類型本身就是 keyword。
分桶(Bucket)聚合是一種特殊類型的聚合,它將輸入文檔集合中的文檔分配到一個或多個桶中,每個桶都對應于一個鍵(key)。
下面是一些常用的分桶聚合類型:
以下是一個使用 terms 分桶聚合的例子:
假設你有一個包含博客文章的 blog 索引,你想知道每個作者寫了多少篇文章,可以使用以下查詢:
GET /blog/_search{ "size": 0, "aggs": { "authors": { "terms": { "field": "author.keyword" } } }}
在這個查詢中:
Elasticsearch 將返回一個包含每個作者以及他們所寫的文章數量的列表。注意,由于 Elasticsearch 默認只返回前十個桶,如果你的數據中有更多的作者,可能需要設置 size 參數來獲取更多的結果。
histogram 是桶聚合的一種類型,它可以按照指定的間隔將數字字段的值劃分為一系列桶。每個桶代表了這個區間內的所有文檔。
以下是一個例子,我們根據價格字段創建一個間隔為 50 的直方圖:
GET /products/_search{ "size": 0, "aggs" : { "prices" : { "histogram" : { "field" : "price", "interval" : 50 } } }}
在這個例子中,“prices” 是一個 histogram 聚合,它以 50 為間隔將產品的價格劃分為一系列的桶。
在 Elasticsearch 中,指標聚合是對數據進行統計計算的一種方式,例如求和、平均值、最小值、最大值等。以下是一些常用的指標聚合類型:
下面是一個示例,假設我們有一個包含售賣商品的 “sales” 索引,我們想要知道所有銷售記錄中的平均價格,可以使用 avg 聚合如下操作:
GET /sales/_search{ "size": 0, "aggs": { "average_price": { "avg": { "field": "price" } } }}
percentiles 是指標聚合的一種,它用于計算數值字段的百分位數。給定一個列表百分比,Elasticsearch 可以計算每個百分比下的數值。
以下是一個例子,我們計算價格字段的 1st, 5th, 25th, 50th, 75th, 95th, and 99th 百分位數:
GET /products/_search{ "size": 0, "aggs" : { "price_percentiles" : { "percentiles" : { "field" : "price", "percents" : [1, 5, 25, 50, 75, 95, 99] } } }}
在這個例子中,“price_percentiles” 是一個 percentiles 聚合,它計算了價格在各個百分位點的數值。
注意,對于大數據集,計算精確的百分位數可能需要消耗大量資源。因此,Elasticsearch 默認使用一個名為 TDigest 的算法來提供近似的計算結果,同時還能保持內存使用的可控性。
如果你想在 Elasticsearch 中進行去重操作,可以使用 terms 聚合加上 cardinality 聚合。這是一個示例,假設我們有一個包含user_id的 "users" 索引,并且我們想要知道有多少唯一的 user_id:
GET /users/_search{ "size": 0, "aggs": { "distinct_user_ids": { "cardinality": { "field": "user_id.keyword" } } }}
在這個查詢中:
Elasticsearch 將返回一個結果,告訴我們有多少個不同的 user_id。請注意,cardinality 聚合可能并不總是完全精確,特別是對于大型數據集,因為它在內部使用了一種叫做 HyperLogLog 的算法來近似計算基數,這種算法會在保持內存消耗相對較小的情況下提供接近準確的結果。如果你需要完全精確的結果,可能需要考慮其他方法,例如使用腳本或者將數據導出到外部系統進行處理。
在 Elasticsearch 中,管道聚合(pipeline aggregations)是指這樣一種聚合:它以其他聚合的結果作為輸入,并進行進一步處理。
常見的管道聚合包括:
這些都是 bucket 級別的管道聚合,它們會在一組數據桶上操作。
下面給出一個示例,假設我們有一個銷售記錄索引 "sales",每個銷售記錄都有售價 "price" 和銷售日期 "date" 字段。如果我們想要計算每月平均銷售價格,并找出所有月份中平均價格最高的月份,可以使用 date_histogram 聚合加上 avg 以及 max_bucket 聚合來實現:
GET /sales/_search{ "size": 0, "aggs": { "sales_per_month": { "date_histogram": { "field": "date", "calendar_interval": "month" }, "aggs": { "avg_price": { "avg": { "field": "price" } } } }, "max_avg_price": { "max_bucket": { "buckets_path": "sales_per_month>avg_price" } } }}
在這個查詢中:
注意到 "max_avg_price" 中的 "buckets_path": "sales_per_month>avg_price"。buckets_path 參數指定了此管道聚合的輸入來源,> 符號表示路徑層次,即先取 "sales_per_month" 聚合的結果,再取其中的 "avg_price" 聚合的結果作為輸入。
返回的結果中會包含每個月的平均銷售價格,以及所有月份中平均銷售價格的最大值。
嵌套聚合就是在聚合內使用聚合,在 Elasticsearch 中,嵌套聚合通常用于處理 nested 類型的字段。nested 類型允許你將一個文檔中的一組對象作為獨立的文檔進行索引和查詢,這對于擁有復雜數據結構(例如數組或列表中的對象)的場景非常有用。
假設我們有一個 users 索引,每個 user 文檔都有一個 purchases 字段,該字段是一個列出用戶所有購買記錄的數組,每個購買記錄包含 product_id 和 price。如果我們想要找出價格超過 100 的所有產品的 ID,可以使用 nested 聚合:
GET /users/_search{ "size": 0, "aggs": { "all_purchases": { "nested": { "path": "purchases" }, "aggs": { "expensive_purchases": { "filter": { "range": { "purchases.price": { "gt": 100 } } }, "aggs": { "product_ids": { "terms": { "field": "purchases.product_id" } } } } } } }}
在這個查詢中:
返回的結果將包含所有 price 大于 100 的產品的 ID 列表。
請注意,在處理 nested 數據時,你需要確保 mapping 中相應的字段已經被設置為 nested 類型,否則該查詢可能無法按預期工作。
基于查詢結果的聚合:在這種情況下,我們首先執行一個查詢,然后對查詢結果進行聚合。
例如,如果我們要查詢所有包含某關鍵字的文檔,并計算它們的平均價格,可以這樣做:
GET /products/_search{ "query": { "match": { "description": "laptop" } }, "aggs": { "average_price": { "avg": { "field": "price" } } }}
在上述例子中,我們首先通過 match 查詢找到描述中包含 "laptop" 的所有產品,然后對這些產品的價格進行平均值聚合。
基于聚合結果的查詢:這種情況下,我們先執行聚合,然后基于聚合的結果執行過濾操作。
這通常用于在聚合結果中應用一些額外的過濾條件。例如,如果我們想對所有產品進行銷售數量聚合,然后從結果中過濾出銷售數量大于10的產品,可以這樣做:
GET /sales/_search{ "size": 0, "aggs": { "sales_per_product": { "terms": { "field": "product_id" } } }, "post_filter": { "bucket_selector": { "buckets_path": { "salesCount": "sales_per_product._count" }, "script": { "source": "params.salesCount > 10" } } }}
在上述例子中,我們首先執行了一個 terms 聚合,按產品ID匯總銷售記錄。然后我們使用 bucket_selector post-filter 進一步篩選出銷售數量大于10的桶(每個桶對應一個產品)。
(1) count
在 Elasticsearch 中,聚合排序允許你基于某一聚合的結果來對桶進行排序。例如,你可能希望查看銷售量最高的10個產品,可以使用 terms 聚合以及其 size 和 order 參數來實現:
GET /sales/_search{ "size": 0, "aggs": { "top_products": { "terms": { "field": "product_id", "size": 10, "order": { "_count": "desc" } } } }}
在這個例子中,top_products 是一個 terms 聚合,用于按 product_id 對銷售記錄進行分組。
"size": 10 的意思是只返回銷售量最高的前10個產品(即只返回前10個桶)。
"order": { "_count": "desc" } 表示按桶中文檔的數量(也就是銷售量)降序排序。_count 是一個內置的排序鍵,代表桶中文檔的數量。
返回的結果將包含銷售量最高的前10個產品的 ID 列表。
(2) term
_term 在 Elasticsearch 的聚合排序中用來指定按照詞條(即桶的鍵)來排序。
GET /sales/_search{ "size": 0, "aggs": { "products": { "terms": { "field": "product_id", "order": { "_term": "asc" } } } }}
在這個例子中,通過 "order": { "_term": "asc" } 指定了按照 product_id 的值升序排序這些桶。
返回的結果將包含按照 product_id 升序排列的產品 ID 列表,每個產品 ID 對應一個桶,并且每個桶內包含對應產品的銷售記錄。
需要注意的是,在新版本的 Elasticsearch 中(7.0 以后),_term 已經被 key 替代用于排序。
GET /sales/_search{ "size": 0, "aggs" : { "products" : { "terms" : { "field" : "product_id", "order" : { "_key" : "asc" } } } }}
本文鏈接:http://www.www897cc.com/showinfo-26-35572-0.html一起學 Elasticsearch 系列-聚合查詢
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 開發者必備21個Python工具