Webアプリエンジニアの加瀬です。
2023年の3月にクチコミをキーワード検索できる機能をリリースし、その後検索速度の改善を実施しました。
Elasticsearchの検索ロジックを変更することで検索速度の改善を実現したのですが、その時の対応内容について今回書き留めてみたいと思います。
クチコミキーワード検索機能について
OpenWorkに掲載されている求人に応募すると、OpenWorkのサービス内で利用可能な「応募特典」が一定期間解放されます。この応募特典の1つに「クチコミキーワード検索」機能があります。
クチコミキーワード検索機能を利用することで、企業に投稿されたクチコミを任意のキーワードで検索することが可能になります。「体育会系」や「残業」といったキーワードでクチコミを絞り込めるため、企業分析をスムーズに進めることが可能です。
クチコミキーワード検索にはElasticsearchを使用しています。リリース当初は検索クエリにwildcardクエリを採用し、指定のキーワードを含むドキュメントを検索していました(SQLでいうところのLIKE検索)。
"wildcard": { "<フィールド名>": "*<検索キーワード>*" } ※クエリイメージ
wildcardクエリを採用していた時に存在した課題
wildcardクエリによる検索の場合、主に以下の課題が存在していました。
検索時間が長かった
リリース後に本番環境でキーワード検索の実行速度を計測したところ、平均で約3.9秒かかっていました。検索の度に待たせてしまう、ストレスを感じさせてしまう状態となってしまっていました。
表記揺れに対応できない
wildcardクエリによる検索の場合、指定されたキーワードを含むドキュメントのみがヒットし、少しでも表記が異なるドキュメントはヒットしません。例として、「OpenWork」で検索すると「openwork」を含むドキュメントはヒットしません。大文字小文字、全角半角の違いに対応できないため検索の利便性に課題がありました。
やったこと
上記の課題を解決するため、Elasticsearchの検索クエリを変更することにしました。主に以下の対応を行いました。
query stringクエリに変更した
wildcardクエリを使用していましたが、query stringクエリを使用する形に変更しました。
"query_string" : { "query" : "<検索キーワード>", "auto_generate_phrase_queries" : true, "fields" : {<各フィールド>} } ※クエリイメージ
query stringクエリを選択した理由としては、検索クエリを組み立てることで柔軟な検索を実現できること、及びOpenWorkのサービス内でこのクエリを使用している箇所があったことが挙げられます。
クエリの変更に伴い、検索対象のデータのフィールドタイプをkeyword型からtext型に変更しました(Elasticsearchに格納していたクチコミデータについて、text型とkeyword型のマルチフィールドにしていたためマッピング設定の変更は行いませんでした)。
検索方法とクエリ、フィールドタイプをまとめると以下のようになります。
クエリ | フィールドタイプ | |
---|---|---|
リリース当初 | wildcard | keyword型 |
変更後 | query string | text型 |
クエリ文字列の組み立てをサーバーサイドで実施
query stringクエリに渡すクエリ文字列の組み立てを検索前に実施しています。主に以下のことを行っています。
空白文字の事前処理
入力された文字列の前後に空白が含まれていると、クエリ組み立て時に不正なクエリとなってしまうため除去します。 また文字列内の全角スペースを半角スペースに変換します。
クエリの組み立て
「残業 有休」のように空白区切りで各単語が入力される形を想定しているため、入力された文字列を空白区切りで単語分けし、それらをand演算子で結合する処理を行います。演算子が入力されている場合はそのまま使用します。
例として、以下のような形でクエリ文字列が組み立てられます。
「残業 有休」 → 「残業 and 有休」(空白で単語が繋がっている場合、and演算子を追加する) 「残業 or 有休」 → 「残業 or 有休」(演算子が入力された場合はそのまま使用)
query stringクエリの仕様やクエリ文字列の構文については公式ドキュメントをご参照ください。
どの程度効果があったか
検索速度
wildcardクエリを用いていた時は平均で約3.9秒かかっていましたが、query stringクエリに変更したところ約10倍早く検索できるようになりました。
平均実行時間(100件) | |
---|---|
wildcardクエリ | 約3.88秒 |
query stringクエリ | 約0.34秒 |
検索の待ち時間によるストレスを大幅に軽減することができました。
その他に解決できた課題
表記揺れへの対応
英字の大文字小文字や全角・半角の違いによる表記揺れを吸収して検索できるようになりました。例として、
「openwork」で検索 → 「OpenWork」もヒットする 「10」(全角)で検索 → 「10」(半角)もヒットする
のように幅広くヒットするようになるため、目的とするクチコミが探しやすくなっています。
複数キーワードによる検索
wildcardクエリによる検索では入力したキーワードをそのまま検索に使用するため、複数キーワードによる検索ができない状態でしたが、query stringクエリでは複数単語によるクチコミの検索が可能になっています。
「残業 有休」で検索 → 「残業」「有休」をどちらも含むクチコミがヒットする 「残業 or 有休」で検索 → 「残業」「有休」のどちらかを含むクチコミがヒットする
演算子を用いた柔軟な検索が行えることも、query stringクエリを使用する利点かと思います。
まとめ
クチコミキーワード検索機能における検索クエリをwildcardクエリからquery stringクエリに変更することで、様々な課題を解消することができました。 query stringクエリを採用したことで様々な利点がありましたが、指定キーワードを含まない(表記が少し異なる)ドキュメントもヒットするようになるなどの影響も出るので目的に適した方式を採用するのが良いかなと思います。
Elasticsearchについてあまり詳しくない状態での開発でしたが、検索速度改善の実装を通して仕組みやクエリの性質など様々なことを学ぶことができました。調査から実装まで担当することができて楽しかったです。
さいごに
オープンワークでは一緒に開発をしていただけるエンジニアを募集しています!ご興味のある方は、ぜひ採用サイトを覗いていただけると嬉しいです。