ElasticSearch 聚合搜索總結

ES的聚合項目:有相關基於ElasticSearch5.6v Java API 的各種用法

源碼地址: GitHub

聚合概念

聚合(Aggregations)也擁有一種可組合(Composable)的語法:獨立的功能單元可以被混合在一起來滿足你的需求。這意味着需要學習的基本概念雖然不多,但是它們的組合方式是幾近無窮的。

爲了掌握聚合,你只需要瞭解兩個主要概念:

Buckets(桶):

滿足某個條件的文檔集合。

Metrics(指標):

爲某個桶中的文檔計算得到的統計信息。

就是這樣!每個聚合只是簡單地由一個或者多個桶,零個或者多個指標組合而成。可以將它粗略地轉換爲SQL:

SELECT COUNT(color) 
FROM table
GROUP BY color

以上的COUNT(color)就相當於指標。GROUP BY color則相當於

桶和SQL中的組(Grouping)擁有相似的概念,而指標則與COUNT(),SUM(),MAX()等相似。

桶(Buckets)

一個桶就是滿足特定條件的一個文檔集合:

  • 一名員工要麼屬於男性桶,或者女性桶。
  • 城市Albany屬於New York州這個桶。
  • 日期2014-10-28屬於十月份這個桶。

指標(Metrics)

桶能夠讓我們對文檔進行有意義的劃分,但是最終我們還是需要對每個桶中的文檔進行某種指標計算。分桶是達到最終目的的手段:提供了對文檔進行劃分的方法,從而讓你能夠計算需要的指標。

多數指標僅僅是簡單的數學運算(比如,min,mean,max以及sum),它們使用文檔中的值進行計算。在實際應用中,指標能夠讓你計算例如平均薪資,最高出售價格,或者百分之95的查詢延遲。

將兩者結合起來

一個聚合就是一些桶和指標的組合。一個聚合可以只有一個桶,或者一個指標,或者每樣一個。在桶中甚至可以有多個嵌套的桶。比如,我們可以將文檔按照其所屬國家進行分桶,然後對每個桶計算其平均薪資(一個指標)。

因爲桶是可以嵌套的,我們能夠實現一個更加複雜的聚合操作:

  1. 將文檔按照國家進行分桶。(桶)
  2. 然後將每個國家的桶再按照性別分桶。(桶)
  3. 然後將每個性別的桶按照年齡區間進行分桶。(桶)
  4. 最後,爲每個年齡區間計算平均薪資。(指標)

此時,就能夠得到每個<國家,性別,年齡>組合的平均薪資信息了。它可以通過一個請求,一次數據遍歷來完成!

聚合的測試數據(Aggregation Test-Drive)

讓我們從一個例子開始。我們會建立一個也許對汽車交易商有所用處的聚合。數據是關於汽車交易的:汽車型號,製造商,銷售價格,銷售時間以及一些其他的相關數據。

首先,通過批量索引(Bulk-Index)來添加一些數據:

PUT http://node1:9200/cars
{
	"mappings":{
		"transactions":{
			"properties":{
				"price":{
					"type":"integer"
				},
				"color":{
					"type":"keyword"
				},
				"make":{
					"type":"keyword"
				},
				"sold":{
					"type":"date"
				}
			}
		}
	}
}
或者===========================================================
{
	"mappings":{
		"transactions":{
			"properties":{
				"price":{
					"type":"integer"
				},
				"color":{
					"type":"text",
					"fielddata":true
				},
				"make":{
					"type":"text",
					"fielddata":true
				},
				"sold":{
					"type":"date"
				}
			}
		}
	}
}



POST http://node1:9200/cars/transactions/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "red", "make" : "honda", "sold" : "2014-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "green", "make" : "ford", "sold" : "2014-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "blue", "make" : "toyota", "sold" : "2014-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "green", "make" : "toyota", "sold" : "2014-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "red", "make" : "bmw", "sold" : "2014-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "blue", "make" : "ford", "sold" : "2014-02-12" }

因爲我們並不關心搜索結果,所以size:0 。聚合工作在頂層的aggs參數下(當然你也可以使用更長的aggregations)。 然後給這個聚合起了一個名字:colors。 最後,我們定義了一個terms類型的桶,它針對color字段。

聚合是以搜索結果爲上下文而執行的,這意味着它是搜索請求中的另一個頂層參數(Top-level Parameter)。聚合可以和查詢同時使用,這一點我們在後續的範圍聚合(Scoping Aggregations)中介紹。

接下來我們爲聚合起一個名字。命名規則是有你決定的; 聚合的響應會被該名字標記,因此在應用中你就能夠根據名字來得到聚合結果,並對它們進行操作了。

然後,我們開始定義聚合本身。比如,我們定義了一個terms類型的桶。terms桶會動態地爲每一個它遇到的不重複的詞條創建一個新的桶。因爲我們針對的是color字段,那麼terms桶會動態地爲每種顏色創建一個新桶。

如果執行出現報錯,搜了一下應該是5.x後對排序,聚合這些操作用單獨的數據結構(fielddata)緩存到內存裏了,需要單獨開啓,簡單來說就是在聚合前執行如下操作

讓我們執行該聚合來看看其結果:

{
    "took": 27,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "colors": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "red",
                    "doc_count": 4
                },
                {
                    "key": "green",
                    "doc_count": 2
                },
                {
                    "key": "blue",
                    "doc_count": 1
                }
            ]
        }
    }
}

因爲我們使用的size爲0,所以沒有搜索結果被返回。 每個桶中的key對應的是在color字段中找到的不重複的詞條。它同時也包含了一個doc_count,用來表示包含了該詞條的文檔數量。

響應包含了一個桶列表,每個桶都對應着一個不重複的顏色(比如,紅色或者綠色)。每個桶也包含了“掉入”該桶中的文檔數量。比如,有4輛紅色的車。

前面的例子是完全實時(Real-Time)的:如果文檔是可搜索的,那麼它們就能夠被聚合。這意味着你能夠將拿到的聚合結果置入到一個圖形庫中來生成實時的儀表板(Dashboard)。一旦你賣出了一臺銀色汽車,在圖形上關於銀色汽車的統計數據就會被動態地更新。

添加一個指標(Metric)

從前面的例子中,我們可以知道每個桶中的文檔數量。但是,通常我們的應用會需要基於那些文檔的更加複雜的指標(Metric)。比如,每個桶中的汽車的平均價格是多少?

爲了得到該信息,我們得告訴ES需要爲哪些字段計算哪些指標。這需要將指標嵌套到桶中。指標會基於桶中的文檔的值來計算相應的統計信息。

讓我們添加一個計算平均值的指標:

GET /cars/transactions/_search
{
    "size": 0,
    "aggs" : { 
        "colors" : { 
            "terms" : {
              "field" : "color" 
            },
            "aggs":{
            	"avg_price":{
            		"avg":{
            			"field":"price"
            		}
            	}
            }
        }
    }
}

我們添加了一個新的aggs層級來包含該指標。然後給該指標起了一個名字:avg_price。最後定義了該指標作用的字段爲price。

正如你所看到的,我們向前面的例子中添加了一個新的aggs層級。這個新的聚合層級能夠讓我們將avg指標嵌套在terms桶中。這意味着我們能爲每種顏色都計算一個平均值。

同樣的,我們需要給指標起一個名(avg_price)來讓我們能夠在將來得到其值。最後,我們指定了指標本身(avg)以及該指標作用的字段(price):

{
    "took": 29,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "colors": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "red",
                    "doc_count": 4,
                    "avg_price": {
                        "value": 32500
                    }
                },
                {
                    "key": "green",
                    "doc_count": 2,
                    "avg_price": {
                        "value": 21000
                    }
                },
                {
                    "key": "blue",
                    "doc_count": 1,
                    "avg_price": {
                        "value": 15000
                    }
                }
            ]
        }
    }
}

桶定義及指標定義都是通過aggs來進行的 但是定義關鍵字不同,一個是terms桶定義 ,一個是avg等指標定義符

現在,在響應中多了一個avg_price元素。

儘管得到的響應只是稍稍有些變化,但是獲得的數據增加的了許多。之前我們只知道有4輛紅色汽車。現在我們知道了紅色汽車的平均價格是32500刀。這些數據你可以直接插入到報表中。

桶中的桶(Buckets inside Buckets)

桶中桶,桶必須有自己的名字,桶中的指標也必須有自己的名字。

當你開始使用不同的嵌套模式時,聚合強大的能力纔會顯現出來。在前面的例子中,我們已經知道了如何將一個指標嵌套進一個桶的,它的功能已經十分強大了。

但是真正激動人心的分析功能來源於嵌套在其它桶中的桶。現在,讓我們來看看如何找到每種顏色的汽車的製造商分佈信息:

POST http://node1:9200/cars/transactions/_search
{
   "aggs": 
   {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": 
         {
            "avg_price": { 
               "avg": {
                  "field": "price"
               }
            },
            "make": { 
                "terms": {
                    "field": "make" 
                }
            }
         } 
      }
   },
   "size": 0
}

此時發生了一些有意思的事情。首先,你會注意到前面的avg_price指標完全沒有變化。一個聚合的每個層級都能夠擁有多個指標或者桶。avg_price指標告訴了我們每種汽車顏色的平均價格。爲每種顏色創建的桶和指標是各自獨立的。

這個性質對你的應用而言是很重要的,因爲你經常需要收集一些互相關聯卻又完全不同的指標。聚合能夠讓你對數據遍歷一次就得到所有需要的信息。

另外一件重要的事情是添加了新聚合make,它是一個terms類型的桶(嵌套在名爲colors的terms桶中)。這意味着我們會根據數據集創建不重複的(color, make)組合。

每個桶中添加桶 或者 計算所在桶的集合函數 必須在與桶名稱下一級聲明 aggs(同級有所在桶的terms聲明) ,然後再aggs下級定義 桶的名稱 或者 函數的名稱, 也可以在aggs 下級同時進行桶中桶的定義 和 桶中函數的定義。如上面例子:

在colors桶中添加桶make:首先在colors 中聲明aggs ,然後在aggs 聲明新的桶的名稱make,make下級利用terms定義該桶按什麼分。

讓我們來看看得到的響應:

{
    "took": 26,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "colors": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "red",
                    "doc_count": 4,
                    "avg_price": {
                        "value": 32500
                    },
                    "make": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "honda",
                                "doc_count": 3
                            },
                            {
                                "key": "bmw",
                                "doc_count": 1
                            }
                        ]
                    }
                },
                {
                    "key": "green",
                    "doc_count": 2,
                    "avg_price": {
                        "value": 21000
                    },
                    "make": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "ford",
                                "doc_count": 1
                            },
                            {
                                "key": "toyota",
                                "doc_count": 1
                            }
                        ]
                    }
                },
                {
                    "key": "blue",
                    "doc_count": 1,
                    "avg_price": {
                        "value": 15000
                    },
                    "make": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "toyota",
                                "doc_count": 1
                            }
                        ]
                    }
                }
            ]
        }
    }
}

該響應告訴了我們如下信息:

有4輛紅色汽車。
紅色汽車的平均價格是32500美刀。
紅色汽車中的3輛是Honda,1輛是BMW。

最後的一個修改(One Final Modification)

爲每個製造商添加兩個指標來計算最低和最高價格:

POST http://node1:9200/cars/transactions/_search
{
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": {
            "avg_price": { "avg": { "field": "price" }
            },
            "make" : {
                "terms" : {
                    "field" : "make"
                },
                "aggs" : { 
                    "min_price" : { "min": { "field": "price"} }, 
                    "max_price" : { "max": { "field": "price"} } 
                }
            }
         }
      }
   },
   "size": 0
}

我們需要添加另一個aggs層級來進行對min和max的嵌套。 
得到的響應如下:

{
    "took": 90,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "colors": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "red",
                    "doc_count": 4,
                    "avg_price": {
                        "value": 32500
                    },
                    "make": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "honda",
                                "doc_count": 3,
                                "max_price": {
                                    "value": 20000
                                },
                                "min_price": {
                                    "value": 10000
                                }
                            },
                            {
                                "key": "bmw",
                                "doc_count": 1,
                                "max_price": {
                                    "value": 80000
                                },
                                "min_price": {
                                    "value": 80000
                                }
                            }
                        ]
                    }
                },
                {
                    "key": "green",
                    "doc_count": 2,
                    "avg_price": {
                        "value": 21000
                    },
                    "make": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "ford",
                                "doc_count": 1,
                                "max_price": {
                                    "value": 30000
                                },
                                "min_price": {
                                    "value": 30000
                                }
                            },
                            {
                                "key": "toyota",
                                "doc_count": 1,
                                "max_price": {
                                    "value": 12000
                                },
                                "min_price": {
                                    "value": 12000
                                }
                            }
                        ]
                    }
                },
                {
                    "key": "blue",
                    "doc_count": 1,
                    "avg_price": {
                        "value": 15000
                    },
                    "make": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "toyota",
                                "doc_count": 1,
                                "max_price": {
                                    "value": 15000
                                },
                                "min_price": {
                                    "value": 15000
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}

在每個make桶下,多了min和max的指標。

此時,我們可以得到如下信息:

有4輛紅色汽車。
紅色汽車的平均價格是32500美刀。
紅色汽車中的3輛是Honda,1輛是BMW。
紅色Honda汽車中,最便宜的價格爲10000美刀。
最貴的紅色Honda汽車爲20000美刀。

直接求值:

POST http://node1:9200/cars/transactions/_search
{
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": {
            "avg_price": { "avg": { "field": "price" }},
            "min_price" : { "min": { "field": "price"}} , 
            "max_price" : { "max": { "field": "price"}}
         }
      }
   },
   "size": 0
}
==========================================================================================
{
    "took": 62,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "colors": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "red",
                    "doc_count": 4,
                    "max_price": {
                        "value": 80000
                    },
                    "min_price": {
                        "value": 10000
                    },
                    "avg_price": {
                        "value": 32500
                    }
                },
                {
                    "key": "green",
                    "doc_count": 2,
                    "max_price": {
                        "value": 30000
                    },
                    "min_price": {
                        "value": 12000
                    },
                    "avg_price": {
                        "value": 21000
                    }
                },
                {
                    "key": "blue",
                    "doc_count": 1,
                    "max_price": {
                        "value": 15000
                    },
                    "min_price": {
                        "value": 15000
                    },
                    "avg_price": {
                        "value": 15000
                    }
                }
            ]
        }
    }
}

創建條形圖(Building Bar Charts)

柱狀圖桶(Histogram Bucket)非常有用。柱狀圖在本質上就是條形圖,如果你創建過一份報告或者分析面板(Analytics Dashboard),毫無疑問其中會有一些條形圖。柱狀圖通過指定一個間隔(Interval)來工作。如果我們使用柱狀圖來表示銷售價格,你或許會指定一個值爲20000的間隔。因此每20000美刀會創建一個桶。然後文檔會被分配到桶中。

對於我們的儀表板,我們想要知道每個價格區間中有多少輛車。同時我們也想知道該價格桶中產生了多少收入。這是通過將該間隔中所有的車的售價累加而計算得到的。

爲了達到這一目的,我們使用了一個histogram(柱狀圖)類型的聚合然後在其中嵌套了一個sum指標:

POST http://node1:9200/cars/transactions/_search
{
   "aggs": {
      "price": {
         "histogram": {
            "field": "price",
            "interval":20000
         },
         "aggs": {
            "revenue":{
            	"sum":{
            		"field":"price"
            	}
            }
         }
      }
   },
   "size": 0
}

正如你能看到的那樣,我們的查詢是圍繞着價格聚合而建立的,該聚合包含了一個柱狀圖桶。該桶需要一個數值字段以及一個間隔值來進行計算。間隔用來定義每個桶有“多寬”。間隔爲20000意味着我們能夠擁有區間[0-19999, 20000-39999, 等]。

接下來,我們在柱狀圖中定義了一個嵌套的指標。它是一個sum類型的指標,會將該區間中的文檔的price字段進行累加。這就得到了每個價格區間中的收入,因此我們就能夠從中看出是普通車還是豪華車賺的更多。

以下是得到的響應:

{
    "took": 32,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "price": {
            "buckets": [
                {
                    "key": 0,
                    "doc_count": 3,
                    "revenue": {
                        "value": 37000
                    }
                },
                {
                    "key": 20000,
                    "doc_count": 3,
                    "revenue": {
                        "value": 70000
                    }
                },
                {
                    "key": 40000,
                    "doc_count": 0,
                    "revenue": {
                        "value": 0
                    }
                },
                {
                    "key": 60000,
                    "doc_count": 0,
                    "revenue": {
                        "value": 0
                    }
                },
                {
                    "key": 80000,
                    "doc_count": 1,
                    "revenue": {
                        "value": 80000
                    }
                }
            ]
        }
    }
}

從圖形上,你可以將前面的數據表示如下:

當然,你可以使用任何生成類別和統計信息的聚合來創建條形圖,並不僅限於使用histogram桶。讓我們創建一個受歡迎的汽車製造商的條形圖,其中包含了它們的平均價格和標準誤差(Standard Error)。需要使用的而是terms桶以及一個extended_stats指標

POST http://node1:9200/cars/transactions/_search
{
  "aggs": {
    "makes": {
      "terms": {
        "field": "make",
        "size": 10
      },
      "aggs": {
        "stats": {
          "extended_stats": {
            "field": "price"
          }
        }
      }
    }
  },
  "size":0
}

它會返回一個製造商列表(根據受歡迎程度排序)以及針對每個製造商的一些列統計信息。其中,我們對stats.avg,stats.count以及stats.std_deviation感興趣。有了這一信息,我們能夠計算出標準誤差:

std_err = std_deviation / count
{
    "took": 35,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "makes": {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets": [
                {
                    "key": "honda",
                    "doc_count": 3,
                    "stats": {
                        "count": 3,
                        "min": 10000,
                        "max": 20000,
                        "avg": 16666.666666666668,
                        "sum": 50000,
                        "sum_of_squares": 900000000,
                        "variance": 22222222.22222221,
                        "std_deviation": 4714.045207910315,
                        "std_deviation_bounds": {
                            "upper": 26094.757082487296,
                            "lower": 7238.5762508460375
                        }
                    }
                },
                {
                    "key": "toyota",
                    "doc_count": 2,
                    "stats": {
                        "count": 2,
                        "min": 12000,
                        "max": 15000,
                        "avg": 13500,
                        "sum": 27000,
                        "sum_of_squares": 369000000,
                        "variance": 2250000,
                        "std_deviation": 1500,
                        "std_deviation_bounds": {
                            "upper": 16500,
                            "lower": 10500
                        }
                    }
                },
                {
                    "key": "bmw",
                    "doc_count": 1,
                    "stats": {
                        "count": 1,
                        "min": 80000,
                        "max": 80000,
                        "avg": 80000,
                        "sum": 80000,
                        "sum_of_squares": 6400000000,
                        "variance": 0,
                        "std_deviation": 0,
                        "std_deviation_bounds": {
                            "upper": 80000,
                            "lower": 80000
                        }
                    }
                },
                {
                    "key": "ford",
                    "doc_count": 1,
                    "stats": {
                        "count": 1,
                        "min": 30000,
                        "max": 30000,
                        "avg": 30000,
                        "sum": 30000,
                        "sum_of_squares": 900000000,
                        "variance": 0,
                        "std_deviation": 0,
                        "std_deviation_bounds": {
                            "upper": 30000,
                            "lower": 30000
                        }
                    }
                }
            ]
        }
    }
}

時間數據處理(Looking at Time)

如果在ES中,搜索是最常見的行爲,那麼創建日期柱狀圖(Date Histogram)肯定是第二常見的。

 

  • 今年的每個月銷售了多少輛車?
  • 過去的12小時中,這隻股票的價格是多少?
  • 上週每個小時我們的網站的平均延遲是多少?

常規的histogram通常使用條形圖來表示而date_histogram傾向於被裝換爲線圖(Line Graph)來表達時間序列(Time Series)。很多公司使用ES就是爲了對時間序列數據進行分析。

date_histogram的工作方式和常規的histogram類似。常規的histogram是基於數值字段來創建數值區間的桶,而date_histogram則是基於時間區間來創建桶。因此每個桶是按照某個特定的日曆時間定義的(比如,1個月或者是2.5天)。

常規Histogram能夠和日期一起使用嗎?

從技術上而言,是可以的。常規的histogram桶可以和日期一起使用。但是,它不懂日期相關的信息(Not calendar-aware)。而對於date_histogram,你可以將間隔(Interval)指定爲1個月,它知道2月份比12月份要短。date_histogram還能夠和時區一同工作,因此你可以根據用戶的時區來對圖形進行定製,而不是根據服務器。

常規的histogram會將日期理解爲數值,這意味着你必須將間隔以毫秒的形式指定。同時聚合也不理解日曆間隔,所以它對於日期幾乎是沒法使用的。

第一個例子中,我們會創建一個簡單的線圖(Line Chart)來回答這個問題:每個月銷售了多少輛車?

GET /cars/transactions/_search?search_type=count
{
   "aggs": {
      "sales": {
         "date_histogram": {
            "field": "sold",
            "interval": "month", 
            "format": "yyyy-MM-dd" 
         }
      }
   }
}

在查詢中有一個聚合,它爲每個月創建了一個桶。它能夠告訴我們每個月銷售了多少輛車。同時指定了一個額外的格式參數讓桶擁有更"美觀"的鍵值。在內部,日期被簡單地表示成數值。然而這會讓UI設計師生氣,因此使用格式參數可以讓日期以更常見的格式進行表示。 5.x以下 空桶不會顯示 5.x以上默認空桶會顯示。

{
    "took": 326,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "sales": {
            "buckets": [
                {
                    "key_as_string": "2014-01-01",
                    "key": 1388534400000,
                    "doc_count": 1
                },
                {
                    "key_as_string": "2014-02-01",
                    "key": 1391212800000,
                    "doc_count": 0
                },
                {
                    "key_as_string": "2014-03-01",
                    "key": 1393632000000,
                    "doc_count": 0
                },
                {
                    "key_as_string": "2014-04-01",
                    "key": 1396310400000,
                    "doc_count": 0
                },
                {
                    "key_as_string": "2014-05-01",
                    "key": 1398902400000,
                    "doc_count": 1
                },
                {
                    "key_as_string": "2014-06-01",
                    "key": 1401580800000,
                    "doc_count": 0
                },
                {
                    "key_as_string": "2014-07-01",
                    "key": 1404172800000,
                    "doc_count": 1
                },
                {
                    "key_as_string": "2014-08-01",
                    "key": 1406851200000,
                    "doc_count": 1
                },
                {
                    "key_as_string": "2014-09-01",
                    "key": 1409529600000,
                    "doc_count": 0
                },
                {
                    "key_as_string": "2014-10-01",
                    "key": 1412121600000,
                    "doc_count": 1
                },
                {
                    "key_as_string": "2014-11-01",
                    "key": 1414800000000,
                    "doc_count": 2
                }
            ]
        }
    }
}

聚合完整地被表達出來了。你能看到其中有用來表示月份的桶,每個桶中的文檔數量,以及漂亮的key_as_string。

返回空桶

5.x之前不會返回空桶, 5.x之後會空桶和非空桶同時返回

因此本質上,我們需要返回所有的桶,哪怕其中不含有任何文檔。我們可以設置兩個額外的參數來實現這一行爲:

{
   "aggs": {
      "sales": {
         "date_histogram": {
            "field": "sold",
            "interval": "month", 
            "format": "yyyy-MM-dd" ,
            "min_doc_count":0,
            "extended_bounds":{
            	"min":"2014-01-01",
            	"max":"2014-12-31"
            }
            
         }
      }
   },
   "size":0
}

以上的min_doc_count參數會強制返回空桶,extended_bounds參數會強制返回一整年的數據。

這兩個參數會強制返回該年中的所有月份,無論它們的文檔數量是多少。min_doc_count的意思很容易懂:它強制返回哪怕爲空的桶。

extended_bounds參數需要一些解釋。min_doc_count會強制返回空桶,但是默認ES只會返回在你的數據中的最小值和最大值之間的桶。

因此如果你的數據分佈在四月到七月,你得到的桶只會表示四月到七月中的幾個月(可能爲空,如果使用了min_doc_count=0)。爲了得到一整年的桶,我們需要告訴ES需要得到的桶的範圍。extended_bounds參數就是用來告訴ES這一範圍的。一旦你添加了這兩個設置,得到的響應就很容易被圖形生成庫處理而最終得到下圖:

另外的例子

我們已經看到過很多次,爲了實現更復雜的行爲,桶可以嵌套在桶中。爲了說明這一點,我們會創建一個用來顯示每個季度,所有制造商的總銷售額的聚合。同時,我們也會在每個季度爲每個製造商單獨計算其總銷售額,因此我們能夠知道哪種汽車創造的收益最多:

POST http://node1:9200/cars/transactions/_search
{
   "aggs": {
      "sales": {
         "date_histogram": {
            "field": "sold",
            "interval": "quarter", 
            "format": "yyyy-MM-dd",
            "min_doc_count" : 0,
            "extended_bounds" : {
                "min" : "2014-01-01",
                "max" : "2014-12-31"
            }
         },
         "aggs": {
            "per_make_sum": {
               "terms": {
                  "field": "make"
               },
               "aggs": {
                  "sum_price": {
                     "sum": { "field": "price" } 
                  }
               }
            },
            "total_sum": {
               "sum": { "field": "price" } 
            }
         }
      }
   },
   "size":0
}

可以發現,interval參數被設成了quarter。季度

得到的響應如下(刪除了很多):

{
    "took": 32,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 7,
        "max_score": 0,
        "hits": []
    },
    "aggregations": {
        "sales": {
            "buckets": [
                {
                    "key_as_string": "2014-01-01",
                    "key": 1388534400000,
                    "doc_count": 1,
                    "per_make_sum": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "bmw",
                                "doc_count": 1,
                                "sum_price": {
                                    "value": 80000
                                }
                            }
                        ]
                    },
                    "total_sum": {
                        "value": 80000
                    }
                },
                {
                    "key_as_string": "2014-04-01",
                    "key": 1396310400000,
                    "doc_count": 1,
                    "per_make_sum": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "ford",
                                "doc_count": 1,
                                "sum_price": {
                                    "value": 30000
                                }
                            }
                        ]
                    },
                    "total_sum": {
                        "value": 30000
                    }
                },
                {
                    "key_as_string": "2014-07-01",
                    "key": 1404172800000,
                    "doc_count": 2,
                    "per_make_sum": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "toyota",
                                "doc_count": 2,
                                "sum_price": {
                                    "value": 27000
                                }
                            }
                        ]
                    },
                    "total_sum": {
                        "value": 27000
                    }
                },
                {
                    "key_as_string": "2014-10-01",
                    "key": 1412121600000,
                    "doc_count": 3,
                    "per_make_sum": {
                        "doc_count_error_upper_bound": 0,
                        "sum_other_doc_count": 0,
                        "buckets": [
                            {
                                "key": "honda",
                                "doc_count": 3,
                                "sum_price": {
                                    "value": 50000
                                }
                            }
                        ]
                    },
                    "total_sum": {
                        "value": 50000
                    }
                }
            ]
        }
    }
}

我們可以將該響應放入到一個圖形中,使用一個線圖(Line Chart)來表達總銷售額,一個條形圖來顯示每個製造商的銷售額(每個季度),如下所示:

無限的可能性

顯然它們都是簡單的例子,但是在對聚合進行繪圖時,是存在無限的可能性的。比如,下圖是Kibana中的一個用來進行實時分析的儀表板,它使用了很多聚合:

因爲聚合的實時性,類似這樣的儀表板是很容易進行查詢,操作和交互的。這讓它們非常適合非技術人員和分析人員對數據進行分析,而不需要他們創建一個Hadoop任務。

爲了創建類似Kibana的強大儀表板,你需要掌握一些高級概念,比如作用域(Scoping),過濾(Filtering)和聚合排序(Sorting Aggregations)。

 

ES聚合剩餘內容

[Elasticsearch] 聚合作用域(Scoping Aggregations)

[Elasticsearch] 過濾查詢以及聚合(Filtering Queries and Aggregations)

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章