PythonとElasticsearchの夕べ ~二代目の帰朝~

別記事の続きです。データの登録と検索について記載します。

もくじ:

データの投入

Elasticsearch().indexプロパティを経由して、データ(ドキュメント)を登録します。公式には「インデキシング」と呼ばれているようですね。

データ内にID("_id")を明示していない場合は、サーバ側で自動的に生成されます。また、マッピングを明示していない場合も自動的に定義されます。

上のように逐次的に登録していく方法もありますが、一括で実行することもできます。複雑な操作でなければこちらのほうが高速に実行できますので効率的です。データはリストとして渡すか、メモリに乗らない場合はイテレータを使うやり方が良いでしょう。

この用途のためにhelpers.bulkメソッドが用意されています。

今回はジェネレータ_load_data()を定義し、インデックス名"_index"やドキュメントタイプ名"_type"、ドキュメント"_source"(データ本体)を持つdictオブジェクトを整形してyieldさせています。

まあ、今回は別途ファイル(データをリストにしたJSONファイル)から全て読んでいるコードなのであまり意味はありませんが、for文の中で別途大きなデータを生成させる場合や、そもそもの件数が多い場合に有効です。

なお、.bulkメソッドに渡すオブジェクトは"_index""_type"を省略する場合、"_id"を明示する場合などバリエーションがあります。"_op_type"の設定次第で、ドキュメントの更新や削除など他の操作もできますがここでは割愛します。

検索

クエリを使って検索できます。標準でサポートされているものはQuery DSLと呼ばれているもので、"query"キーを持つオブジェクトで定義するのがよく使われる例です。

特定のフィールド(例の場合は"name")がある値に一致するものを取得するには、下記のように書きます。

マッピングの定義方法にもよりますが、結果は下記のようになりました。"name"フィールドで指定した値を含むものが返ってきていますね。

デフォルトでスコアが計算されるようになっていますので、もっと実際的なデータを扱う際にはソートに使うことができます。スコアの詳しい設定はクエリ等の書き方次第で、要件に応じて調整することもできます。

今回は元のドキュメント(_sourceの部分)そのものを応答に含めていますが、ドキュメント単体が大きい場合など、取得するフィールドを絞ることもできます。下記はドキュメントを省略させる場合の例です。

ヒット数'hits.total'が多い場合、'hits.hits'に入れられるドキュメントはそのうちの一部(デフォルトでは10)になります。ページネーションされているイメージですが、検索のオプション次第で振る舞いを変えることができます。

クエリのバリエーションはそれこそ書ききれないくらい豊富なので、ここではあと一例くらい。And条件で複数のフィールドの一致を指定する場合は、こんなクエリになります。

結果はこんな感じ。指定したフィールドのAnd部分が返ってきているのがわかります。

SQL型のデータベースと比較しても機能的にほぼ同等のクエリが書けますし、より柔軟な検索(あいまい検索など)が可能な分、検索機能についての完成度はやはり高いですね。

もちろん、アナライズなどの例と同じく、KibanaのコンソールからWeb APIをたたくこともできますので、オンデマンドに色々試してみるとクエリについての理解が深まると思います。

サンプル

今回のサンプルです。各操作をファンクションに分けて記述していますが、基本的にはElasticsearch()クライアントを生成して各メソッドを呼び出すだけです。

おわり。