こんにちは、レシピ事業部検索チームの薄羽です。
検索チームでは日々レシピ検索機能の改善を行っています。
最近行った検索結果改善の過程で、2つの検索結果を交互に配置するinterleavingを実装する機会がありました。今回は、この機能の実装の際の課題となる点や、それをどう解決したのかを紹介します。
Interleaving
Interleavingは2つのランキングを評価する手法であり、2つのランキングの一番上から文書を交互に取っていくことで、ランキングを生成します。

interleaving機能を実装する際、ページネーションを壊さずにinterleavingを実装するには少し工夫が必要です。2つの検索結果から1つの検索結果を作るため、2つのランキングで重複した文書があると、生成される検索結果には同じ文書が2回表示されることになってしまいます。

どうやるか
すでにビズリーチさんがElasticsearchでのinterleavingの取り組みを記事にされています (https://engineering.visional.inc/blog/615/implement-interleaving-for-search-evaluation/)。ビズリーチさんもinterleavingの実装にあたりページネーションの問題にぶつかっていて、その解決のためにinterleavingするページを固定し、すでに表示した文書をキャッシュしておくことで、問題を解決しています。
我々も同様にページネーションの問題にぶつかり、同様にキャッシュを用いましたが、少し別のアプローチを取りました。1ページ目のときだけinterleavingするというアイデアも最初はありましたが、我々のアプリケーションでは検索結果は無限スクロールとなっており、ユーザがページを捲りやすいようになっています。またper_pageも基本20~30と小さいです。2ページ目以降もinterleavingするとなると、2ページ目のinterleavingした結果も保存する必要が出てきてしまいますが、検索結果全てのページをキャッシュするというのはあまり良い方法ではありません。加えて、保守性の観点で1ページ目だけに特別な意味を持たせることをしたくないという理由でページを限定してinterleavingする方法はやめました。
以上を考慮して、我々はページの代わりに「interleavingする片方のランキング」を固定することでページを渡るinterleavingを実装しました。
実装
我々は、片方の検索結果を全て取得してキャッシュに保存するという方法を取りました。 片方の検索結果を全件取得することで、どのページであってももう片方の検索結果から重複する結果を除外した上で検索することが可能になります。

我々のケースでは、interleavingしたい片方のランキングの長さは短く、対象の文書やクエリは限定的で、事前計算されています。また、返却する文書情報も多くないため、検索結果全体をキャッシュしても問題ないと判断しました。
この方法では、最初の検索では2回直列でESへの問い合わせをする必要がありますが、それ以降は通常の検索と並列にキャッシュへのアクセスをすることで、遅延の増加を抑えることができます。
今回、評価のためだけでなくランキングの仕組みの一つとしてinterleavingできるようにしたいため、interleavingを始める位置や長さ・文書の数を柔軟にコントロールできるような機能にしました。そのため、2ページ以降もinterleavingするときの課題として、ページ・per_page、今回はさらにinterleavingを始めるoffsetによって「どっちのランキングの文書から始まるか」が変わり複雑になるということがあります。ここで、単純にページとper_pageのみを考えるのであれば、per_pageとページが共に奇数のときのみ、検索結果の一番上がどっちから始まるかが変わるということがわかります。あとはここにoffsetを考慮して、愚直にどっちから始まるかを計算してあげればよさそうです。

キャッシュを用いた手法では、ページを切り替えるタイミングでキャッシュが切れると検索結果がおかしくなる可能性がありますが、今回のケースではキャッシュのttlを長めにとることができるため、問題にならないと判断しました。
この手法により、ページネーションの整合を保ったままページを渡ったinterleavingを実装することができました。我々のケースではキャッシュの肥大化も問題にならず、特別latencyの増加も認められませんでした。
まとめ
- Elasticsearchをサーチエンジンとしたinterleavingを実装しました。
- interleavingをサーバ側でやるとページネーションが壊れてしまうという問題がありますが、片方のランキングを全てキャッシュしておくことで解決しました。
We are hiring!
クックパッドでは現在絶賛採用活動中です。毎日の料理を楽しみにしたい皆様からのご応募を熱烈歓迎しております。まずはいま開いている枠を眺めてみてくださいませ。 https://cookpad.careers/