Elasticsearch 聚合學習之四:結果排序

語言: CN / TW / HK

歡迎訪問我的 GitHub

這裡分類和彙總了欣宸的全部原創(含配套原始碼): http://github.com/zq2599/blog_demos

本篇概覽

  • 本文是《Elasticsearch 聚合學習》系列的第四篇,在前面的實戰中,聚合的結果以桶(bucket)為單位,放在 JSON 陣列中返回,這些資料是沒有排序的,今天來學習如何給這些資料進行排序;

系列文章列表

  1. 《Elasticsearch聚合學習之一:基本操作》

  2. 《Elasticsearch聚合學習之二:區間聚合》

  3. 《Elasticsearch聚合學習之三:範圍限定》 ;

  4. 《Elasticsearch聚合學習之四:結果排序》

環境資訊

  • 以下是本次實戰的環境資訊,請確保您的 Elasticsearch 可以正常執行:

  1. 作業系統:Ubuntu 18.04.2 LTS

  2. JDK:1.8.0_191

  3. Elasticsearch:6.7.1

  4. Kibana:6.7.1

  • 實戰用的資料依然是一些汽車銷售的記錄,在第一章有詳細的匯入步驟,請參考操作,匯入後您的 es 中的資料如下圖:

  • 接下來一起實戰聚合排序吧;

預設排序

  • 之前文章中的聚合查詢,我們都沒有做排序設定,此時 es 會用每個桶的 doc_count 欄位做降序,下圖是個 terms 桶聚合的示例,可見返回了三個 bucket 物件,是按照 doc_count 欄位降序排列的:

內建排序

  • 除了自定義排序,es 自身也內建了兩種排序引數,可以直接拿來使用:

  • _count:這個引數對應的就是 doc_count,以下請求的排序效果和預設的排序效果是一致的:

GET /cars/transactions/_search{  "size":0,  "aggs":{   "popular_colors":{     "terms": {       "field": "color",       "order": {             ---表示要對聚合結果做排序         "_count": "desc"     ---排序欄位是doc_count,順序是降序       }     }   }   }}

複製程式碼

  • _key:在區間聚合的時候(histogram 或者 date_histogram),可以根據桶的 key 做排序:

GET /cars/transactions/_search{  "size": 0,  "aggs": {    "price": {      "histogram": {           ---區間聚合        "field": "price",      ---取price欄位的值        "interval": 20000,     ---每個區間的大小是20000        "order": {             ---表示要對聚合結果做排序          "_key": "desc"       ---排序欄位是桶的key值,這裡是每個區間的起始值,順序是降序        }      }    }  }}

複製程式碼

  • 返回結果如下,已經按照 key 的大小從大到小排序:

  ......  "aggregations" : {    "price" : {      "buckets" : [        {          "key" : 80000.0,          "doc_count" : 1        },        {          "key" : 60000.0,          "doc_count" : 0        },        {          "key" : 40000.0,          "doc_count" : 0        },        {          "key" : 20000.0,          "doc_count" : 4        },        {          "key" : 0.0,          "doc_count" : 3        }      ]    }  }}

複製程式碼

  • 但是在實際操作中發現,6.7.1 版本中,除了 histogram 和 date_histogram,terms 桶也可以用**_key**排序,如下圖,是按照 key 的字母降序:

  • desc 改為 asc 之後返回如下圖,變成了按照 key 的首字母升序排序:

  • 另外 《Elasticsearch 權威指南》 中還提到一種內建排序型別**_term**,但是 《Elasticsearch官方文件》 中宣佈該型別在 6.0 之後已經廢棄,如下:

  • 也許是"手賤"的緣故,我還是用_term 試了下,可以返回結果,但是會建議用_key 替代_term,如下圖:

按照 metrics 排序(metrics 結果只有一個值)

  • 常見的 metrics 有累加和(sum)、最大值(max)、最小值(min)、平均值(avg),這些 metrics 的特點是處理結果只有一個值,我們可以按照這個結果來排序,例如計算每個汽車品牌的銷售額,再按照銷售額排序:

GET /cars/transactions/_search{  "size": 0,  "aggs": {    "sales_rank": {      "terms": {               ---桶型別是terms        "field": "make",       ---按照make欄位聚合        "order": {             ---要求排序          "sales": "desc"      ---排序欄位是sales        }      },      "aggs": {        "sales": {            ---metrics處理後的結果儲存在名為sales的欄位中,排序已經指定了該欄位          "sum": {            ---桶內的metrics處理,型別是累加            "field": "price"  ---將price欄位的值累加          }        }      }    }  }}

複製程式碼

  • 下面是聚合結果,可見已按照每個品牌的銷售額大小做了降序的排序:

......  "aggregations" : {                          ---聚合結果    "sales_rank" : {                          ---桶名稱      "doc_count_error_upper_bound" : 0,      "sum_other_doc_count" : 0,      "buckets" : [                           ---這個JSON陣列內是按照品牌聚合而成的所有桶        {          "key" : "bmw",                      ---品牌為bmw的桶          "doc_count" : 1,                    ---文件數量為1          "sales" : {                         ---metrics處理結果            "value" : 80000.0                 ---品牌為bmw的汽車銷售總額是80000          }        },        {          "key" : "ford",          "doc_count" : 2,          "sales" : {            "value" : 55000.0          }        },        {          "key" : "honda",          "doc_count" : 3,          "sales" : {            "value" : 50000.0          }        },        {          "key" : "toyota",          "doc_count" : 2,          "sales" : {            "value" : 27000.0          }        }      ]    }  }}

複製程式碼

按照 metrics 排序(metrics 結果有多個值)

  • 和 sum、max 這些只有一個結果的 metrics 不同,extended_stats 的結果包含了數量、最大值、最小值、平均值、累加和等多種處理,此時必須要指定用其中的哪一項(否則會返回錯誤:Invalid aggregation order path [xxxx]. When ordering on a multi-value metrics aggregation a metric name must be specified):

GET /cars/transactions/_search{  "size": 0,  "aggs": {    "sales_rank": {      "terms": {                 ---桶型別是terms        "field": "make",         ---按照make欄位聚合        "order": {               ---要求排序          "stat.avg": "asc"      ---排序欄位是metrics結果的一個子項(平均值),升序        }      },      "aggs": {        "stat": {                ---metrics處理後的結果儲存在名為stat的欄位中,排序已經指定了該欄位的agv子項(平均值)          "extended_stats": {    ---桶內的metrics處理,型別是計算數量、最大值、最小值、平均值等多個指標項            "field": "price"     ---將price欄位的值拿來做metrics處理          }        }      }    }  }}

複製程式碼

  • 返回結果如下,可見已經按照 metrics 結果的 avg 子項做了升序排序:

......"aggregations" : {    "sales_rank" : {      "doc_count_error_upper_bound" : 0,      "sum_other_doc_count" : 0,      "buckets" : [        {          "key" : "toyota",          "doc_count" : 2,          "stat" : {            "count" : 2,            "min" : 12000.0,            "max" : 15000.0,            "avg" : 13500.0,               ---排序欄位            "sum" : 27000.0,            "sum_of_squares" : 3.69E8,            "variance" : 2250000.0,            "std_deviation" : 1500.0,            "std_deviation_bounds" : {              "upper" : 16500.0,              "lower" : 10500.0            }          }        },        {          "key" : "honda",          "doc_count" : 3,          "stat" : {            "count" : 3,            "min" : 10000.0,            "max" : 20000.0,            "avg" : 16666.666666666668,    ---排序欄位            "sum" : 50000.0,            "sum_of_squares" : 9.0E8,            "variance" : 2.222222222222221E7,            "std_deviation" : 4714.045207910315,            "std_deviation_bounds" : {              "upper" : 26094.757082487296,              "lower" : 7238.5762508460375            }          }        },        ......

複製程式碼

巢狀桶排序

  • 在聚合查詢中,經常對聚合的資料再次做聚合處理,例如統計每個汽車品牌下的每種顏色汽車的銷售額,這時候 DSL 中就有了多層 aggs 物件的巢狀,這就是巢狀桶(此名稱來自 《Elasticsearch 權威指南》 ),如下圖所示:

歡迎關注 InfoQ:程式設計師欣宸

學習路上,你不孤單,欣宸原創一路相伴...