ES的Query、Filter、Metric、Bucketing使用詳解

https://www.cnblogs.com/sunsky303/p/9443013.html

 

由於筆者在實際項目僅僅將ES用作索引數據庫,並沒有深入研究過ES的搜索功能。而且鑑於筆者的搜索引擎知識有限,本文將僅僅介紹ES簡單(非全文)的查詢API。

筆者原本打算在本文中介紹聚合API的內容,但是寫着寫着發現文章有點過長,不便於閱讀,故將聚合API的內容移至下一篇博客中。

引言

單單介紹理論和API是乏味和低效率的,本文將結合一個實際的例子來介紹這些API。下表是本文數據表的表結構,表名(type)爲“student”。注意,studentNo是本表的id,也就是_id字段的值與studentNo的值保持一致。

字段名 字段含義 類型 是否能被索引 備註
studentNo 學號 string id
name 姓名 string  
sex 性別 string  
age 年齡 integer  
birthday 出生年月 date  
address 家庭住址 string  
classNo 班級 string  
isLeader 是否爲班幹部 boolean  

上面的表結構所對應的mapping如下,將數據保存在索引名爲“student”的索引中。

{
  "student": {
    "properties": {
      "studentNo": {
        "type": "string",
        "index": "not_analyzed"
      },
      "name": {
        "type": "string",
        "index": "not_analyzed"
      },
      "male": {
        "type": "string",
        "index": "not_analyzed"
      },
      "age": {
        "type": "integer"
      },
      "birthday": {
        "type": "date",
        "format": "yyyy-MM-dd"
      },
      "address": {
        "type": "string",
        "index": "not_analyzed"
      },
      "classNo": {
        "type": "string",
        "index": "not_analyzed "
      },
      "isLeader": {
        "type": "boolean"
      }
    }
  }
}

索引中保存的數據如下,下面介紹的所有API都將基於這個數據表。

studentNo name male age birthday classNo address isLeader
1 劉備 24 1985-02-03 1 湖南省長沙市 true
2 關羽 22 1987-08-23 2 四川省成都市 false
3 糜夫人 19 1990-06-12 1 上海市 false
4 張飛 20 1989-07-30 3 北京市 false
5 諸葛亮 18 1992-04-27 2 江蘇省南京市 true
6 孫尚香 16 1994-05-21 3   false
7 馬超 19 1991-10-20 1 黑龍江省哈爾濱市 false
8 趙雲 23 1986-10-26 2 浙江省杭州市 false

查詢API

ES中的查詢非常靈活,爲用戶提供了非常方便而強大的API。個人覺得ES的調用接口設計得非常好,所有接口合理且風格一致,值得好好研究!

Query和Filter

ES爲用戶提供兩類查詢API,一類是在查詢階段就進行條件過濾的query查詢,另一類是在query查詢出來的數據基礎上再進行過濾的filter查詢。這兩類查詢的區別是:

  • query方法會計算查詢條件與待查詢數據之間的相關性,計算結果寫入一個score字段,類似於搜索引擎。filter僅僅做字符串匹配,不會計算相關性,類似於一般的數據查詢,所以filter得查詢速度比query快。
  • filter查詢出來的數據會自動被緩存,而query不能。

query和filter可以單獨使用,也可以相互嵌套使用,非常靈活。

Query查詢

下面的情況下適合使用query查詢:

  • 需要進行全文搜索。
  • 查詢結果依賴於相關性,即需要計算查詢串和數據的相關性。

(1)Match All Query

查詢所有的數據,相當於不帶條件查詢。下面的代碼是一個典型的match_all查詢的調用方式。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "match_all": {}
  }
}
'

 

查詢結果如下。其他所有的查詢都是返回這種格式的數據。

{
  "took": 156,                  // 查詢耗時(毫秒)
  "timed_out": false,           // 是否超時
  "_shards": {
    "total": 5,                 // 總共查詢的分片數
    "successful": 5,            // 查詢成功的分片數
    "failed": 0                 // 查詢失敗的分片數
  },
  "hits": {
    "total": 8,                 // 本次查詢的記錄數
    "max_score": 1,             // 查詢所有數據中的最大score
    "hits": [                   // 數據列表
      {
        "_index": "student",    // 數據所屬的索引名
        "_type": "student",     // 數據所屬的type
        "_id": "4",             // 數據的id值
        "_score": 1,            // 該記錄的score
        "_source": {            // ES將原始數據保存到_source字段中
          "studentNo": "4",
          "name": "張飛",
          "male": "男",
          "age": "20",
          "birthday": "1989-07-30",
          "classNo": "3",
          "isLeader": "F"
        }
      },
      {
         ……                     // 其他的數據格式相同,就不列出來了
      }
    ]
  }
}

 

查詢時,你會發現無論數據量有多大,每次最多隻能查到10條數據。這是因爲ES服務端默認對查詢結果做了分頁處理,每頁默認的大小爲10。如果想自己指定查詢的數據,可使用from和size字段,並且按指定的字段排序。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "match_all": {}
  },
  "from": 2,        // 從2條記錄開始取
  "size": 4,        // 取4條數據
  "sort": {
    "studentNo": {  // 按studentNo字段升序
      "order": "asc"// 降序爲desc
    }
  } 
}
'

注意:不要把from設得過大(超過10000),否則會導致ES服務端因頻繁GC而無法正常提供服務。其實實際項目中也沒有誰會翻那麼多頁,但是爲了ES的可用性,務必要對分頁查詢的頁碼做一定的限制。

(2)term query

詞語查詢,如果是對未分詞的字段進行查詢,則表示精確查詢。查找名爲“諸葛亮”的學生,查詢結果爲學號爲5的記錄。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "term": {
      "name": "諸葛亮"
    }
  }
}
'

(3)Bool Query

Bool(布爾)查詢是一種複合型查詢,它可以結合多個其他的查詢條件。主要有3類邏輯查詢:

  • must:查詢結果必須符合該查詢條件(列表)。
  • should:類似於in的查詢條件。如果bool查詢中不包含must查詢,那麼should默認表示必須符合查詢列表中的一個或多個查詢條件。
  • must_not:查詢結果必須不符合查詢條件(列表)。

查找2班的班幹部,查詢結果爲學號爲5的記錄。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "classNo": "2"
          }
        },
        {
          "term": {
            "isLeader": "true"
          }
        }
      ]
    }
  }
}
'

(4)Ids Query

id字段查詢。查詢數據id值爲1和2的同學,由於id的值與studentNo相同,故查詢結果爲學號爲1和2的學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "ids": {
      "type": "student",
      "values": [
        "1",
        "2"
      ]
    }
  }
}
'

 

(5)Prefix Query

前綴查詢。查找姓【趙】的同學,查詢結果是學號爲8的趙雲。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "prefix": {
      "name": "趙"
    }
  }
}
'

(6)Range Query

範圍查詢,針對date和number類型的數據。查找年齡到18~20歲的同學,查詢結果是學號爲3、4、5、7的記錄。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "range": {
      "age": {
        "gte": "18",     // 表示>=
        "lte": "20"      // 表示<=
      }
    }
  }
}
'

實際上,對於date類型的數據,ES中以其時間戳(長整形)的形式存放的。

(7)Terms Query

多詞語查詢,查找符合詞語列表的數據。如果要查詢的字段索引爲not_analyzed類型,則terms查詢非常類似於關係型數據庫中的in查詢。下面查找學號爲1,3的學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "terms": {
      "studentNo": [
        "1",
        "3"
      ]
    }
  }
}
'

 

(8)Wildcard Query

通配符查詢,是簡化的正則表達式查詢,包括下面兩類通配符:

  • * 代表任意(包括0個)多個字符
  • ? 代表任意一個字符

查找名字的最後一個字是“亮”的同學,查詢結果是學號爲5的諸葛亮。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "wildcard": {
      "name": "*亮"
    }
  }
}
'

 

(9)Regexp Query同學

正則表達式查詢,這是最靈活的字符串類型字段查詢方式。查找家住長沙市的學生,查詢結果爲學號爲1的學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {
    "regexp": {
      "address": ".*長沙市.*"  // 這裏的.號表示任意一個字符
    }
  }
}
'

 

Filter查詢

下面的情況下適合使用filter查詢:

  • yes/no的二元查詢
  • 針對精確值進行查詢

filter和query的查詢方式有不少是重疊的,所以本節僅僅介紹API的調用,一些通用的注意的事項就不再重複了。

(1)Term Filter

詞語查詢,如果是對未分詞的字段進行查詢,則表示精確查詢。查找名爲“諸葛亮”的學生,查詢結果爲學號爲5的記錄。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {               
    "term": {
      "name": "諸葛亮",
      "_cache" : true // 與query主要是這裏的區別,可以設置數據緩存
    }
  }
}
'

filter查詢方式都可以通過設置_cache爲true來緩存數據。如果下一次恰好以相同的查詢條件進行查詢並且該緩存沒有過期,就可以直接從緩存中讀取數據,這樣就大大加快的查詢速度。

(2)Bool Filter

查找2班的班幹部,查詢結果爲學號爲5的記錄。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
    "bool": {
      "must": [
        {
          "term": {
            "classNo": "2"
          }
        },
        {
          "term": {
            "isLeader": "true"
          }
        }
      ]
    }
  }
}
'

(3)And Filter

And邏輯連接查詢,連接1個或1個以上查詢條件。它與bool查詢中的must查詢非常相似。實際上,and查詢可以轉化爲對應的bool查詢。查找2班的班幹部,查詢結果爲學號爲5的學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
      "and": [
        {
          "term": {
            "classNo": "2"
          }
        },
        {
          "term": {
            "isLeader": "true"
          }
        }
      ]
  }
}
'

(4)Or Filter

Or連接查詢,表示邏輯或。。查找2班或者是班幹部的學生名單,查詢結果爲學號爲1、2、5、8的學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
      "or": [
        {
          "term": {
            "classNo": "2"
          }
        },
        {
          "term": {
            "isLeader": "true"
          }
        }
      ]
  }
}
'

(5)Exists Filter

存在查詢,查詢指定字段至少包含一個非null值的數據。如果字段索引爲not_analyzed類型,則查詢sql中的is not null查詢方式。查詢地址存在學生,查詢結果爲除了6之外的所有學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
    "exists": {
      "field": "address"
    }
  }
}
'

(6)Missing Filter

缺失值查詢,與Exists查詢正好相反。查詢地址不存在的學生,查詢結果爲學號爲6的學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
    "missing": {
      "field": "address"
    }
  }
}
'

(7)Prefix Filter

前綴查詢。查找姓【趙】的同學,查詢結果是學號爲8的趙雲。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
    "prefix": {
      "name": "趙"
    }
  }
}
'

 

(8)Range Filter

範圍查詢,針對date和number類型的數據。查找年齡到18~20歲的同學,查詢結果是學號爲3、4、5、7的記錄。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
    "range": {
      "age": {
        "gte": "18",
        "lte": "20"
      }
    }
  }
}
'

 

(9)Terms Filter

多詞語查詢,查找符合詞語列表的數據。如果要查詢的字段索引爲not_analyzed類型,則terms查詢非常類似於關係型數據庫中的in查詢。下面查找學號爲1,3的學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
    "terms": {
      "studentNo": [
        "1",
        "3"
      ]
    }
  }
}
'

 

(10)Regexp Filter

正則表達式查詢,是最靈活的字符串類型字段查詢方式。查找家住長沙市的學生,查詢結果爲學號爲1的學生。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "filter": {
    "regexp": {
      "address": ".*長沙市.*"
    }
  }
}
'

 

Aggregations (聚合)API的使用

 

ES提供的聚合功能可以用來進行簡單的數據分析。本文仍然以上一篇提供的數據爲例來講解。數據如下:

studentNo name male age birthday classNo address isLeader
1 劉備 24 1985-02-03 1 湖南省長沙市 true
2 關羽 22 1987-08-23 2 四川省成都市 false
3 糜夫人 19 1990-06-12 1 上海市 false
4 張飛 20 1989-07-30 3 北京市 false
5 諸葛亮 18 1992-04-27 2 江蘇省南京市 true
6 孫尚香 16 1994-05-21 3   false
7 馬超 19 1991-10-20 1 黑龍江省哈爾濱市 false
8 趙雲 23 1986-10-26 2 浙江省杭州市 false

本文的主要內容有:

  1. metric API的使用
  2. bucketing API的使用
  3. 兩類API的嵌套使用

1. 聚合API

ES中的Aggregations API是從Facets功能基礎上發展而來,官網正在進行替換計劃,建議用戶使用Aggregations API,而不是Facets API。ES中的聚合上可以分爲下面兩類:

  1. metric(度量)聚合:度量類型聚合主要針對的number類型的數據,需要ES做比較多的計算工作
  2. bucketing(桶)聚合:劃分不同的“桶”,將數據分配到不同的“桶”裏。非常類似sql中的group語句的含義。

metric既可以作用在整個數據集上,也可以作爲bucketing的子聚合作用在每一個“桶”中的數據集上。當然,我們可以把整個數據集合看做一個大“桶”,所有的數據都分配到這個大“桶”中。

ES中的聚合API的調用格式如下:

"aggregations" : {                  // 表示聚合操作,可以使用aggs替代
    "<aggregation_name>" : {        // 聚合名,可以是任意的字符串。用做響應的key,便於快速取得正確的響應數據。
        "<aggregation_type>" : {    // 聚合類別,就是各種類型的聚合,如min等
            <aggregation_body>      // 聚合體,不同的聚合有不同的body
        }
        [,"aggregations" : { [<sub_aggregation>]+ } ]? // 嵌套的子聚合,可以有0或多個
    }
    [,"<aggregation_name_2>" : { ... } ]* // 另外的聚合,可以有0或多個
}

 

1.1 度量類型(metric)聚合

(1)Min Aggregation

最小值查詢,作用於number類型字段上。查詢2班最小的年齡值。

curl -XPOST "192.168.1.101:9200/student/student/_search" -d 
'
{
  "query": {         // 可以先使用query查詢得到需要的數據集
    "term": {
      "classNo": "2"
    }
  },
  "aggs": {
    "min_age": {
      "min": {
        "field": "age"
      }
    }
  }
}
'

 

查詢結果爲:

{
  "took": 19,                     // 前面部分數據與普通的查詢數據相同
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 3,
    "max_score": 1.4054651,
    "hits": [
      {
        "_index": "student",
        "_type": "student",
        "_id": "2",
        "_score": 1.4054651,
        "_source": {
          "studentNo": "2",
          "name": "關羽",
          "male": "男",
          "age": "22",
          "birthday": "1987-08-23",
          "classNo": "2",
          "isLeader": "false"
        }
      },
      {
        "_index": "student",
        "_type": "student",
        "_id": "8",
        "_score": 1,
        "_source": {
          "studentNo": "8",
          "name": "趙雲",
          "male": "男",
          "age": "23",
          "birthday": "1986-10-26",
          "classNo": "2",
          "isLeader": "false"
        }
      },
      {
        "_index": "student",
        "_type": "student",
        "_id": "5",
        "_score": 0.30685282,
        "_source": {
          "studentNo": "5",
          "name": "諸葛亮",
          "male": "男",
          "age": "18",
          "birthday": "1992-04-27",
          "classNo": "2",
          "isLeader": "true"
        }
      }
    ]
  },
  "aggregations": {                    // 聚合結果
    "min_age": {                       // 前面輸入的聚合名
      "value": 18,                     // 聚合後的數據
      "value_as_string": "18.0"
    }
  }
}

 

上面的聚合查詢有兩個要注意的點:

  1. 可以通過query先過濾數據
  2. 返回的結果會包含聚合操作所作用的數據全集

有時候我們對作用的數據全集並不太敢興趣,我們僅僅需要最終的聚合結果。可以通過查詢類型(search_type)參數來實現這個需求。下面查詢出來的數據量會大大減少,ES內部也會在查詢時減少一些耗時的步驟,所以查詢效率會提高。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d     // 注意這裏的search_type=count
'
{
  "query": {             // 可以先使用query查詢得到需要的數據集
    "term": {
      "classNo": "2"
    }
  },
  "aggs": {
    "min_age": {
      "min": {
        "field": "age"
      }
    }
  }
}
'

 

本次的查詢結果爲:

{
...

"aggregations": {                    // 聚合結果
    "min_age": {                       // 前面輸入的聚合名
      "value": 18,                     // 聚合後的數據
      "value_as_string": "18.0"
    }
  }
}

 

(2)Max Aggregation

最大值查詢。下面查詢2班最大的年齡值,查詢結果爲23。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "query": {
    "term": {
      "classNo": "2"
    }
  },
  "aggs": {
    "max_age": {
      "max": {
        "field": "age"
      }
    }
  }
}
'

 

 

(3)Sum Aggregation

數值求和。下面統計查詢2班的年齡總和,查詢結果爲63。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "query": {
    "term": {
      "classNo": "2"
    }
  },
  "aggs": {
    "sum_age": {
      "sum": {
        "field": "age"
      }
    }
  }
}
'

 

 

(4)Avg Aggregation

計算平均值。下面計算查詢2班的年齡平均值,結果爲21。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "query": {
    "term": {
      "classNo": "2"
    }
  },
  "aggs": {
    "avg_age": {
      "avg": {
        "field": "age"
      }
    }
  }
}
'

 

 

(5)Stats Aggregation

統計查詢,一次性統計出某個字段上的常用統計值。下面對整個學校的學生進行簡單地統計。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "aggs": {
    "stats_age": {
      "stats": {
        "field": "age"
      }
    }
  }
}
'

 

 

查詢結果爲:

{
  ...                     // 次要數據省略

  "aggregations": {
    "stats_age": {
      "count": 8,        // 含有年齡數據的學生計數
      "min": 16,         // 年齡最小值
      "max": 24,         // 年齡最大值
      "avg": 20.125,     // 年齡平均值
      "sum": 161,        // 年齡總和
      "min_as_string": "16.0",
      "max_as_string": "24.0",
      "avg_as_string": "20.125",
      "sum_as_string": "161.0"
    }
  }
}

 

(6)Top hits Aggregation

取符合條件的前n條數據記錄。下面查詢全校年齡排在前2位的學生,僅需返回學生姓名和年齡。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
{
  "aggs": {
    "top_age": {
      "top_hits": {
        "sort": [               // 排序
          {
            "age": {            // 按年齡降序
              "order": "desc"
            }
          }
        ],
        "_source": {
          "include": [           // 指定返回字段
            "name",
            "age"
          ]
        },
        "size": 2                 // 取前2條數據
      }
    }
  }
}

 

返回結果爲:

{
  ...

  "aggregations": {
    "top_age": {
      "hits": {
        "total": 9,
        "max_score": null,
        "hits": [
          {
            "_index": "student",
            "_type": "student",
            "_id": "1",
            "_score": null,
            "_source": {
              "name": "劉備",
              "age": "24"
            },
            "sort": [
              24
            ]
          },
          {
            "_index": "student",
            "_type": "student",
            "_id": "8",
            "_score": null,
            "_source": {
              "name": "趙雲",
              "age": "23"
            },
            "sort": [
              23
            ]
          }
        ]
      }
    }
  }
}

1.2 桶類型(bucketing)聚合

(1)Terms Aggregation

按照指定的1或多個字段將數據劃分成若干個小的區間,計算落在每一個區間上記錄數量,並按指定順序進行排序。下面統計每個班的學生數,並按學生數從大到小排序,取學生數靠前的2個班級。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "aggs": {
    "terms_classNo": {
      "terms": {
        "field": "classNo",            // 按照班號進行分組
        "order": {                     // 按學生數從大到小排序
          "_count": "desc"
        },
        "size": 2                      // 取前兩名
      }
    }
  }
}
'

值得注意的,取得的前2名的學生數實際上是一個近似值,ES的實現方式參見這裏。如果想要取得精確值,可以不指定size值,使其進行一次全排序,然後在程序中自行去取前2條記錄。當然,這樣做會使得ES做大量的排序運算工作,效率比較差。

(2)Range Aggregation

自定義區間範圍的聚合,我們可以自己手動地劃分區間,ES會根據劃分出來的區間將數據分配不同的區間上去。下面將全校學生按照年齡劃分爲5個區間段:16歲以下、16~18、19~21、22~24、24歲以上,要求統計每一個年齡段內的學生數。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "aggs": {
    "range_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "to": 15
          },
          {
            "from": "16",
            "to": "18"
          },
          {
            "from": "19",
            "to": "21"
          },

          {
            "from": "22",
            "to": "24"
          },
          {
            "from": "25"
          }
        ]
      }
    }
  }
}
'

(3)Date Range Aggregation

時間區間聚合專門針對date類型的字段,它與Range Aggregation的主要區別是其可以使用時間運算表達式。主要包括+(加法)運算、-(減法)運算和/(四捨五入)運算,每種運算都可以作用在不同的時間域上面,下面是一些時間運算表達式示例。

  • now+10y:表示從現在開始的第10年。
  • now+10M:表示從現在開始的第10個月。
  • 1990-01-10||+20y:表示從1990-01-01開始後的第20年,即2010-01-01。
  • now/y:表示在年位上做舍入運算。今天是2015-09-06,則這個表達式計算結果爲:2015-01-01。說好的rounding運算呢?結果是做的flooring運算,不知道爲啥,估計是我理解錯了-_-!!

下面查詢25年前及更早出生的學生數。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "aggs": {
    "range_age": {
      "date_range": {
        "field": "birthday",
        "ranges": [
          {
            "to": "now-25y"
          }
        ]
      }
    }
  }
}
'

 

(4)Histogram Aggregation

直方圖聚合,它將某個number類型字段等分成n份,統計落在每一個區間內的記錄數。它與前面介紹的Range聚合非常像,只不過Range可以任意劃分區間,而Histogram做等間距劃分。既然是等間距劃分,那麼參數裏面必然有距離參數,就是interval參數。下面按學生年齡統計各個年齡段內的學生數量,分隔距離爲2歲。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "aggs": {
    "histogram_age": {
      "histogram": {
        "field": "age",
        "interval": 2,               // 距離爲2
        "min_doc_count": 1           // 只返回記錄數量大於等於1的區間
      }
    }
  }
}
'

(5)Date Histogram Aggregation

時間直方圖聚合,專門對時間類型的字段做直方圖聚合。這種需求是比較常用見得的,我們在統計時,通常就會按照固定的時間斷(1個月或1年等)來做統計。下面統計學校中同一年出生的學生數。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "aggs": {
    "data_histogram_birthday": {
      "date_histogram": {
        "field": "birthday",
        "interval": "year",              // 按年統計
        "format": "yyyy"                 // 返回結果的key的格式
      }
    }
  }
}
'

 

 

返回結果如下,可以看到由於上面的”format”: “yyyy”,所以返回的key_as_string只返回年的信息。

{
  "buckets": [
    {
      "key_as_string": "1985",
      "key": 473385600000,
      "doc_count": 1
    },
    {
      "key_as_string": "1986",
      "key": 504921600000,
      "doc_count": 1
    },
    {
      "key_as_string": "1987",
      "key": 536457600000,
      "doc_count": 1
    },
    {
      "key_as_string": "1989",
      "key": 599616000000,
      "doc_count": 1
    },
    {
      "key_as_string": "1990",
      "key": 631152000000,
      "doc_count": 1
    },
    {
      "key_as_string": "1991",
      "key": 662688000000,
      "doc_count": 1
    },
    {
      "key_as_string": "1992",
      "key": 694224000000,
      "doc_count": 1
    },
    {
      "key_as_string": "1994",
      "key": 757382400000,
      "doc_count": 1
    }
  ]
}

 

 

(6)Missing Aggregation

值缺損聚合,它是一類單桶聚合,也就是最終只會產生一個“桶”。下面統計學生信息中地址欄缺損的記錄數量。由於只有學號爲6的孫尚香的地址缺損,所以統計值爲1。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d 
'
{
  "aggs": {
    "missing_address": {
      "missing": {
        "field": "address"
      }
    }
  }
}
'

 

 

1.3 嵌套使用

前面已經說過,聚合操作是可以嵌套使用的。通過嵌套,可以使得metric類型的聚合操作作用在每一“桶”上。我們可以使用ES的嵌套聚合操作來完成稍微複雜一點的統計功能。下面統計每一個班裏最大的年齡值。

curl -XPOST "192.168.1.101:9200/student/student/_search?search_type=count" -d
'
{
  "aggs": {
    "missing_address": {
      "terms": {
        "field": "classNo"
      },
      "aggs": {                 // 在這裏嵌套新的子聚合
        "max_age": {
          "max": {              // 使用max聚合
            "field": "age"
          }
        }
      }
    }
  }
}
'

 

返回結果如下:

{
  "buckets": [
    {
      "key": "1",               // key是班級號
      "doc_count": 3,           // 每個班級內的人數
      "max_age": {              // 這裏是我們指定的子聚合名
        "value": 24,            // 每班的年齡值
        "value_as_string": "24.0"
      }
    },
    {
      "key": "2",
      "doc_count": 3,
      "max_age": {
        "value": 23,
        "value_as_string": "23.0"
      }
    },
    {
      "key": "3",
      "doc_count": 1,
      "max_age": {
        "value": 20,
        "value_as_string": "20.0"
      }
    },
    {
      "key": "4",
      "doc_count": 1,
      "max_age": {
        "value": 16,
        "value_as_string": "16.0"
      }
    }
  ]
}

 

2. 總結

本文介紹了ES中的一些常用的聚合API的使用,包括metric、bucketing以及它們的嵌套使用方法。掌握了這些API就可以完成簡單的數據統計功能,更多的API詳見官方文檔

 

想進階的同學,請看:ES權威指南

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