目次
こんなツイートを見つけたので、Aggregationのサンプルでも書こうかなと。(前から書こうと思ってたんですが。。。)
@elasticsearch Hi, Would you please tell me the way to do "Pivot Faceting" like Solr-4.0 in elasticsearch-1.1.1 or prior version? Thank you.
— Y.Kentaro (@yoshi_ken) 2014, 5月 2
ちなみに、Aggregationは1.0.0から導入された機能なので、ElasticSearch Server日本語版には掲載されていない機能になります。(ごめんなさい)
公式ガイドのAggregationsのページはこちらになりますが、実例があったほうがいいかなと。
@yoshi_ken さんから実例のサンプルの指定もいただいたので、ブログを書くのが非常に楽です。ありがとうございます。
問題
次のような不動産系のデータがあるとします。
- id
- 物件名
- 都道府県(東京、神奈川、…..)
- 物件種別(賃貸、売買、…..)
この時、都道府県別に、物件種別ごとの件数を取得したいという趣旨です。
- 東京
- 賃貸: xxx件
- 売買: yyy件
- 神奈川
- 賃貸: xxx件
- 売買: yyy件 …
これを、Elasticsearchでどうやって取得するかという問題です。
インデックスとデータの登録
まずは、インデックスを作ります。 あくまでもサンプルなので、全部not_analyzedにしてますが、そのへんは適宜変更してください。
# create index
PUT /pref_aggs
{
"settings": {
"number_of_shards": 2
},
"mappings": {
"japan" : {
"_id" : {
"path" : "id"
},
"properties": {
"id": {"type": "string", "index": "not_analyzed"},
"name": {"type": "string", "index": "not_analyzed"},
"pref": {"type": "string", "index": "not_analyzed"},
"type": {"type": "string", "index": "not_analyzed"}
}
}
}
}
_id
を使用して、データ登録時にid
フィールドにある文字列をそのままIDとして登録できるように指定してあります。
登録するデータは次のようなものを適当に100件程度作ってりました。
{"id": "id0", "name": "name0", "pref": "01_北海道", "type": "売買"}
{"id": "id1", "name": "name1", "pref": "09_栃木県", "type": "売買"}
{"id": "id2", "name": "name2", "pref": "38_愛媛県", "type": "賃貸"}
{"id": "id3", "name": "name3", "pref": "40_福岡県", "type": "賃貸"}
{"id": "id4", "name": "name4", "pref": "35_山口県", "type": "売買"}
{"id": "id5", "name": "name5", "pref": "12_千葉県", "type": "賃貸"}
...
データの登録には、前に紹介した方法「stream2esと複数データの登録」を用いました。
ファセット
このようなデータがある場合に、まず思いつくのはファセットによる取得です。 いささか強引ですが。。。
GET /pref_aggs/japan/_search
{
"size": 0,
"query": {
"match_all": {}
},
"facets": {
"type_賃貸": {
"terms": {
"order": "term",
"field": "pref",
"size": 50
}, "facet_filter": {"term": {"type": "賃貸" }}
},
"type_売買": {
"terms": {
"order": "term",
"field": "pref",
"size": 50
}, "facet_filter": {"term": {"type": "売買" }}
}
}
}
facet_filter
を使用して、type
フィールドによる個別の絞込を行っています。
あとは、pref
フィールドのファセットを取得すれば、出力は次のようになります。
{
"took": 6,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"hits": {
"total": 100,
"max_score": 0,
"hits": []
},
"facets": {
"type_賃貸": {
"_type": "terms",
"missing": 0,
"total": 52,
"other": 0,
"terms": [
{
"term": "00_北海道",
"count": 1
},
{
"term": "01_青森県",
"count": 2
},
{
"term": "03_宮城県",
"count": 3
},
...
},
"type_売買": {
"_type": "terms",
"missing": 0,
"total": 48,
"other": 0,
"terms": [
{
"term": "00_北海道",
"count": 2
},
{
"term": "02_岩手県",
"count": 1
},
{
"term": "04_秋田県",
"count": 1
},
...
}
望んでいた形式とは少し異なりますが、facet_filter
する回数を少なくするため、
ファセットは都道府県のフィールドを指定したためです。
アプリで頑張って入れ替えてください。。。
この場合、’type’の個数がわかっているので、頑張ってこのような記述ができました。
ただ、type
が増えた時にアプリの修正とかが必要になりますよね。
Aggregations
ということで、Aggregationsの出番です。 ファセットよりも柔軟に、検索結果に対していろいろな集計が行える機能になります。 一見に如かずということで、クエリを紹介します。
GET /pref_aggs/japan/_search
{
"size": 0,
"query": {
"match_all": {}
},
"aggs": {
"pref": {
"terms": {
"order": {
"_term": "asc"
},
"field": "pref",
"size": 50
},
"aggs": {
"type": {
"terms": {
"field": "type",
"size": 10
}
}
}
}
}
}
ファセットよりもシンプルですし、賃貸
といったような値を指定していません。
aggs
というのがaggregations
機能を指定している部分になります。
検索結果は次のように出力されます。
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"hits": {
"total": 100,
"max_score": 0,
"hits": []
},
"aggregations": {
"pref": {
"buckets": [
{
"key": "00_北海道",
"doc_count": 3,
"type": {
"buckets": [
{
"key": "売買",
"doc_count": 2
},
{
"key": "賃貸",
"doc_count": 1
}
]
}
},
{
"key": "01_青森県",
"doc_count": 2,
"type": {
"buckets": [
{
"key": "賃貸",
"doc_count": 2
}
]
}
},
...
Aggregationsの結果は、望んでいた通りの出力になっています。
クエリの構成を見てみましょう。
"aggs": {
"pref": { #1
"terms": {
"order": {
"_term": "asc"
},
"field": "pref",
"size": 50
},
"aggs": { #2
"type": {
"terms": {
"field": "type",
"size": 10
}
}
}
}
}
最初の#1のpref
は出力を扱いやすくするためにつけているラベルになります。好きな名前をつけることが可能です。
次のterms
がAggregationのタイプ(どのような集計をして欲しいか)になります。
今回は、pref
フィールドにある単語(term)毎に、集計をしたいので、terms
を指定します。
その他にどんなタイプがあるかは、公式ガイドをご覧ください。
次に、さらにtype
フィールドで集計したいので、#2の部分で後続のAggregationを指定しています。
都道府県同様、type
フィールドにある単語毎に集計するために、terms
を指定します。
これで、先ほどのような結果が出力できます。
ちなみに、さらにtype
の中に他の種別で集計したいという場合は、さらにaggs
を追加していけばOKです。
Aggregationは非常に柔軟な集計を可能にする機能です。ただし、検索結果に対して集計処理を行っているため、 メモリやCPUなどのリソースを消費するので注意が必要です。
Aggregationの説明については、こちらのFound.noのブログ(英語)がわかりやすかったので参考にしてみてください。
まとめ
非常に簡単ですが、Aggregationsについて紹介しました。 その他にもAggregationsでできることがあるので、後日別のサンプルを用意して説明しようかと思います。
100件のデータやここまでの操作については、gistにあるので、興味がある方はご覧いただければと。 stream2esの操作以外は、Marvelに付属のsenseを利用しています。
comments powered by Disqus
See Also by Hugo
- 2014年のElasticsearch
- ローマ字入力のゆれと読み(JapaneseCompletionAnalyzerその2)
- 第6回Elasticsearch勉強会を開催しました。#elasticsearchjp
- Elasticsearchの新しいJavaクライアント(2024年3月版)
- 機械学習による検索ランキング改善ガイドを読みました