検索API量産環境の短期構築、改善事例で分かる、「AWS Codeシリーズ」によるCI/CDパイプライン自動化のコツリクルート事例で分かるIaaS→PaaS移行のコツ(3)

リクルートの情報検索組織において検索APIの基盤をどうやってPaaS中心のシステムに移行したかを紹介する連載。今回は、API開発システムの全体構成と構築の流れについて解説する。

» 2022年08月23日 05時00分 公開
[佐藤万莉リクルート]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

 リクルートの情報検索組織において検索APIの基盤をどうやってPaaS中心のシステムに移行したかを紹介する本連載「リクルート事例で分かるIaaS→PaaS移行のコツ」。これまでの連載では、われわれのシステムの目標、それを実現するために導入した原理原則、これまでの技術選定の変遷について解説しました。

 連載3回目となる今回は、下記のように情報検索API(以下、API)の開発システムの全体構成とAPI構築の流れを解説します。

 われわれのシステムはクラウドネイティブ化を進めることで、API構築作業のうちユーザー体験やアルゴリズム以外の作業をできるだけ自動化しようとしていますが、いまだその途中にあり、手動で対処している作業があります。クラウドネイティブ化を進めているチームでどのように開発しているかの参考になれば幸いです。

想定読者

  • クラウドネイティブな開発基盤を活用してAPIを量産する体制に関心がある方
  • クラウドネイティブな開発基盤を活用してAPI量産環境を短期間で立ち上げるまでの実例を知りたい方
  • APIを立ち上げてから改善する方法に関心がある方

情報検索API開発システム全体構成

 前回のおさらいです。われわれのシステムは下記5つのコンポーネントから成り立っています。

コンポーネント 機能 目的
データ連携 検索対象のデータやログなどのデータに関するETL(Extract、Transform、Load)処理 検索対象の取得と検索品質を向上させるデータの取得
分析処理 データ分析処理(主にTransform処理) 検索品質向上のための前処理(自然言語処理やクラスタリングなど)
Index更新 検索エンジンElasticsearchへのIndexを格納するETL処理 Indexの更新
Elasticsearch 全文検索エンジン 検索機能の実行
検索APIサーバ RESTfulなAPIの提供 検索品質を向上させるクエリの解釈や他のAPIからの情報を利用

 API構築はこれらのコンポーネントを立ち上げる作業です。API構築の手順は下記のように各コンポーネントと対応付けられます。

順序 コンポーネント 対応する手順
1 データ連携 参照するデータファイルをテーブルに格納する
2 分析処理 SQLを実行して検索対象のデータをテーブル形式で作成する
SQLだけでは実行困難で複雑なデータ加工(自然言語処理や機械学習など)
3 Index更新 Google BigQueryのテーブルをElasticsearchにロードしてIndex作成
4 Elasticsearch Elasticsearchを管理する
検索クエリを作成する
5 検索APIサーバ 検索APIを実装、デプロイする

 API構築の手順を説明する前に、手順全体に共通するインフラ構築とジョブの実行環境について説明します。

インフラ構築

 インフラ構築では、前掲の5つのコンポーネントを立ち上げます。第1回、第2回で触れたように、われわれのシステムでは、ユーザー体験やアルゴリズムの試行錯誤以外のタスクを、フォーマット化/テンプレート化することを目標の一つに置いていました。

 従って、われわれのシステムではInfrastructure as Code(IaC)を促進し「AWS Cloud Development Kit」(以下、CDK)を導入してインフラ構築部分をコード化しました。API構築に必要なインフラを統一することで、インフラ構築部分を共通モジュール化し、APIごとに異なる部分のみ環境変数で定義しています。

 現在、われわれのシステムは、下記のようなインフラから成り立っています。

図1
機能 構成するインフラ
資材(データやログ)を配置する Amazon S3
テーブル Google BigQuery
ジョブ AWS CodePipeline
AWS CodeBuild
AWS CodeDeploy
検索エンジン Amazon Opensearch Service
検索API Amazon Elastic Container Service(ECS)
AWS Fargate
ロードバランサー ALB(Application Load Balancer)

 CDKは、共通モジュールとして「Construct」部分にインフラ構築部分を定義しています。開発者は、APIの構築するリソースごとに「Stack」を作成します。Stackでは環境変数のみ定義し、インフラ構築部分は「Construct」を呼び出しています。共通化を進めた結果、開発者はStackの環境変数を定義して「cdk deploy」コマンドだけでインフラ構築が完了するようになりました。

 同様に、「cdk destroy」コマンドによって、構築したインフラを全削除できます。これは、除却するときだけでなく開発環境をリセットして本番環境の構築リハーサルをするときにも役立ちます。

 余談ですが、「AWS CDK Intro Workshop」をやってみるとCDKの書き方のイメージがつかみやすいと思います。

ジョブの実行環境

 われわれのシステムでは、「AWS CodeBuild」(以下、CodeBuild)を使ってSQL実行やデータのロードなどのジョブを実行しています。CodeBuildの仕組みはこちらを参照してください。

 ジョブの実行環境はDockerイメージを使用しています。このDockerイメージはAmazon Web Services(AWS)、Google Cloud Platform(GCP)、Elasticsearchなど機能ごとに独立しています。

 CodeBuildジョブのビルド仕様ファイル(=buildspec)は「GitHub」から呼び出した共通のスクリプトを使用しています。buildspecからはチームで独自に開発したPythonコマンドを実行しています(各手順で実行しているコマンドの詳細は後述します)。テーブルの参照先、検索エンジンのURLなどAPIによって変わる部分はbuildspecに環境変数として渡しています。この環境変数は、CDKでCodeBuildジョブを構築する際にStackで定義された環境変数から設定しています。また、「Google BigQuery」(以下、BigQuery)やAWSの各サービスにアクセスするための認証情報はbuildspecに記述したくないので「AWS Secret Manager」から呼び出しています。

情報検索APIを立ち上げるまでの流れ

 構築手順を説明します。前のセクションで説明したインフラを全て構築した状態からスタートします。各セクションでは、手順の他に、自動化できる点とそうでない点に焦点を当てます。

データを連携させてテーブルに格納する

 この手順では、「AWS Glue」(以下、Glue)を使って後続の分析処理に必要なデータファイルをBigQueryテーブルにロードします。例えば、下図左のndjson形式のファイルは下図右のBigQueryテーブルになります(図2)。Glueジョブの仕組みはこちらを参照してください。

図2

 連載第2回でも触れた通り、われわれのシステムではデータ連携の方式として、以下の2つの形式をサポートしています。

  • Amazon S3(以下、S3)へのPOST
  • BigQueryのテーブル参照

 分析処理に必要なデータが全てBigQuery上にある場合はこの手順は不要です。

 この手順は、Glueジョブの作成作業、Glueジョブの実行の2つのステップから成り立ちます。開発者はGlueジョブを作成して、「AWS CodePipeline」(以下、CodePipeline)の「変更をリリースする」ボタンを押すとS3に配置されたファイルからBigQueryのテーブルが作成されます。

 2回目以降はCodePipelineを流すだけで、この手順は完了します。

・Glueジョブの構成要素の共通化

 Glueジョブの構成要素は共通化されています。例えばデータを変換するスクリプトは、システム共通のカスタムスクリプトを使用しています。また、GlueからBigQueryへの接続は「AWS Marketplace」が提供する「AWS Glue Connector for Google BigQuery」を使っています。カスタムコネクターの使い方はこちらの記事が分かりやすかったのでぜひ参考にしてください。

・Glueジョブ実行の自動化

 CI/CD(継続的インテグレーション/継続的デリバリー)の考え方に従い、データ格納はCodeBuildジョブを使って自動化しています。CodeBuildジョブがビルドされると、Glueジョブを実行するコマンドが呼び出されます。このコマンドは、「boto3」というPython向けのAWS SDKを使って実装されていて、Glue APIを呼び出してGlueジョブを実行しています。

・改善点

 現行のシステムを開発し始めた2021年5月ごろはCDKのGlue関連のPythonライブラリがなかったので、Glueジョブ作成の自動化には至りませんでした。開発者はチームで用意した詳細な手順書を見ながら、「Glue Studio」のコンソール画面からGlueジョブを手動で作成しています。

 われわれのチームでは自動化できない作業は暫定的に手動で対応していますが、将来的には、このGlueジョブ作成作業も自動化したいと考えています。

テーブルデータを作成する

 前のセクションまでで、データソースのBigQueryテーブルを準備できました。この手順では検索対象のデータをBigQueryテーブル形式で作成します。

 テーブルは検索対象の1アイテムにつき1行で、アイテムのIDの他に、絞り込み対象の属性(例:カテゴリー、都道府県)や並び順で参考にする属性(例:価格、事前に計算できるスコア)があります。

 要件にかかわらず、情報検索APIはユーザーが必要としている情報、すなわち、情報ニーズに関連するアイテムを順序付きリストの形式で返します。この検索結果は、リストの先頭にあるアイテムの関連度が最も高く、リストの後ろになるほど関連度が下がる並び順になっています。

 アイテムの並び順は、ユーザーの入力に依存する動的なスコアと、ユーザーの入力に依存せず事前にアイテムごとに計算できるスコア(以後、事前スコア)の2種類で決まります。

 動的なスコアの例として、ユーザーの入力した検索キーワードとアイテムの文字列のマッチ度が挙げられます。

 事前スコアはユーザーの入力に依存しない形で、「アイテムがユーザーの情報ニーズをどれだけ満たしているか」を表現します。例として、情報の鮮度や過去のアクセスログから集計した人気度が挙げられます。

 動的なスコアの計算方法や、最終的なスコアの計算方法は次の「Elasticsearchの検索クエリ作成」のセクションで説明します。

 この手順はコンポーネントの分析処理に対応し、SQLの作成作業、SQLの実行の2つのステップから成り立ちます。開発者はSQLを作成して、CodePipelineでSQLを発行して一連の事前スコアの計算や自然言語処理などの一連の処理を実行します。

・SQL実行の自動化

 CI/CDの考え方に従い、BigQueryテーブルの作成はCodeBuildジョブを使って自動化しています。CodeBuildジョブが起動されると。SQLを実行するコマンドが呼び出されます。コマンドはBigQuery APIのPythonクライアントを利用して実装します。

・SQL作成の進め方

 検索したいデータの性質も事前スコアのアルゴリズムもAPIごとに異なるので、SQLの内容は共通化できません。開発者は、最初はシンプルなSQLでテーブルを作成し、ブラッシュアップの過程で次のような改善を加えます。

 ・事前スコアのアルゴリズムを改善する

 ユーザーにとって検索結果の上位にあるとうれしいアイテムの特徴を考え、アルゴリズムに反映します。

 例えば、時期によってアイテムの人気が変動することが考えられるなら、過去1年間だけでなく、季節やイベントの流行を考慮して過去1カ月間、突発的な流行を考慮して過去3日間の人気度をそれぞれスコアとして計算できます。

 ・絞り込み対象の属性や並び順で参考にする属性を追加する

 ユーザーの情報ニーズによって柔軟に対応するために、絞り込み対象の属性や並び順で参考にする属性を追加します。追加する属性は、データウェアハウスにある他のテーブルのデータをアイテムにひも付けることもあれば、既に使っているテーブルのデータから抽出することもあります。

 例えば、正規表現を使って構造化されていないフリーテキストから数値や文字列を抽出できます。

 ・検索の精度を上げるためにデータを変換、加工する

 検索ロジックでは複数の属性から並び順を決めます。属性間で値の表現がばらばらだと並び順を決めるロジックが機能しません。検索の精度を上げるためにはデータを変換したり、加工したりする必要があります。

 例えば、数値データは値の範囲が違ったり、値の分布に偏りがあったりすると正しく比較できません。この問題を解消するためには、「連続値を離散化する」「値の範囲が大きい場合は正規化する」「値の分布に偏りがある場合は対数変換する」「年月日の表記やタイムゾーンは統一しておく」など、数値データの処理が必要です。

・改善点

 試行錯誤を重ねるにつれ、SQLおよびSQL実行のワークフローは複雑になってしまいます。SQLもワークフローも、複雑さが増すとコードの可読性が下がり、開発者だけでなくレビュワーにとっても理解しづらいものになってしまいます。

 詳細は連載第5回で解説しますが、次のような改善策を考えています。

  1. よく使う処理は永続的なUDF(User Defined Function)として切り出して共通モジュール化する
  2. GCPの「CloudFunctions」を導入してPythonでシンプルに実装する
  3. 「dbt」(data build tool)を導入してワークフローを整理する

テーブルデータを加工する

 前のセクションでは、検索対象のデータを作成する手順について説明しました。基本的にはBigQueryのSQLを利用しますが、形態素解析器を使った読み仮名の付与や分散表現(Embedding)の追加など、SQLで実装するのが難しい処理もあります。このような処理はGCPの「Dataflow」を使ってPythonで実装しています(図3)。Dataflowについてはこちらを参照してください。

図3

 われわれのシステムでは、Dataflow用のDockerイメージを「Amazon Elastic Container Registry」(ECR)に登録しておき、CodeBuildでそのイメージを指定して、Dataflowを実行しています。

 この手順はコンポーネントの分析処理に対応しています。開発者はDataflowのパイプラインを実装し、DockerイメージをビルドしてECRにプッシュします。CodePipelineを実行すると、データ加工済みのテーブルがBigQuery上に作成されます。

・データ加工処理の共通化

 形態素解析によってやりたいことを整理して、次のような処理を共通モジュールとして切り出しました。

  • 読み仮名付与
  • ローマ字読み付与
  • 品詞解析
  • 漢数字を算用数字に変換

・Dataflowジョブ実行の自動化

 CI/CDの考え方に従い、Dataflowパイプラインの実行はCodeBuildジョブを使って自動化しています。CodeBuildジョブがビルドされると、パイプラインを起動するPythonスクリプトが実行されます。パイプラインを起動するPythonスクリプトは「Apache Beam SDK」を使って実装されています。

・改善点

 Dataflowのパイプライン作成はテンプレート化には至っておらず、「Apache Beam」の書き方に不慣れな開発者には時間のかかる作業になっています。一方で、BigQueryからリモート関数が呼び出せるようになり、形態素解析APIを使ってDataflowと同様の処理ができるのではないかと考え始めています。テンプレート化を進めるか、別のツールに移行するか、われわれのチームでも試行錯誤を重ねている途中です。

ElasticsearchにIndexを作成する

 前のセクションまでで、テーブル形式の検索対象データが完成しました。この手順では、Glueを使ってElasticsearchにIndexを作成し、BigQueryテーブル形式の検索対象データをElasticsearchにロードします(図4)。

図4

 この手順は、コンポーネントのIndex更新部分に対応しています。われわれのシステムでは、定期的にジョブを流してIndexを作り直し、検索APIから参照するエイリアスを貼り替えることでデータを全件更新しています(図5)。

図5

・クラウドネイティブ化の4つの指針

 この仕組みは、連載第2回で解説したクラウドネイティブ化の4つの指針を次のように実現できます。

指針 実現方法
シンプル化 Glueを使ってBigQueryテーブルをElasticsearchにロードするというシンプルな作りになっている
一貫性 BigQueryのテーブルとElasticsearchのIndexの中身が一致している状態
安定運用 たとえ障害でIndexが消えたとしても、BigQueryテーブルをElasticsearchにロードすれば元の状態に戻すことができる
フレキシビリティの向上 CodeBuildジョブを実行するだけでIndexを作り直すことができる

 検索対象データのリアルタイム更新やバッチ更新は、トランザクションのログを追ってデータを復元しなければならない点で4つの方針から外れているといえます。

 一方で、全件更新には、サービスに影響がでないように更新フローを高速に完了させなければならないという制約がありました。この制約を満たすために、われわれのシステムでは、データの生成/取得/更新/削除のうち更新と削除をスコープから外して、高速な全件更新に注力しました。詳細は「短時間でロードするための設定調整」で解説します。

 これまで携わったプロダクトの性質上、リアルタイムの更新と削除の機能がなくても大きな悪影響はありませんでしたが、今後、4つの方針を阻害しない形で更新機能を追加する可能性も検討しています。

・Glueジョブの共通化、自動化

 データ連携と同様、Glueジョブの構成要素は共通化されています。接続プロパティはMarketplaceの「Elasticsearch Connector for AWS Glue」を使用しています。

 CI/CDの考え方に従い、Index作成はCodeBuildジョブを使って自動化しています。CodeBuildジョブがビルドされると、Glueジョブを実行するコマンドが呼び出されます。コマンドの中身はデータ連携と同じなので割愛します。

・短時間でロードするための設定調整

 連載第2回でも触れましたが、ElasticsearchのIndex作成では大量のデータをテーブルからElasticsearchに送る必要があります。大規模なIndexの場合は「Amazon OpenSearch Service」のスペックを上げたり、Glueジョブの設定を変更して同時実行数を増やしたりすることで、短時間で大量のデータをロードできるようになります。

Elasticsearchを操作、管理する

 前のセクションまでで、ElasticsearchにIndexが作成されました。この手順では、ElasticsearchやIndexを操作します。具体的には次のような機能があります。

  • 検索APIから参照するIndexを切り替えるために、稼働中のIndexからエイリアスを削除し、新しく作成したIndexにエイリアスを付与する
  • 検索性能を上げるためにIndexを効率化する処理を実行する(Force merge)
  • Elasticsearchを停止させることなく安全に再起動する

・ジョブ実行の自動化

 CI/CDの考え方に従い、Elasticsearchを操作する手順はCodeBuildジョブを使って自動化しています。CodeBuildジョブが起動されると。Elasticsearchを操作するコマンドが呼び出されます。コマンドの内部では、何を操作するかによって使用するライブラリを変えています。

 Indexの操作はElasticsearchのPythonクライアントを使っています。

 ドメインの操作はboto3を使っています。

 ドメインの再起動は、「ドメインの設定を変更するとブルーグリーンデプロイが走る」というAmazon OpenSearch特有の機能を活用しています。EBSの設定をわずかに変えてブルーグリーンデプロイを発生させることで、Elasticsearchを停止させることなく安全に再起動できるようになりました。

Elasticsearch検索クエリを作成する

 前のセクションまでで、エイリアスが付いたIndexが完成しました。この手順では検索ロジックを実装します。

・検索クエリ作成の進め方

 APIごとに検索ロジックは異なるので、クエリ作成は開発者が一から作成し、リリースまで試行錯誤を重ねる作業です。

 われわれのチームでは主に「Function score query」を使用しています。query部分と絞り込みロジック、functions部分とスコアリングのロジックがそれぞれ対応します。

 スコアリングのロジックでは、「ユーザーの入力がアイテムのどのフィールドとマッチすると何点加点するか」「どのように事前スコアとブレンドするか」を定義します。

 「Kibana」上で適当な検索キーワードや検索条件を想定してElasticsearchクエリを作成してから、クエリの可変の(検索キーワードや検索条件など、ユーザーの入力によって変わり得る)部分を変数に置き換えて「Search template」形式に落とし込みます。Search templateが正しく書けているかどうかはElasticsearchのレンダリングAPIを使って確認します。

検索APIを実装する

 前のセクションまでで、検索機能のコア部分を実装しました。この手順では検索機能の入出力部分のロジックを実装します。具体的には、クライアントからのリクエストをElasticsearchへのリクエストに変換するロジック、Elasticsearchからのレスポンスをクライアントへのレスポンスに変換するロジックを実装します。

 その過程で外部APIと通信することもあります。検索APIはElasticsearchにとってのクライアント、外部APIにとってのクライアントの役割を果たしています。実装したAPIは開発者のローカル環境でDockerイメージをビルドしてECRにプッシュします。

・API仕様書作成の自動化

 API実装以外では、API仕様書はyaml形式で書いた「OpenAPI Specification」を「ReDoc」でHTML形式にレンダリングして自動生成しています。

・検索API実装の共通化

 検索APIサーバの実装に必須な機能は共通モジュール化しています。例えば、他のAPIを呼び出すHTTPクライアントの作成、パラメーターのバリデーション、APIを起動するときの引数設定、ロガー作成、死活監視で使うpingエンドポイントなどが該当します。

 とはいえ、コードの大部分を占める、他のAPIとの通信部分やリクエスト加工/レスポンス加工部分のロジックはAPIごとに異なるので、コードの共通化には限界があります。

・検索API実装の進め方

 最初はリクエストパラメーターの値をそのままSearch templateの変数に渡すくらいのシンプルさで実装しますが、検索ロジックをブラッシュアップするにつれて複雑さが増していきます(詳細は後述)。

 われわれのチームでは、最初は時間をかけずにシンプルな要件でAPIを実装し、関係者にフィードバックをもらいながらロジックをブラッシュアップすることを大切にしています。APIをリリースするまでに、検索APIに変更を入れてデプロイすることを何度も繰り返すことになります。

検索APIをデプロイする

 前のセクションまでで検索APIの実装が完了し、ECRに最新バージョンのイメージが格納されました。この手順では最新バージョンの検索APIをデプロイします。

 われわれのシステムでは、「AWS CodeDeploy」(以下、CodeDeploy)を使ってECSのサービスを入れ替えることで、検索APIをデプロイしています。

・デプロイ作業の自動化

 CI/CDの考え方に従い、検索APIのデプロイはCodeDeployを使って自動化されています。開発者がAPIのイメージをECRにpushすると、CodeDeployが「AWS CodeStar」に登録されたECSの設定ファイルを元にAPIのECSサービスを立ち上げ、ブルーグリーンデプロイが始まります。

Locustを使ったIntegration Test、検索品質テスト、性能試験の実施

 前のセクションまでで、APIの構築が完了しました。このセクションでは、毎回のデプロイでは実施しないものの、リリース前のIntegration Test、検索品質テスト、性能試験について説明します。

 それぞれの目的、入力、テスト内容は下記のように整理できます。

テスト分類 目的 入力
Integration Test APIが要件通りに動くかどうかを確認する 要件をハードコーディングした入力値
検索品質テスト レスポンスの件数を検証することでIndexの品質を確認する 要件をハードコーディングした入力値
性能試験 実運用での負荷を想定し、アクセスログから作成したリクエストを投げてリソース観点で問題がないことを確認する 過去ログ

 連載第2回でも触れた通り、われわれのチームでは3つのテストが共通項目を持つことに着目し、実行方法を「Locust」に統一してPythonでテストシナリオ(=locustfile)を書いています。

 Locustの実行環境はDockerイメージにしていて、ECSで実行できるようになっています。同じDockerイメージを利用することで、CodeBuild上でのIntegration Testでの利用に流用することも可能になりました。locustfileの書き方については、詳しくはこちらを参照してください。

手順

 負荷をかけるタスクが立ち上がると、BigQueryにSQLを発行して入力値のリストを取得します。各タスクではリストからランダムに値を取得してAPIにリクエストを送ります。

 Integration Testや検索品質テストの場合は、レスポンスが要件通りになっているかどうかを検証します。検証結果はレスポンスの要素をログに出力して、「Amazon CloudWatch」(以下、CloudWatch)の「CloudWatch Logs」から確認できます。われわれのシステムではログ形式をJSONに統一しているので、簡単な集計ならCloudWatchの「Log Insights」から集計できます。複雑な集計の場合はBigQueryにロードしてSQLで集計します。

 性能試験の場合は、Locustのダッシュボード(ドキュメントでは「Web UI」)から1秒当たりのリクエスト数(RPS)やレスポンスタイムのパーセンタイル値を確認します。性能試験の結果は、試験後にBigQueryに格納されます。検索エンジンのリソース使用状況はAmazon OpenSearch Serviceのダッシュボードからでもモニタリングできます。

改善点

 検索条件が多いAPIの場合、レスポンス要素の検証が複雑になり、テストケースの作成に時間がかかってしまいがちです。詳しくは連載の後の回で解説しますが、「Postman collection」などのツールを使って、APIのドキュメントとして使っているyamlを検証用のJSONに変換することを考えています。

検索のユーザー体験のブラッシュアップ

 ここまで、API構築の手順を追いながら、どのように共通化、自動化を進めてきたかを説明しました。共通化を進めた結果、開発者は検索のユーザー体験に関わってくる、SQL、Elasticsearchクエリ作成、検索API実装のブラッシュアップに開発期間の多くを費やすことができるようになりました。

 このセクションでは、われわれのチームがこれまでに取り組んだ案件から、検索のユーザー体験の改善例を紹介します。

検索キーワードの分類

 同じ検索画面を使っていても、ユーザーが何を重視しているかは異なります。例えば、次のような分類が考えられます。

  • エリア(駅/都道府県/市区町村)
  • ジャンル
  • こだわり条件
  • 安さ
  • 人気度

 ある案件では、分類対象のキーワードのIndexを作成して検索キーワードをクエリとして投げ、検索キーワードを分類しました。分類対象のキーワードは、基本的にサービスの検索条件のマスターデータを流用していますが、ジャンルやこだわり条件はマスターデータだけでは網羅できませんでした。そこで、検索対象のIndexをアグリゲーションして、ジャンルやこだわり条件に関連しそうなキーワードを抽出することで分類対象のキーワードを拡充しました。

 ユーザーが重視している部分が異なる場合、絞り込みやスコアリングのロジックも変わってきます。検索キーワードを分類することで、検索ロジックを細分化してユーザーの検索意図に柔軟に対応できるようになります。例えば次の方法があります。

  • カテゴリーごとに異なるSearch templateを適用する
  • 同じSearch templateの中で条件分岐して、カテゴリーごとに絞り込むフィールドを変更する
  • 同じSearch templateの中で条件分岐して、カテゴリーごとにキーワードでマッチしたときに加点するフィールドを変更する

検索キーワードの拡張

 ある案件では、絞り込み条件が厳しくてアイテムがヒットしない場合でも関連するアイテムを検索結果に返せるように、検索キーワードを拡張しました。例えば、具体的過ぎるジャンル、小規模なエリアや駅を指定して絞り込んだときに、次の項目を検索キーワードに追加しました。

  • 指定されたジャンルより大きな“くくり”のジャンル
  • 指定されたエリアより大きな“くくり”のエリア
  • 指定された駅に隣接する駅

 また、ユーザーが入力した検索キーワードでマッチしたアイテムが、拡張したキーワードでマッチしたアイテムより上に来るようにスコアリングを工夫しています。

スコアリングの改善

 「Elasticsearchクエリを作成する」のセクションでも触れた通り、われわれのチームではFunction score queryを使っています。そのfunctions部分では、「ユーザーの入力と、アイテムのどのフィールドとマッチするとどれくらい加点するか」というスコアリングのロジックを実装します。

 ある案件では、スコアリングを改善するためにユーザーが重視するフィールドを分析しました。具体的には、「カセットに表示されているフィールドの加点を大きくする」「ドメインの知識が豊富なサービスの関係者にヒアリングして、ユーザーが重視している情報に関連するフィールドの加点を大きくする」といったことです。

 別の案件では、Locustを利用して過去6カ月分の検索キーワードをIndexに投げ、どのフィールドにヒットしやすいか分析しました。

参考

 より包括的な改善手法については「検索システム ― 実務者のための開発改善ガイドブック」(打田智子、古澤智裕、大谷純、加藤遼、鈴木翔吾、河野晋策 共著、ラムダノート刊)の「第11章 検索を成功させるための支援」を読んでみてください。

共通化と自動化は何をもたらしたのか

 今回は、APIの開発システムの全体構成とAPI構築の流れを解説しました。共通化を進めた結果、開発者は短期間でAPIを構築できるようになり、開発期間の多くを検索のユーザー体験のブラッシュアップに注力できるようになりました。フレキシブルなシステムになったことで、改善してから関係者からフィードバックを得るまでのサイクルを高速化することもできました。

 リリースまでに何度もAPIをブラッシュアップすることで、質の高い検索APIを提供できるようにもなりました。リリース後のA/Bテストでも、質の高さがサービスに貢献していることを実感しています。

 一方で、API構築の各手順でも触れたように、われわれのシステムはクラウドネイティブ化の途上にあります。開発者が快適に高速にAPI構築できるようになるまでには、まだまだ改善の余地があると考えています。これからもシステムと検索ロジックのアップデートを重ねて、検索のユーザー体験をより良くする情報検索APIを作成していきます。

筆者紹介

佐藤万莉

2014年、同志社大学理工学部入学

2018年、京都大学大学院情報学研究科に入学(情報学修士)

2020年、リクルートに入社、検索ソリューショングループに配属


Copyright © ITmedia, Inc. All Rights Reserved.

スポンサーからのお知らせPR

注目のテーマ

Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。