1. 程式人生 > >ElasticSearch - 聚合 aggs

ElasticSearch - 聚合 aggs

聚合概念

  • ElasticSearch除了致力於搜尋之外,也提供了聚合實時分析資料的功能

    • 如果把搜尋比喻為大海撈針(從海量的文件中找出符合條件的那一個),那麼聚合就是去分析大海中的針們的特性,像是

      • 在大海里有多少針?

      • 針的平均長度是多少?

      • 按照針的製造商來劃分,針的長度中位值是多少?

      • 每月加入到海中的針有多少?

      • 這裡面有異常的針麼?

    • 因此透過聚合,我們可以得到一個數據的概覽,聚合能做的是分析和總結全套的資料,而不是查詢單個文件(這是搜尋做的事)

  • 聚合允許我們向資料提出一些複雜的問題,雖然他的功能完全不同於搜尋,但他們其實使用了相同的資料結構,這表示聚合的執行速度很快,並且就像搜尋一樣幾乎是實時的

    • 並且由於聚合和搜尋是使用同樣的資料結構,因此聚合和搜尋可以是一起執行的

    • 這表示我們可以在一次json請求裡,同時對相同的資料進行 搜尋/過濾 + 分析,兩個願望一次滿足

  • 聚合的兩個主要的概念,分別是 桶 和 指標

    • 桶(Buckets) : 滿足特定條件的文件的集合

      • 當聚合開始被執行,每個文件會決定符合哪個桶的條件,如果匹配到,文件將放入相應的桶並接著進行聚合操作

        • 像是一個員工屬於男性桶或者女性桶,日期2014-10-28屬於十月桶,也屬於2014年桶

      • 桶可以被巢狀在其他桶裡面

        • 像是北京能放在中國桶裡,而中國桶能放在亞洲桶裡

      • Elasticsearch提供了很多種型別的桶,像是時間、最受歡迎的詞、年齡區間、地理位置桶等等,不過他們在根本上都是通過同樣的原理進行操作,也就是基於條件來劃分文件,一個文件只要符合條件,就可以加入那個桶,因此一個文件可以同時加入很多桶

    • 指標(Metrics) : 對桶內的文件進行統計計算

      • 桶能讓我們劃分文件到有意義的集合, 但是最終我們需要的是對這些桶內的文件進行一些指標的計算

      • 指標通常是簡單的數學運算(像是min、max、avg、sum),而這些是通過當前桶中的文件的值來計算的,利用指標能讓你計算像平均薪資、最高出售價格、95%的查詢延遲這樣的資料

  • aggs 聚合的模板

    • 當query和aggs一起存在時,會先執行query的主查詢,主查詢query執行完後會搜出一批結果,而這些結果才會被拿去aggs拿去做聚合

      • 另外要注意aggs後面會先接一層自定義的這個聚合的名字,然後才是接上要使用的聚合桶

      • 如果有些情況不在意查詢結果是什麼,而只在意aggs的結果,可以把size設為0,如此可以讓返回的hits結果集是0,加快返回的速度

    • 一個aggs裡可以有很多個聚合,每個聚合彼此間都是獨立的,因此可以一個聚合拿來統計數量、一個聚合拿來分析資料、一個聚合拿來計算標準差...,讓一次搜尋就可以把想要做的事情一次做完

      • 像是此例就定義了3個聚合,分別是custom_name1、custom_name2、custom_name3

    • aggs可以巢狀在其他的aggs裡面,而巢狀的桶能作用的文件集範圍,是外層的桶所輸出的結果集

    GET 127.0.0.1/mytest/doc/_search
    {
        "query": { ... },
        "size": 0,
        "aggs": {
            "custom_name1": {  //aggs後面接著的是一個自定義的name
                "桶": { ... }  //再來才是接桶
            },
            "custom_name2": {  //一個aggs裡可以有很多聚合
                "桶": { ... }
            },
            "custom_name3": {
                "桶": {
                   .....
                },
                "aggs": {  //aggs可以巢狀在別的aggs裡面
                    "in_name": { //記得使用aggs需要先自定義一個name
                        "桶": { ... } //in_name的桶作用的文件是custom_name3的桶的結果
                    }
                }
            }
        }
    }
    • 結果

        {
         "hits": {
             "total": 8,
             "max_score": 0,
             "hits": [] //因為size設為0,所以沒有查詢結果返回
         },
         "aggregations": {
             "custom_name1": {
                 ...
             },
             "custom_name2": {
                 ...
             },
             "custom_name3": {
                 ... ,
                 "in_name": {
                    ....
                 }
             }
         }
        }

聚合中常用的桶 terms、filter、top_hits

  • terms桶 : 針對某個field的值進行分組,field有幾種值就分成幾組

    • terms桶在進行分組時,會為此field中的每種值建立一個新的桶

    • 要注意此 "terms桶" 和平常用在主查詢query中的 "查詢terms" 是不同的東西

    • 具體例項

      • 首先插入幾筆資料,其中color是一個keyword型別

        { "color": "red" }
        { "color": "green" }
        { "color": ["red", "blue"] }
      • 執行terms聚合

        GET 127.0.0.1/mytest/doc/_search
        {
            "query": {
                "match_all": {}
            },
            "size": 0,
            "aggs": {
                "my_name": {
                    "terms": {
                        "field": "color" //使用color來進行分組
                    }
                }
            }
        }
      • 結果

        • 因為color總共有3種值,red、blue、green,所以terms桶為他們產生了3個bucket,並計算了每個bucket中符合的文件有哪些

        • bucket和bucket間是獨立的,也就是說一個文件可以同時符合好幾個bucket,像是{"color": ["red", "blue"]}就同時符合了red和blue bucket

        "aggregations": {
            "my_name": {
                "doc_count_error_upper_bound": 0,
                "sum_other_doc_count": 0,
                "buckets": [
                    {
                        "key": "blue",
                        "doc_count": 1
                    },
                    {
                        "key": "red",
                        "doc_count": 2  //表示color為red的文件有2個,此例中就是 {"color": "red"} 和 {"color": ["red", "blue"]}這兩個文件
                    },
                    {
                        "key": "green",
                        "doc_count": 1
                    }
                ]
            }
        }
    • 具體例項二

      • 將terms桶搭配度量指標(avg、min、max、sum...)一起使用

        • 其實度量指標也可以看成一種"桶",他可以和其他正常的桶們進行巢狀作用,差別只在指標關注的是這些文件中的某個數值的統計,而桶關注的是文件

      • 首先準備資料,color一樣為keyword型別,而price為integer型別

        { "color": "red", "price": 100 }
        { "color": "green", "price": 500 }
        { "color": ["red", "blue"], "price": 1000 }
      • 將avg指標巢狀在terms桶裡一起使用

        GET 127.0.0.1/mytest/doc/_search
        {
            "query": {
                "match_all": {}
            },
            "size": 0,
            "aggs": {
                "my_name": {
                    "terms": {
                        "field": "color"
                    },
                    "aggs": {  //巢狀兩個指標avg、min在terms桶中
                        "my_avg_price": { //my_avg_price計算每個bucket的平均price
                            "avg": {
                                "field": "price"
                            }
                        },
                        "my_min_price": { //my_min_price計算每個bucket中的最小price
                            "min": {
                                "field": "price"
                            }
                        }
                    }
                }
            }
        }
      • 結果

        "aggregations": {
            "my_name": {
                "doc_count_error_upper_bound": 0,
                "sum_other_doc_count": 0,
                "buckets": [ //terms桶中的每個bucket都會計算avg和min兩個指標
                    {
                        "key": "blue",
                        "doc_count": 1,
                        "my_avg_price": { //avg指標
                            "value": 1000
                        },
                        "my_min_price": { //min指標
                            "value": 100
                        }
                    },
                    {
                        "key": "red",
                        "doc_count": 2,
                        "my_avg_price": {  //avg指標計算的值,因為符合color為red的文件有兩筆,所以平均price為100+1000/2 = 550
                            "value": 550
                        },
                        "my_min_price": {
                            "value": 100
                        }
                    },
                    {
                        "key": "green",
                        "doc_count": 1,
                        "my_avg_price": {
                            "value": 500
                        },
                        "my_min_price": {
                            "value": 500
                        }
                    }
                ]
            }
        }
  • filter桶 : 一個用來過濾的桶

    • 要注意此處的 "filter桶" 和用在主查詢query的 "過濾filter" 的用法是一模一樣的,都是過濾

    • 不過差別是 "filter桶" 會自己給建立一個新的桶,而不會像 "過濾filter" 一樣依附在query下

      • 因為filter桶畢竟還是一個聚合桶,因此他可以和別的桶進行巢狀,但他不是依附在別的桶上

    • 具體例項

      • 取得color為red或是blue的文件

        GET 127.0.0.1/mytest/doc/_search
        {
            "query": {
                "match_all": {}
            },
            "size": 0,
            "aggs": {
                "my_name": {
                    "filter": { //因為他用法跟一般的過濾filter一樣,所以也能使用bool巢狀
                        "bool": {
                            "must": {
                                "terms": { //注意此terms是查詢terms,不是terms桶
                                    "color": [ "red", "blue" ]
                                }
                            }
                        }
                    }
                }
            }
        }
      • 結果

        "aggregations": {
            "my_name": {
                "doc_count": 2 //filter桶計算出來的文件數量
            }
        }
    • 具體例項二

      • filter桶和terms桶巢狀使用,先過濾出color為red以及blue的文件,再對這些文件進行color分組

        GET 127.0.0.1/mytest/doc/_search
        {
            "query": {
                "match_all": {}
            },
            "size": 0,
            "aggs": {
                "my_name": { //my_name聚合
                    "filter": { //filter桶
                        "bool": {
                            "must": {
                                "terms": {
                                    "color": [ "red", "blue" ]
                                }
                            }
                        }
                    },
                    "aggs": {
                        "my_name2": { //my_name2聚合,巢狀在my_name聚合裡
                            "terms": { //terms桶
                                "field": "color"
                            }
                        }
                    }
                }
            }
        }
      • 結果

        • 因為terms桶巢狀在filter桶內,所以query查詢出來的文件們會先經過filter桶,如果符合filter桶,才會進入到terms桶內

        • 此處通過filter桶的文件只有兩筆,分別是{"color": "red"}以及{"color": ["red", "blue"]},所以terms桶只會對這兩筆文件做分組

        • 這也是為什麼terms桶裡沒有出現color為green的分組,因為這個文件在filter桶就被擋下來了

        "aggregations": {
            "my_name": {
                "doc_count": 2, //filter桶計算的數量,通過此處的文件只有2筆
                "my_name2": {
                    "doc_count_error_upper_bound": 0,
                    "sum_other_doc_count": 0,
                    "buckets": [ 
                        {
                            "key": "red",
                            "doc_count": 2  //terms桶計算的數量
                        },
                        {
                            "key": "blue",
                            "doc_count": 1  //terms桶計算的數量
                        }
                    ]
                }
            }
        }
  • top_hits桶 : 在某個桶底下找出這個桶的前幾筆hits,返回的hits格式和主查詢query返回的hits格式一模一樣

    • top_hits桶支援的引數

      • fromsize

      • sort : 設定返回的hits的排序

        • 要注意,假設在主查詢query裡已經對資料設定了排序sort,此sort並不會對aggs裡面的資料造成影響,也就是說主查詢query查找出來的資料會先丟進aggs而非先經過sort,因此就算主查詢設定了sort,也不會影響aggs資料裡的排序

        • 因此如果在top_hits桶裡的返回的hits資料想要排序,需要自己在top_hits桶裡設定sort

        • 如果沒有設定sort,預設使用主查詢query所查出來的_score排序

      • _source : 設定返回的欄位

    • 具體例項

      • 首先準備資料,color是keyword型別

        { "color": "red", "price": 100 }
        { "color": ["red", "blue"], "price": 1000 }
      • 使用terms桶分組,再使用top_hits桶找出每個group裡面的price最小的前5筆hits

        GET 127.0.0.1/mytest/doc/_search
        {
            "query": {
                "match_all": {}
            },
            "size": 0,
            "aggs": {
                "my_name": {
                    "terms": {
                        "field": "color"
                    },
                    "aggs": {
                        "my_top_hits": {
                            "top_hits": {
                                "size": 5,
                                "sort": {
                                    "price": "asc"
                                }
                            }
                        }
                    }
                }
            }
        }
      • 結果

        "aggregations": {
            "my_name": {
                "doc_count_error_upper_bound": 0,
                "sum_other_doc_count": 0,
                "buckets": [
                    {
                        "key": "red",
                        "doc_count": 2,  //terms桶計算出來的color為red的文件數
                        "my_top_hits": {
                            "hits": {  //top_hits桶找出color為red的這些文件中,price從小到大排序取前5筆
                                "total": 2,
                                "max_score": null,
                                "hits": [
                                    {
                                        "_score": null,
                                        "_source": { "color": "red", "price": 100 },
                                        "sort": [ 100 ]
                                    },
                                    {
                                        "_score": null,
                                        "_source": { "color": [ "red", "blue" ], "price": 1000 },
                                        "sort": [ 1000 ]
                                    }
                                ]
                            }
                        }
                    },
                    {
                        "key": "blue",
                        "doc_count": 1,  //terms桶計算出來的color為blue的文件數
                        "my_top_hits": {
                            "hits": { //top_hits桶找出的hits
                                "total": 1,
                                "max_score": null,
                                "hits": [
                                    {
                                        "_source": {
                                            "color": [ "red", "blue" ], "price": 1000 },
                                        "sort": [ 1000 ]
                                    }
                                ]
                            }
                        }
                    }
                ]
            }
        }

多桶排序

  • terms桶、histogram桶、data_histogram桶這些桶屬於多值桶,也就是說他們會動態生成很多桶,對於這些生成出來的桶們,Elasticsearch預設會使用doc_value進行降序排序,也就是說哪個生成桶的doc_value文件數較多,哪個生成桶就排在前面

    • 如果想要改變這個生成桶與生成桶之間的排序,可以在使用terms桶、histogram桶、data_histogram桶時,使用order進行排序

    • order支援的引數

      • _count : 按照文件數排序

      • _key : 按照每個桶的字串值的字母順序排序

  • 具體例項

    • 準備資料,color是keyword型別

      { "color": "red", "price": 100 }
      { "color": ["red", "blue"], "price": 1000 }
    • 使用terms桶進行分組,並且規定按照桶的字母順序升序,因此a生成桶會排在最前面而z生成桶會排在最後面

      GET 127.0.0.1/mytest/doc/_search
      {
          "query": {
              "match_all": {}
          },
          "size": 0,
          "aggs": {
              "my_name": {
                  "terms": {
                      "field": "color",
                      "order": {
                          "_key": "asc"
                      }
                  }
              }
          }
      }
    • 結果

      "aggregations": {
          "my_name": {
              "doc_count_error_upper_bound": 0,
              "sum_other_doc_count": 0,
              "buckets": [
                  {
                      "key": "blue",
                      "doc_count": 1
                  },
                  {
                      "key": "red",
                      "doc_count": 2
                  }
              ]
          }
      }