ElasticsearchのField Collapsingで検索結果をグルーピングする

こんにちは。WEBサービス開発グループの中野です。
業務ではWEBサービスの検索基盤を担当しています。

ニフティのWEBサービスでは、一部サービスのサイト内検索やログ可視化などで Elasticsearch を利用しています。

Elasticsearch は Elastic社の提供する分散型の検索エンジンで、検索・集計・分析はもちろん、可視化の Kibana を初めとした強力な周辺ツールが魅力的です。
また、Apache Solr などと同じく Apache Lucene をベースとしています。

elastic

今回は、Elasticsearch 5.3 から追加された Field Collapsing 機能を使ってみたいと思います。

Field Collapsing はその名のとおり検索結果を「折りたたんで」くれる機能で、検索結果をグルーピングして SQL の GROUP BY のような操作をすることができます。
まずはサンプルをみてみましょう。

サンプル

データ

サンプルデータとして、青空文庫さんの作品リストcsvを使用させて頂いています。

このデータを Elasticsearch に投入し、通常の検索結果と Field Collapsing でグルーピングした検索結果を比較してみます。

通常の検索結果

JSONの hits の中に、作品のオブジェクトが並んでいます。

Field Collapsing の検索結果

JSONの hits の中に並んでいるのは作品ではなく著者で、それぞれの著者の下に inner_hits として作品が並んでいます。

Field Collapsing でグルーピングした方は、著者ごとにまとまった検索結果になっていることが確認できました。

使い方

次に、ドキュメント に従って Field Collapsing の使い方を確認したいと思います。

余談ですが、Elasticsearch のクエリ動作確認には Kibana の Dev Tools が便利です。
ブラウザから簡単にクエリ実行ができ、シンタックスの補完もしてくれます。
devtools

グルーピング

グルーピングをするためには、クエリに collapse 句を追加します。
collapse 句の中では、グルーピングのキーとなるフィールド名を filed に指定します。
キーに指定するフィールドは、keyword 型か numeric 型である必要があります。

下記のクエリでは、aozora というインデックスを検索条件なしで取得して、検索結果を author というフィールドの値ごとにグルーピングしています。

グループの展開

collapse 句の中に inner_hits 句を追加することで、各グループに含まれるレコードを検索結果に含めることができます。
inner_hits 句では、name 句で検索結果内のJSONオブジェクト名、size 句で各グループごとに何件取得するか、sort 句で各グループ内のソート方法、を指定可能です。

下記のクエリでは、検索結果を author というフィールドでグルーピングし、グループごとに pudDate の降順にソートした上位3件を books_recent という名前で取得しています。

参考までに、今後のアップデート では multiple inner_hits 機能が追加され、各グループごとに複数の inner_hits を取得することが可能になるようです。

ページング・ソート

通常のクエリと同様に、ページングは from 句と size 句、ソートは sort 句で指定することが可能です。

下記のクエリでは、グルーピング結果を author フィールドの昇順にソートして50グループ目から10グループぶん取得しています。

Field Collapsing の基本的な使い方については、以上となります。

注意事項

Field Collapsing を利用していると、検索結果に含まれる took(検索実行の所要時間)より実際のレスポンスタイムが大幅に遅くなることがあります。
特に、inner_hits 句を指定して各グループに含まれる情報を展開している場合、グループ数が多くなればなるほどこの差は大きくなります。

この理由は、Field Collapsing の仕組み上 inner_hits を生成するために各グループごとに内部クエリを発行しているのですが、took にはこの内部クエリの所要時間が含まれていないためです。
本問題については既に Pull Request で取り上げられているため、今後のバージョンアップで改善されると思います。

ちなみに上記の内部クエリをどの程度並列に実行するかは max_concurrent_group_searches パラメータで調節することも可能です。
この値はデフォルトではノード数とスレッドプールサイズによって自動的に決定されるようです。

その他、Elasticsearch がどのように検索を実行しているかについては、ドキュメントのガイドが非常に参考になりますので、ご一読をおすすめします。

新しい機能を利用する際には色々な問題に出くわすこともありますが、ドキュメントだけでなくその機能が追加された Pull Request や関連する Issue を確認してみると参考になる情報が得られるかもしれません。

まとめ

Field Collapsing はいかがでしたでしょうか。
WEBサイトやECサービスで検索結果などのコンテンツを表示する際に、重複したコンテンツを一つにまとめたり、属性ごとにグルーピングして提供できれば一覧性が向上しそうですね。

ニフティのWEBサービスでも Field Collapsing などの便利な機能を活用していきたいです。

Elasticsearch は5系になってからリリース頻度がとても高いので、今後のアップデートにも期待したいと思います。