プライベートのサービスでは、手頃な (かつ安く運用できる) 全文検索エンジンとして、 Meilisearch を使っています。
わたしの Oracle の安いインスタンスでも (インデックス数が少ないのが大部分の理由ではあるとは思いつつ) 高速に動いてくれているので大変満足しています。 ところで、ある日サービスのユーザーから「うちの投稿が表示されない」と報告があったので調査してみたところ、検索エンジンあるあるの問題にぶち当たったのでメモがてら残しておきます。
うちのプライベートのサービスでは、 Google 検索のような一致度検索というよりかは、 Yahoo! リアルタイム検索や Twitter の検索の「最新」タブのような、とりあえず新しい投稿のうち一致しているものだけをフィルタリングする、といった要件がありました。 しかし Meilisearch は気を利かせて一致度で検索をしてくれるので、そこをカスタマイズしたい、さてどうしたものか、というのが今回のテーマ。
やり方としては該当インデックスのランキングルールを変更することで実現できます。
デフォルトではドキュメントにあるとおり、以下の順でスコアが決定されます:
- words
- typo
- proximity
- attribute
- sort
- exactness
このうち5番目の sort
は検索時に指定されるソート順で、ユーザー (今回の場合はサービス) が任意の値を入力することが出来ます。
他の5項目についてはそれぞれ以下のようなイメージです:
- words - クエリと一致した語数順
- typo - typo の少ない順 (ワードの一致度)
- proximity - 複数単語入力した場合の、単語間の距離が少ない順
- attribute - より重要なカラムにクエリが含まれる順
- exactness - ワード全体の一致度が高い順
今回の要件を満たすにはどの順番に、もしくはどのルールを使用するかは、 Meilisearch の検索時オプションに showRankingScore
と showRankingScoreDetails
を指定することでチューニングしていくことが出来ます。
例えば、今回の場合以下のようなスコアとスコア情報が返されました。
// データは省略して、それぞれルールごとのスコアのみを表示しています [ // 特に問題の無かった投稿 { id: 1, body: "xxx", score: 0.9090909090909091, score_detail: { words: 1, typo: 1, proximity: 1, attributes: 0.8181818181818182, sort: 0, exactness: 0.3333333333333333 } }, // ...(省略) // 出てきて欲しいが、今回スコアが低くて表示されなかった投稿 { id: 200, body: "yyy", score: 0.6363636363636364, score_detail: { words: 1, typo: 1, proximity: 1, attributes: 0.2727272727272727, sort: 0, exactness: 0.3333333333333333 } }, ]
この場合、出てきて欲しくないものはそもそもスコアが0のため表示されなかったこと、重要なスコア (words, typo, proximity) はすべて一致していること、 attributes が必要以上に低く出ていることなどから、以下のようにすることで、要件を満たすことが出来ました。
- words
- typo
- proximity
- sort
attribute と exactness は今回の要件では特に必要が無い (検索している attribute は1つだけであること、 exactness は部分的に一致していれば良かったこと) などから省き、 sort として created_at:desc
のような値を指定することで、いわゆる最新順でフィルターするといったことが可能になりました。
ランキングルールの更新は API 経由だったりで行うことができ、今回の場合は下記ページ・エンドポイントを見る・実行することで実現できました。
ということで、今回は Meilisearch のチューニング?話でした。ということでまた来月。