聚合查詢(Metric聚合)
說明
:該博客對於的Elasticsearch 的版本為7.3。
在Mysql中,我們可以獲取一組數據的 最大值(Max)、最小值(Min)。同樣我們能夠對這組數據進行 分組(Group)。那么對於Elasticsearch中
我們也可以實現同樣的功能,聚合有關資料官方文檔內容較多,這里大概分兩篇博客寫這個有關Elasticsearch聚合。
官方對聚合有四個關鍵字: Metric(指標)
、Bucketing(桶)
、Matrix(矩陣)
、Pipeline(管道)
。
一、聚合概念
1. ES聚合分析是什么?
概念
Elasticsearch除全文檢索功能外提供的針對Elasticsearch數據做統計分析的功能。它的實時性高,所有的計算結果都是即時返回。
Elasticsearch將聚合分析主要分為如下4類:
Metric(指標): 指標分析類型,如計算最大值、最小值、平均值等等 (對桶內的文檔進行聚合分析的操作)
Bucket(桶): 分桶類型,類似SQL中的GROUP BY語法 (滿足特定條件的文檔的集合)
Pipeline(管道): 管道分析類型,基於上一級的聚合分析結果進行在分析
Matrix(矩陣): 矩陣分析類型(聚合是一種面向數值型的聚合,用於計算一組文檔字段中的統計信息)
2.ES聚合分析查詢的寫法
在查詢請求體中以aggregations節點按如下語法定義聚合分析:
"aggregations" : {
"<aggregation_name>" : { <!--聚合的名字 -->
"<aggregation_type>" : { <!--聚合的類型 -->
<aggregation_body> <!--聚合體:對哪些字段進行聚合 -->
}
[,"meta" : { [<meta_data_body>] } ]? <!--元 -->
[,"aggregations" : { [<sub_aggregation>]+ } ]? <!--在聚合里面在定義子聚合 -->
}
[,"<aggregation_name_2>" : { ... } ]* <!--聚合的名字 -->
}
說明
:aggregations 也可簡寫為 aggs
3、指標(metric)和 桶(bucket)
雖然Elasticsearch有四種聚合方式,但在一般實際開發中,用到的比較多的就是Metric和Bucket。
(1) 桶(bucket)
a、簡單來說桶就是滿足特定條件的文檔的集合。
b、當聚合開始被執行,每個文檔里面的值通過計算來決定符合哪個桶的條件,如果匹配到,文檔將放入相應的桶並接着開始聚合操作。
c、桶也可以被嵌套在其他桶里面。
(2)指標(metric)
a、桶能讓我們划分文檔到有意義的集合,但是最終我們需要的是對這些桶內的文檔進行一些指標的計算。分桶是一種達到目的地的手段:它提供了一種給文檔分組的方法來讓
我們可以計算感興趣的指標。
b、大多數指標是簡單的數學運算(如:最小值、平均值、最大值、匯總),這些是通過文檔的值來計算的。
二、指標(Metric)詳解
官網
: 指標聚合官網文檔:Metric
Metric聚合分析分為單值分析和多值分析兩類:
#1、單值分析,只輸出一個分析結果
min,max,avg,sum,cardinality
#2、多值分析,輸出多個分析結果
stats,extended_stats,percentile,percentile_rank,top hits
1、Avg(平均值)
計算從聚合文檔中提取的數值的平均值。
POST /exams/_search?size=0
{
"aggs" : {
"avg_grade" : { "avg" : { "field" : "grade" } }
}
}
2、Max(最大值)
計算從聚合文檔中提取的數值的最大值。
POST /sales/_search?size=0
{
"aggs" : {
"max_price" : { "max" : { "field" : "price" } }
}
}
3、Min(最小值)
計算從聚合文檔中提取的數值的最小值。
POST /sales/_search?size=0
{
"aggs" : {
"min_price" : { "min" : { "field" : "price" } }
}
}
4、Sum(總和)
計算從聚合文檔中提取的數值的總和。
POST /sales/_search?size=0
{
"query" : {
"constant_score" : {
"filter" : {
"match" : { "type" : "hat" }
}
}
},
"aggs" : {
"hat_prices" : { "sum" : { "field" : "price" } }
}
}
5、 Cardinality(唯一值)
cardinality 求唯一值,即不重復的字段有多少(相當於mysql中的distinct)
POST /sales/_search?size=0
{
"aggs" : {
"type_count" : {
"cardinality" : {
"field" : "type"
}
}
}
}
6、Stats
stats 統計,請求后會直接顯示多種聚合結果
POST /exams/_search?size=0
{
"aggs" : {
"grades_stats" : { "stats" : { "field" : "grade" } }
}
}
返回
{
...
"aggregations": {
"grades_stats": {
"count": 2,
"min": 50.0,
"max": 100.0,
"avg": 75.0,
"sum": 150.0
}
}
}
7、Percentiles
對指定字段的值按從小到大累計每個值對應的文檔數的占比,返回指定占比比例對應的值。
1)默認取百分比
默認按照[ 1, 5, 25, 50, 75, 95, 99 ]來統計
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time"
}
}
}
}
返回結果可以理解為:占比為50%的文檔的age值 <= 445,或反過來:age<=445的文檔數占總命中文檔數的50%
{
...
"aggregations": {
"load_time_outlier": {
"values" : {
"1.0": 5.0,
"5.0": 25.0,
"25.0": 165.0,
"50.0": 445.0,
"75.0": 725.0,
"95.0": 945.0,
"99.0": 985.0
}
}
}
}
2)指定分位值
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time",
"percents" : [95, 99, 99.9]
}
}
}
}
Keyed Response
默認情況下,keyed標志設置為true,它將唯一的字符串鍵與每個存儲桶相關聯,並將范圍作為哈希而不是數組返回。
GET latency/_search
{
"size": 0,
"aggs": {
"load_time_outlier": {
"percentiles": {
"field": "load_time",
"keyed": false
}
}
}
}
返回結果
{
...
"aggregations": {
"load_time_outlier": {
"values": [
{
"key": 1.0,
"value": 5.0
},
{
"key": 5.0,
"value": 25.0
},
{
"key": 25.0,
"value": 165.0
},
{
"key": 50.0,
"value": 445.0
},
{
"key": 75.0,
"value": 725.0
},
{
"key": 95.0,
"value": 945.0
},
{
"key": 99.0,
"value": 985.0
}
]
}
}
}
8、 Percentile Ranks
上面是通過百分比求文檔值,這里通過文檔值求百分比。
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_ranks" : {
"percentile_ranks" : {
"field" : "load_time",
"values" : [500, 600]
}
}
}
}
返回結果
{
...
"aggregations": {
"load_time_ranks": {
"values" : {
"500.0": 55.1,
"600.0": 64.0
}
}
}
}
結果說明
:時間小於500的文檔占比為55.1%,時間小於600的文檔占比為64%,
9、Top Hits
一般用於分桶后獲取該桶內匹配前n的文檔列表
POST /sales/_search?size=0
{
"aggs": {
"top_tags": {
"terms": {
"field": "type", #根據type進行分組 每組顯示前3個文檔
"size": 3
},
"aggs": {
"top_sales_hits": {
"top_hits": {
"sort": [
{
"date": {
"order": "desc" #按照時間進行倒敘排序
}
}
],
"_source": {
"includes": [ "date", "price" ] #只顯示文檔指定字段
},
"size" : 1
}
}
}
}
}
}
三、示例
下面會針對上面官方文檔的例子進行舉例說明。
1、添加測試數據
1)創建索引
DELETE /employees
PUT /employees/
{
"mappings" : {
"properties" : {
"age" : {
"type" : "integer"
},
"gender" : {
"type" : "keyword"
},
"job" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 50
}
}
},
"name" : {
"type" : "keyword"
},
"salary" : {
"type" : "integer"
}
}
}
}
2)添加數據
添加10條數據,每條數據包含:姓名、年齡、工作、性別、薪資
PUT /employees/_bulk
{ "index" : { "_id" : "1" } }
{ "name" : "Emma","age":32,"job":"Product Manager","gender":"female","salary":35000 }
{ "index" : { "_id" : "2" } }
{ "name" : "Underwood","age":41,"job":"Dev Manager","gender":"male","salary": 50000}
{ "index" : { "_id" : "3" } }
{ "name" : "Tran","age":25,"job":"Web Designer","gender":"male","salary":18000 }
{ "index" : { "_id" : "4" } }
{ "name" : "Rivera","age":26,"job":"Web Designer","gender":"female","salary": 22000}
{ "index" : { "_id" : "5" } }
{ "name" : "Rose","age":25,"job":"QA","gender":"female","salary":18000 }
{ "index" : { "_id" : "6" } }
{ "name" : "Lucy","age":31,"job":"QA","gender":"female","salary": 25000}
{ "index" : { "_id" : "7" } }
{ "name" : "Byrd","age":27,"job":"QA","gender":"male","salary":20000 }
{ "index" : { "_id" : "8" } }
{ "name" : "Foster","age":27,"job":"Java Programmer","gender":"male","salary": 20000}
{ "index" : { "_id" : "9" } }
{ "name" : "Gregory","age":32,"job":"Java Programmer","gender":"male","salary":22000 }
{ "index" : { "_id" : "10" } }
{ "name" : "Bryant","age":20,"job":"Java Programmer","gender":"male","salary": 9000}
2、求薪資最低值
POST employees/_search
{
"size": 0,
"aggs": {
"min_salary": {
"min": {
"field":"salary"
}
}
}
}
返回
3、找到最低、最高和平均工資
POST employees/_search
{
"size": 0,
"aggs": {
"max_salary": {
"max": {
"field": "salary"
}
},
"min_salary": {
"min": {
"field": "salary"
}
},
"avg_salary": {
"avg": {
"field": "salary"
}
}
}
}
4、一個聚合,輸出多值
POST employees/_search
{
"size": 0,
"aggs": {
"stats_salary": {
"stats": {
"field":"salary"
}
}
}
}
返回
5、求一共有多少工作類型
POST employees/_search
{
"size": 0,
"aggs": {
"cardinate": {
"cardinality": {
"field": "job.keyword"
}
}
}
}
返回
注意
我們需要把job的類型為keyword
類型,這樣就不會分詞,把它當成一個整體。
6、查看中位數的薪資
POST employees/_search
{
"size": 0,
"aggs": {
"load_time_outlier": {
"percentiles": {
"field": "salary",
"percents" : [50, 99],
"keyed": false
}
}
}
}
返回
發現這些工作的中位數是:21000元。
7、取每個工作類型薪資最高的數據
多層嵌套
根據工作類型分桶,然后按照性別分桶,計算每個桶中工資的最高的薪資。
POST employees/_search
{
"size": 0,
"aggs": {
"Job_gender_stats": {
"terms": {
"field": "job.keyword"
},
"aggs": {
"gender_stats": {
"terms": {
"field": "gender"
},
"aggs": {
"salary_stats": {
"max": {
"field": "salary"
}
}
}
}
}
}
}
}
返回
參考
1、Elasticsearch核心技術與實戰---阮一鳴(eBay Pronto平台技術負責人
我相信,無論今后的道路多么坎坷,只要抓住今天,遲早會在奮斗中嘗到人生的甘甜。抓住人生中的一分一秒,勝過虛度中的一月一年!(12)