Help us understand the problem. What is going on with this article?

elasticsearch-dsl で集計を伴う検索クエリを書く

More than 1 year has passed since last update.

Elasticsearch の高レベル Python クライアントである elasticsearch-dsl (ドキュメント) を使ってみました1
elasticsearch-dsl を用いると、低レベル Python クライアントである elasticsearch を用いる場合に比べて直観的に検索クエリを作成することができます。

事前準備

まずは下記のようなインデックス・データを作成します。

from elasticsearch import Elasticsearch
elastic = Elasticsearch()

index = "dsl_sample"
elastic.indices.create(index)

data = [                                                         
    {"hoge": 1, "fuga": 1, "value": 3},
    {"hoge": 1, "fuga": 2, "value": 4},
    {"hoge": 2, "fuga": 1, "value": 5},
    {"hoge": 2, "fuga": 2, "value": 6},
    {"hoge": 1, "fuga": 1, "value": 100}
]

for i, body in enumerate(data, 1):
    elastic.index(index=index, doc_type="_doc", id=i, body=body)

検索・集計をする

今回は下記のような集計を行う検索クエリを作ってみます。

  • valueが1以上100未満の値のみが集計対象となるようにフィルタする
  • 1つのクエリで「hoge で Groub By した集計結果」と「fuga で Group By した集計結果」の2つを取得する

このクエリは elasticsearch-dsl を用いて下記のように実行できます。

from elasticsearch_dsl import Search, A

search = Search(using=elastic, index=index)  # ① Search オブジェクトの作成
search = search.filter("range", value={"gte": 1, "lt": 100})  # ②フィルタ条件の記載
search = search.extra(size=0)  # ③ドキュメントの取得件数を指定

# ④ Aggregation オブジェクトの作成
aggs_hoge = A("terms", field="hoge", size=10).metric('sum_value', 'sum', field='value')
aggs_fuga = A("terms", field="fuga", size=10).metric('sum_value', 'sum', field='value')

# ⑤ Aggregation オブジェクトを Search オブジェクトに紐付ける
search.aggs.bucket("aggs_name_hoge", aggs_hoge)
search.aggs.bucket("aggs_name_fuga", aggs_fuga)

response = search.execute()
  • ①で検索に用いるオブジェクトを作成します。例のように using で既に作成した低レベルクライアントを渡すことも可能ですし、 connections を用いて接続することもできるようです。
  • 今回は集計結果が取得できればよく、ドキュメント自体を返却する必要はないので③で取得件数を 0 に指定しています。extraを用いれば検索リクエストに任意のプロパティ値を設定できるようです。
  • ④の A で集計について定義しています。
    • Afield で集計のキーを指定しています。
    • metric を用いて、どのカラムをどのように集計するのかを指定します。なおドキュメント数のみで良い場合は metric の指定が不要となります。
  • ⑤で検索オブジェクトに集計オブジェクトを関連づけます。このように2回 bucket を呼ぶことで、2種類の集計が関連づけられます。

説明のために行を分けましたが、①〜③あたりは Search(・・・).filter(・・・).extra(・・・) みたいにメソッドチェーンで直観的に書けるのが良いですね。

結果に対しては下記のようにアクセス可能です。

response.aggregations.aggs_name_hoge.buckets
# [{'key': 1, 'doc_count': 2, 'sum_value': {'value': 7.0}}, {'key': 2, 'doc_count': 2, 'sum_value': {'value': 11.0}}]

response.aggregations.aggs_name_fuga.buckets
# [{'key': 1, 'doc_count': 2, 'sum_value': {'value': 8.0}}, {'key': 2, 'doc_count': 2, 'sum_value': {'value': 10.0}}]

正しく集計されていることがわかります。

dict のクエリを確認する

elasticsearch-dsl では to_dict を用いて、低レベルクライアントでリクエストする場合の検索クエリ dict を取得することができます。

search.to_dict()

# {
#   'query': {
#     'bool': {
#       'filter': [
#         {
#           'range': {
#             'value': {
#               'gte': 1,
#               'lt': 100
#             }
#           }
#         }
#       ]
#     }
#   },
#   'aggs': {
#     'aggs_name_hoge': {
#       'terms': {
#         'field': 'hoge',
#         'size': 10
#       },
#       'aggs': {
#         'sum_value': {
#           'sum': {
#             'field': 'value'
#           }
#         }
#       }
#     },
#     'aggs_name_fuga': {
#       'terms': {
#         'field': 'fuga',
#         'size': 10
#       },
#       'aggs': {
#         'sum_value': {
#           'sum': {
#             'field': 'value'
#           }
#         }
#       }
#     }
#   },
#   'size': 0
# }

低レベル Python クライアントの elasticsearch を用いて検索クエリを作る場合はこの dict を直接作成する必要があるので、それに比べると elasticsearch-dsl を使えばだいぶ楽に検索クエリが書ける気がしますね。

おわりに

今回は elasticsaerch-dsl で簡単な集計クエリを書いてみました。
「集計を2つ設定するにはどうするのか?」とか「size=0を指定するにはどうするのか?」など最初は少し試行錯誤しましたが、慣れれば django の QuerySet のようにメソッドチェーンで検索クエリが書けるので直観的で良いなと思いました。

明日は @usangit が何か書いてくれます。お楽しみに!


  1. 今回は elasticsearch-dsl のバージョンとして 6.3.0 を使いました。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした