Elasticsearch聚合查詢概念及字段類型示例
Elasticsearch聚合
Elasticsearch中的聚合是一種以結(jié)構(gòu)化的方式提取和展示數(shù)據(jù)的機制??梢园阉暈镾QL中的GROUP BY語句,但是它更加強大和靈活。
Elasticsearch支持很多類型的聚合,包括:
- Metrics Aggregations:這類聚合基于文檔字段的數(shù)值進行計算并返回一個單一的數(shù)值結(jié)果。例如最大值(max)、最小值(min)、平均值(average)、總和(sum)、統(tǒng)計信息(stats,包含了上述幾種操作),以及其他復(fù)雜的聚合如百分數(shù)(percentiles)、基數(shù)(cardinality)等。
- Bucket Aggregations:這類聚合會創(chuàng)建一組buckets,每個bucket對應(yīng)一個特定的條件或范圍,然后文檔會根據(jù)這些條件或范圍被分類到相應(yīng)的bucket中。常見的包括區(qū)間(range)、日期區(qū)間(date range)、直方圖(histogram)、日期直方圖(date histogram)、地理哈希網(wǎng)格(geohash grid)等。
- Pipeline Aggregations:這類聚合可以基于其他聚合的結(jié)果進行二次計算。比如計算差異、比例、移動平均等。
Elasticsearch的聚合操作支持嵌套,即一個聚合內(nèi)部可以包含別的子聚合,從而實現(xiàn)非常復(fù)雜的數(shù)據(jù)挖掘和統(tǒng)計需求。
聚合(aggs)不同于普通查詢,是目前學(xué)到的第二種大的查詢分類,第一種即“query”,因此在代碼中的第一層嵌
套由“query”變?yōu)榱?ldquo;aggs”。用于進行聚合的字段必須是exact value,分詞字段不可進行聚合,對于text字段如
果需要使用聚合,需要開啟fielddata,但是通常不建議,因為fielddata是將聚合使用的數(shù)據(jù)結(jié)構(gòu)由磁盤
(doc_values)變?yōu)榱硕褍?nèi)存(field_data),大數(shù)據(jù)的聚合操作很容易導(dǎo)致OOM。
doc values 和 fielddata
在 Elasticsearch 中,聚合操作主要依賴于 doc values 或 fielddata 來進行。
- Doc values:對于大多數(shù)字段類型,Elasticsearch 使用 doc values 進行排序和聚合。doc values 是一種在磁盤上的、列式存儲的數(shù)據(jù)結(jié)構(gòu),適用于稀疏字段,也就是字段中有很多不同的值。它們默認開啟,并且不能被禁用。
- Fielddata:對于TEXT字段,doc values 默認是關(guān)閉的,因為文本字段通常包含很多不同的值,使用 doc values 會消耗大量內(nèi)存。這時候,如果需要對文本字段進行聚合或排序,Elasticsearch 使用 fielddata。fielddata 是一個將所有文檔的字段值加載到內(nèi)存的數(shù)據(jù)結(jié)構(gòu),使用它可以使得聚合、排序和腳本運行更快,但代價是消耗更多的內(nèi)存。
當(dāng)執(zhí)行聚合操作時,Elasticsearch 需要訪問所有匹配文檔的字段值。對于非文本字段,默認情況下Elasticsearch 使用 doc values 來實現(xiàn)。對于文本字段,必須首先啟用 fielddata。然而,由于 fielddata 占用大量內(nèi)存,Elasticsearch 默認禁用了它。
對于文本字段,fielddata 默認是禁用的。如果你確實需要對一個文本字段啟用 fielddata(雖然大多數(shù)場景下不推薦這么做,因為可能導(dǎo)致內(nèi)存消耗過大),你可以通過更新映射(mapping)來實現(xiàn)。以下是如何在 my_field
字段上啟用 fielddata 的示例:
PUT my-index/_mapping { "properties": { "my_field": { "type": "text", "fielddata": true } } }
注意,更改 fielddata 設(shè)置只會影響新的數(shù)據(jù),已經(jīng)索引的數(shù)據(jù)不會受到更改。如果你想讓更改生效,需要重新索引(reindex)你的數(shù)據(jù)。
另外,一般情況下,建議你使用 mapping 中的 keyword
類型來進行聚合、排序或腳本,而不是啟用 text
類型的 fielddata。這是因為 keyword
類型字段默認開啟了 doc values,比在 text
上啟用 fielddata 更加高效且節(jié)省內(nèi)存。
multi-fields(多字段)類型
在 Elasticsearch 中,一個字段有可能是 multi-fields(多字段)類型,這意味著同一份數(shù)據(jù)可以被索引為不同類型的字段。常見的情況就是,一個字段既被索引為 text
類型用于全文搜索,又被索引為 keyword
類型用于精確值搜索、排序和聚合。
當(dāng)你在一個字段名后面加上 .keyword
(例如 field.keyword
),這說明你是在引用這個字段的 keyword
子字段。這個 keyword
子字段在索引時并不會被分詞器拆分成單獨的詞條,而是作為一個完整的字符串被存儲。這樣,你就可以對這個字段進行精確值匹配、排序或者聚合操作。
舉例來說,如果你有一個 message
字段并且想要對其進行聚合,你應(yīng)該使用 message.keyword
而非 message
。因為如果你直接對 message
進行聚合,Elasticsearch 就會嘗試對每一個獨立的詞條進行聚合,而不是對整個字段值進行聚合。
如果你的字段沒有 .keyword
子字段,那可能是在定義 mapping 時沒有包含這一部分,或者這個字段的類型本身就是 keyword
。
聚合分類
- 分桶聚合(Bucket agregations):類比SQL中的group by的作用,主要用于統(tǒng)計不同類型數(shù)據(jù)的數(shù)量。
- 指標聚合(Metrics agregations):主要用于最大值、最小值、平均值、字段之和等指標的統(tǒng)計。
- 管道聚合(Pipeline agregations):用于對聚合的結(jié)果進行二次聚合,如要統(tǒng)計綁定數(shù)量最多的標簽bucket,就是要先按照標簽進行分桶,再在分桶的結(jié)果上計算最大值。
分桶聚合
分桶(Bucket)聚合是一種特殊類型的聚合,它將輸入文檔集合中的文檔分配到一個或多個桶中,每個桶都對應(yīng)于一個鍵(key)。
下面是一些常用的分桶聚合類型:
terms
:基于文檔中某個字段的值,將文檔分組到各個桶中。date_histogram
:基于日期字段,將文檔按照指定的時間間隔分組到各個桶中。histogram
:基于數(shù)值字段,將文檔按照指定的數(shù)值范圍分組到各個桶中。range
:根據(jù)設(shè)置的范圍,將數(shù)據(jù)分為不同的桶。
以下是一個使用 terms
分桶聚合的例子:
假設(shè)你有一個包含博客文章的 blog
索引,你想知道每個作者寫了多少篇文章,可以使用以下查詢:
GET /blog/_search { "size": 0, "aggs": { "authors": { "terms": { "field": "author.keyword" } } } }
在這個查詢中:
size: 0
表示我們只對聚合結(jié)果感興趣,不需要返回任何具體的搜索結(jié)果。"aggs"
(或者"aggregations"
) 塊定義了我們的聚合。"authors"
是我們自己為這個聚合命名的標簽,你可以用任何你喜歡的標簽名。"terms": { "field": "author.keyword" }
定義了我們要進行聚合的方式和字段。這里,我們告訴 Elasticsearch 使用terms
聚合,并且使用author.keyword
字段的值作為分桶的依據(jù)。
Elasticsearch 將返回一個包含每個作者以及他們所寫的文章數(shù)量的列表。注意,由于 Elasticsearch 默認只返回前十個桶,如果你的數(shù)據(jù)中有更多的作者,可能需要設(shè)置 size
參數(shù)來獲取更多的結(jié)果。
Histogram 聚合
histogram
是一個類型的桶聚合,它可以按照指定的間隔將數(shù)字字段的值劃分為一系列桶。每個桶代表了這個區(qū)間內(nèi)的所有文檔。
以下是一個例子,我們根據(jù)價格字段創(chuàng)建一個間隔為 50 的直方圖:
GET /products/_search { "size": 0, "aggs" : { "prices" : { "histogram" : { "field" : "price", "interval" : 50 } } } }
在這個例子中,“prices” 是一個 histogram 聚合,它以 50 為間隔將產(chǎn)品的價格劃分為一系列的桶。
指標聚合
在 Elasticsearch 中,指標聚合是對數(shù)據(jù)進行統(tǒng)計計算的一種方式,例如求和、平均值、最小值、最大值等。以下是一些常用的指標聚合類型:
avg
:計算字段的平均值。sum
:計算字段的總和。min
:查找字段的最小值。max
:查找字段的最大值。count
:計算匹配文檔的數(shù)量。stats
:提供了 count、sum、min、max 和 avg 的基本統(tǒng)計。
下面是一個示例,假設(shè)我們有一個包含售賣商品的 “sales” 索引,我們想要知道所有銷售記錄中的平均價格,可以使用 avg
聚合如下操作:
GET /sales/_search { "size": 0, "aggs": { "average_price": { "avg": { "field": "price" } } } }
在這個查詢中:
"size": 0
表示我們只對聚合結(jié)果感興趣,不需要返回任何具體的搜索結(jié)果。"aggs"
(或者"aggregations"
) 塊定義了我們的聚合。"average_price"
是我們自己為這個聚合命名的標簽,可以用任何你喜歡的標簽名。"avg": { "field": "price" }
定義了我們執(zhí)行的聚合類型以及對哪個字段進行聚合。在這里,我們告訴 Elasticsearch 使用avg
聚合,并且對price
字段的值進行計算。Elasticsearch 將返回一個包含所有銷售記錄平均價格的結(jié)果。
Percentiles 聚合
percentiles
是指標聚合的一種,它用于計算數(shù)值字段的百分位數(shù)。給定一個列表百分比,Elasticsearch 可以計算每個百分比下的數(shù)值。
以下是一個例子,我們計算價格字段的 1st, 5th, 25th, 50th, 75th, 95th, and 99th 百分位數(shù):
GET /products/_search { "size": 0, "aggs" : { "price_percentiles" : { "percentiles" : { "field" : "price", "percents" : [1, 5, 25, 50, 75, 95, 99] } } } }
在這個例子中,“price_percentiles” 是一個 percentiles 聚合,它計算了價格在各個百分位點的數(shù)值。
注意,對于大數(shù)據(jù)集,計算精確的百分位數(shù)可能需要消耗大量資源。因此,Elasticsearch 默認使用一個名為 TDigest
的算法來提供近似的計算結(jié)果,同時還能保持內(nèi)存使用的可控性。
cardinality聚合
如果你想在 Elasticsearch 中進行去重操作,可以使用 terms
聚合加上 cardinality
聚合。這是一個示例,假設(shè)我們有一個包含user_id的 "users" 索引,并且我們想要知道有多少唯一的 user_id:
GET /users/_search { "size": 0, "aggs": { "distinct_user_ids": { "cardinality": { "field": "user_id.keyword" } } } }
在這個查詢中:
"distinct_user_ids"
是我們自己為這個聚合命名的標簽。"cardinality": { "field": "user_id.keyword" }
使用了cardinality
聚合,該聚合會返回指定字段(在這里是user_id.keyword
)的不同值的數(shù)量。
Elasticsearch 將返回一個結(jié)果,告訴我們有多少個不同的 user_id。請注意,cardinality
聚合可能并不總是完全精確,特別是對于大型數(shù)據(jù)集,因為它在內(nèi)部使用了一種叫做 HyperLogLog 的算法來近似計算基數(shù),這種算法會在保持內(nèi)存消耗相對較小的情況下提供接近準確的結(jié)果。如果你需要完全精確的結(jié)果,可能需要考慮其他方法,例如使用腳本或者將數(shù)據(jù)導(dǎo)出到外部系統(tǒng)進行處理。
管道聚合
在 Elasticsearch 中,管道聚合(pipeline aggregations)是指這樣一種聚合:它以其他聚合的結(jié)果作為輸入,并進行進一步處理。常見的管道聚合包括:
avg_bucket
sum_bucket
min_bucket
max_bucket
stats_bucket
extended_stats_bucket
percentiles_bucket
這些都是 bucket 級別的管道聚合,它們會在一組數(shù)據(jù)桶上操作。
下面給出一個示例,假設(shè)我們有一個銷售記錄索引 "sales",每個銷售記錄都有售價 "price" 和銷售日期 "date" 字段。如果我們想要計算每月平均銷售價格,并找出所有月份中平均價格最高的月份,可以使用 date_histogram 聚合加上 avg 以及 max_bucket 聚合來實現(xiàn):
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" } } } }
在這個查詢中:
"sales_per_month"
是一個按月聚合銷售記錄的 date_histogram 聚合。"avg_price"
是一個嵌套在"sales_per_month"
下的 avg 聚合,用于計算每月的平均銷售價格。"max_avg_price"
是一個 max_bucket 聚合,它會找出"sales_per_month"
中所有子桶的"avg_price"
最大值。
注意到 "max_avg_price"
中的 "buckets_path": "sales_per_month>avg_price"
。buckets_path
參數(shù)指定了此管道聚合的輸入來源,>
符號表示路徑層次,即先取 "sales_per_month"
聚合的結(jié)果,再取其中的 "avg_price"
聚合的結(jié)果作為輸入。
返回的結(jié)果中會包含每個月的平均銷售價格,以及所有月份中平均銷售價格的最大值。
嵌套聚合
嵌套聚合就是在聚合內(nèi)使用聚合,在 Elasticsearch 中,嵌套聚合通常用于處理 nested 類型的字段。nested 類型允許你將一個文檔中的一組對象作為獨立的文檔進行索引和查詢,這對于擁有復(fù)雜數(shù)據(jù)結(jié)構(gòu)(例如數(shù)組或列表中的對象)的場景非常有用。
假設(shè)我們有一個 users
索引,每個 user 文檔都有一個 purchases
字段,該字段是一個列出用戶所有購買記錄的數(shù)組,每個購買記錄包含 product_id
和 price
。如果我們想要找出價格超過 100 的所有產(chǎn)品的 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" } } } } } } } }
在這個查詢中:
"all_purchases"
是一個 nested 聚合,指定了 nested 對象的路徑purchases
。"expensive_purchases"
是一個嵌套在"all_purchases"
下的 filter 聚合,它會過濾出price
大于 100 的購買記錄。"product_ids"
是一個嵌套在"expensive_purchases"
下的 terms 聚合,它會提取出所有滿足條件的product_id
。
返回的結(jié)果將包含所有 price
大于 100 的產(chǎn)品的 ID 列表。
請注意,在處理 nested 數(shù)據(jù)時,你需要確保 mapping 中相應(yīng)的字段已經(jīng)被設(shè)置為 nested 類型,否則該查詢可能無法按預(yù)期工作。
基于查詢結(jié)果和聚合 & 基于聚合結(jié)果的查詢
基于查詢結(jié)果的聚合:在這種情況下,我們首先執(zhí)行一個查詢,然后對查詢結(jié)果進行聚合。例如,如果我們要查詢所有包含某關(guān)鍵字的文檔,并計算它們的平均價格,可以這樣做:
GET /products/_search { "query": { "match": { "description": "laptop" } }, "aggs": { "average_price": { "avg": { "field": "price" } } } }
在上述例子中,我們首先通過 match
查詢找到描述中包含 "laptop" 的所有產(chǎn)品,然后對這些產(chǎn)品的價格進行平均值聚合。
基于聚合結(jié)果的查詢(Post-Filter):這種情況下,我們先執(zhí)行聚合,然后基于聚合的結(jié)果執(zhí)行過濾操作。這通常用于在聚合結(jié)果中應(yīng)用一些額外的過濾條件。例如,如果我們想對所有產(chǎn)品進行銷售數(shù)量聚合,然后從結(jié)果中過濾出銷售數(shù)量大于10的產(chǎn)品,可以這樣做:
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" } } } }
在上述例子中,我們首先執(zhí)行了一個 terms
聚合,按產(chǎn)品ID匯總銷售記錄。然后我們使用 bucket_selector
post-filter 進一步篩選出銷售數(shù)量大于10的桶(每個桶對應(yīng)一個產(chǎn)品)。
聚合排序
_count
在 Elasticsearch 中,聚合排序允許你基于某一聚合的結(jié)果來對桶進行排序。例如,你可能希望查看銷售量最高的10個產(chǎn)品,可以使用 terms
聚合以及其 size
和 order
參數(shù)來實現(xiàn):
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個產(chǎn)品(即只返回前10個桶)。"order": { "_count": "desc" }
表示按桶中文檔的數(shù)量(也就是銷售量)降序排序。_count
是一個內(nèi)置的排序鍵,代表桶中文檔的數(shù)量。
返回的結(jié)果將包含銷售量最高的前10個產(chǎn)品的 ID 列表。
需要注意的是,由于 Elasticsearch 默認會對桶進行優(yōu)化,所以在使用 size
參數(shù)時可能無法得到完全準確的結(jié)果。如果需要更精確的結(jié)果,可以在請求中設(shè)置 "size": 0
,然后使用 composite
聚合來分頁獲取所有結(jié)果。
_term
_term
在 Elasticsearch 的聚合排序中用來指定按照詞條(即桶的鍵)來排序。
GET /sales/_search { "size": 0, "aggs": { "products": { "terms": { "field": "product_id", "order": { "_term": "asc" } } } } }
在這個例子中,通過 "order": { "_term": "asc" }
指定了按照 product_id
的值升序排序這些桶。
返回的結(jié)果將包含按照 product_id
升序排列的產(chǎn)品 ID 列表,每個產(chǎn)品 ID 對應(yīng)一個桶,并且每個桶內(nèi)包含對應(yīng)產(chǎn)品的銷售記錄。
需要注意的是,在新版本的 Elasticsearch 中(7.0 以后),_term
已經(jīng)被 key
替代用于排序。
GET /sales/_search { "size": 0, "aggs" : { "products" : { "terms" : { "field" : "product_id", "order" : { "_key" : "asc" } } } } }
以上就是Elasticsearch聚合查詢概念及字段類型示例的詳細內(nèi)容,更多關(guān)于Elasticsearch聚合查詢的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解JAVA中的Collection接口和其主要實現(xiàn)的類
這篇文章主要介紹了JAVA中的Collection接口和其主要實現(xiàn)的類,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03springboot 啟動時初始化數(shù)據(jù)庫的步驟
這篇文章主要介紹了springboot 啟動時初始化數(shù)據(jù)庫的步驟,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2021-01-01Spring?boot?整合RabbitMQ實現(xiàn)通過RabbitMQ進行項目的連接
RabbitMQ是一個開源的AMQP實現(xiàn),服務(wù)器端用Erlang語言編寫,支持多種客戶端,這篇文章主要介紹了Spring?boot?整合RabbitMQ實現(xiàn)通過RabbitMQ進行項目的連接,需要的朋友可以參考下2022-10-10Jackson2的JsonSchema實現(xiàn)java實體類生成json方式
這篇文章主要介紹了Jackson2的JsonSchema實現(xiàn)java實體類生成json,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11