見出し画像

Algolia を利用した全文検索エンジンリプレイスの取り組み

こんにちは、株式会社 POL でエンジニアをしている山田高寛です。
株式会社 POL では研究を頑張る理系学生のための採用プラットフォーム LabBase を開発・運営していますが、今回はその LabBase で利用している全文検索機能をリプレイスして、Algolia を導入した取り組みについて共有したいと思います。 

LabBase の全文検索

LabBase は理系学生を採用したい人事担当者の方が学生を探してスカウトを送るサービスです。LabBase では全文検索を用意していて、理系学生の研究内容などを検索できるようになっております。この全文検索機能は以前まで、MySQL の Full-Text Search 機能を利用して実装していました [1]。

しかし、サービスが成長し、LabBase の利用者が増えるにつれて MySQL の Full-Text Search 機能を利用した全文検索機能では下記のような課題が浮き上がってきました。

・パフォーマンス
・パーサーが ngram しか利用できない

まず、1つ目の「パフォーマンス」について、MySQL を利用した全文検索では応答時間が平均して 4 秒ほどかかっておりました。次に2つ目の「パーサーが ngram しか利用できない」についてですが、Aurora MySQL を利用していたため、MeCab Full-Text Parser Plugin といった形態素解析を利用できませんでした。これらの課題に対処するために全文検索エンジンのリプレイスに取り組むことになりました。

リプレイス先の選定

リプレイス先の候補として "Elasticsearch" と "Algolia" の2つが上がりました。Elasticsearch は言わずもがなの有名どころですが、今回は下記のような理由から Algolia を利用することになりました。

・クラスタ運用・管理が不要
・料金が従量課金

まず1つ目についてですが、Algolia ではクラスタ管理などの運用面は簡略化されており、メモリやストレージ容量などを気にする必要がありません(Algolia にもドキュメントサイズなどの制限はあります)。また、我々は Elasticsearch の運用経験が無かったので、初めて検索エンジンを運用するには Algolia の方が適切かと思いました。

次に2つ目についてですが、Algolia の料金体型はデータ量(検索対象のドキュメント数)もしくは検索リクエスト数の大きい方に応じて課金されます [2]。検索エンジンを動かすために必要なサーバー起動時間では課金されません。この課金体系(個人的に真の従量課金と呼びたい)が、プロダクトの性質(日本の新卒採用活動は時期性がある)としてや、導入時の不確定な状況にフィットしそうと思いました。

Algolia を利用した新アーキテクチャ

LabBase での全文検索エンジンを Algolia に置き換える前と後のアーキテクチャは以下のようになっております。

画像5

画像6

検索リクエストについてはバックエンドサーバーにて Algolia と MySQL を参照して2つのデータを組み合わせて検索結果を返しています。Algolia 単体でも検索はできますが、後述するデータの一貫性のためにこのような構成にしております。

また、サービス上でユーザーのプロフィールなどが更新された場合、更新分を継続的に Algolia へ送信する必要があります。その箇所については AWS SNS と SQS を用いたメッセージキューを作成し、サービスのデータ更新系 API が呼び出されたら更新対象のデータをメッセージキューにキューイングして、Lambda がメッセージキューから更新対象データを読み取り、Algolia へ非同期にデータ更新するようにしました。このような構成には下記のような利点があります。

・サービス間を疎結合に保つ
・サーバーレス構成で運用コストを下げる

1つ目については、サービス間を疎結合にしておけば、既存システムの Algolia に対する依存をなるべく小さくできること、また、最悪 Algolia への移行を諦める決定をした場合にスムーズに元の構成に戻すことができます。2つ目については、せっかく Algolia で運用コストを少なくしたのに、他の場所で運用コストを生み出してしまうのは勿体ないことと思いました。

一方で、このような構成にしたことで、データの一貫性について考慮する必要がありました。MySQL 上のデータが更新されてから Algolia への更新が非同期で実施されると、Algolia 側で更新が完了されるまで MySQL 上と Algolia 上でデータの差分が発生します。基本的には結果整合性モデルの採用で対応できますが、強い一貫性が必要なデータもあったため、検索 API では Algolia から取得したデータと MySQL 側のデータを突き合わせてレスポンスを構築するようにしました。

ここが良いよ Algolia

画像5

一番の魅力は検索対象のドキュメントを用意するだけですぐに検索を実施できることです。データの投入は Algolia のコンソールから、もしくは Algolia の API を使用します。API については 10 種類以上のプログラミング言語の API クライアントが用意されているため、サービスにあったものを選択できます。また、各クライアント API は GitHub 上でソースコードが公開されているので、挙動の確認もできるし、バグなどがあればプルリクエストを出すこともできました(下記は自分が Kotlin API にプルリクエストを出した例 )。

データを投入した後は、Algolia のコンソールから検索もできますし、"UI Demos" というプロトタイプのためのページを発行することができるので、開発者以外でも Algolia を利用した検索がどのようなものなのかをすぐに試すことができます。更に、InstantSearch という UI ライブラリも用意されており、フロントエンドのみで全文検索が完結させる (フロントエンドから直接 Algolia を呼び出す) こともできます [3]。

また、Algolia は海外のサービスとはなりますが、検索クエリも日本語文章も扱えることができます。例えば、Transliteration 機能があるため、「きかいがくしゅう」で検索しても「機械学習」を含むドキュメントを検索することが可能です。

画像2

その他にも Algolia には様々な機能がありますが、ここで挙げきれないので、Alogolia の Solution Engineer である篠原さんのブログを参考にいただければと思います。Algolia のドキュメントは英語のみしか用意されていないため、篠原さんのブログには大変お世話になりました。

ここがむず痒いよ Algolia

今まで Algolia の魅力を語ってきましたが、残念ながら Algolia には弱いところもあります。

1つ目は、ネストした配列のフィルタリングはできない点です [4]。例えば、学生のプロフィールで「希望職種1位がプログラマ、かつ、 希望職種2位が寿司職人」という検索をしたい場合、Algolia に投入するデータを工夫しておく必要があります。下図の左側のようなデータ構造では望む結果を得ることはできず、右のようなデータ構造にする必要があります。

画像3

2つ目は、インデックスできるドキュメントのサイズ制限が最大で 100 KB までで、かつ平均して 10 KB 以内に収める必要があります [5]。もしこれらの制限を超える場合はドキュメントを分割して Algolia に保存して検索時にグルーピングするといった対応が必要になります。

このようにデータをインデクシングする際に考慮しなければならない点が多いため、Algolia へ投入するデータ構造を事前にきちんと検討することが必要です。

移行した結果

画像4

まず、検索速度については大幅に改善することができました。Algolia 側の応答速度は平均しても 150 ミリ秒以下に抑えることができており、ユーザー側から見た応答についても 1 秒以下の応答速度になっております。

今後の取組み

Algolia は機械学習系の機能についても力を入れており、検索ログや検索結果に対するイベント(クリックやコンバージョン)を Algolia に送信することで、それらのログをもとに検索ユーザーに応じた Recommend や検索結果の Persionalization を実施することができます。この機能の ON・OFF も Algolia コンソール上からボタンをポチポチするだけで設定可能です。今後はこれらの機能を導入して、よりよい検索結果をユーザーに提示できるようにしていきたいです。

番外編

今回 Algolia の利用事例として、Algolia 日本法人の篠原様に Podcast 上にてインタビューしていただきました!こちらのページから Podcast を聞くことができるのでぜひ聞いてください。

また、今回の取り組みについてもっと詳しくという方は Meety でお話しましょう!もちろん、その他の話についても歓迎しています。

参考

[1] MySQL :: MySQL 5.7 Reference Manual :: 12.10 Full-Text Search Functions
https://dev.mysql.com/doc/refman/5.7/en/fulltext-search.html

[2] Algolia Pricing | Pay As You Go | Site Search Pricing | Algolia
https://www.algolia.com/pricing/

[3] Design seamless search experiences with InstantSearch | Algolia
https://www.algolia.com/products/search-and-discovery/ui-component-libraries/

[4] How to match multiple attributes in nested object with numericFilters - Open Q&A - Algolia Community
https://discourse.algolia.com/t/how-to-match-multiple-attributes-in-nested-object-with-numericfilters/4887/9

[5] Is there a size limit for my index/records? | Basics FAQ | Docs Algolia
https://www.algolia.com/doc/faq/basics/is-there-a-size-limit-for-my-index-records/


この記事が気に入ったらサポートをしてみませんか?