はじめに
こんにちは。バイセルテクノロジーズ テクノロジー戦略本部 開発 2 部の小松山です。
バイセルはリユース事業を行っており、日々、たくさんの商品をお客様から買い取っています。
開発部では、買い取る際に、値付けの参考となるデータをスタッフが閲覧できるサービス「Promas」を提供しているのですが、検索結果から探している商品にたどり着きづらいという課題がありました。 本記事ではこの課題を Elasticsearch を利用してどのように解決したかを紹介します。
商品マスタサービス「Promas」とその課題
まずは商品情報管理サービス「Promas」とは何かについて軽く紹介します。
Promas はあらゆる商品情報をマスタ化し、商品情報の管理を一元化・買取・販売実績の蓄積・値付けの補助などが行えるサービスです。現在は既存の買取の基幹システムに組み込まれ、ブランドバッグの査定業務で使用されています。査定時に商品マスタを検索し、商品情報の入力補完と値付けの補助を行うことで査定の高速化を図っています。
Promas とはどういったプロダクトか、どのように導入されていったかという点に関して、下記の note の記事でも紹介されています。気になった方はご覧ください。
課題
弊社では日々多くの査定が行われています。買取システムの操作に時間がかかると、ユーザ (査定員) だけでなく、査定を待っているお客様のストレスにもなってしまいます。そのため、買取システムは速くレスポンスを返すことができる必要があります。買取システムの操作時間を減らし、査定全体の時間を減らすことで単位時間あたりの査定数が増えて買取数の向上も見込めます。
そのため査定時間を減らすことを Promas チームの目標としていました。そのためにはいくつかの問題があったのですが、そのひとつが「似た商品名が多いため、マスタの特定までに時間がかかる」という問題でした。例えば、現在 Promas の DB に格納されている商品情報で、商品名に「ネヴァーフル」が含まれる商品は 75 件、商品名に「ジッピーウォレット」が含まれる商品は 167 件存在します。査定を行うユーザは商品名で検索しにいくことが多く (2022 年 7 月に行われた商品マスタ検索のリクエストの約 78 %で商品名が指定されている)、検索結果に表示された数十件もの似た選択肢の中から、査定に必要な商品マスタを選ぶのは時間がかかってしまいます。
Elasticsearch を活用した解決方針
全体像
私達は上記課題の解決策として、よく取引される商品を検索結果の上位に持ってくる、という対応を行いました。同じ商品名でも頻出する商品が上位に表示されれば「ユーザが商品を検索結果から選ぶ負担は減り、査定時間の短縮につながる」と考えました。
前述の通り、査定にはスピードが求められるため商品検索には Elasticsearch を使っています。今回の検索に対する課題は Elasticsearch を活用して解決しました。解決策について説明する前に Elasticsearch 周りのアーキテクチャについて概要を説明しておきたいと思います。 Promas では Elasticsearch を GKE に載せて運用しています。インデックスには Promas のデータベースで保持している商品情報が入っており、インデックスとデータベースを同期するバッチが 1 時間おきに実行されています。
技術選定の経緯などの詳細は下記記事でも触れていますのでよろしければご覧ください。
取引実績スコアの算出
Promas では、買取実績・販売実績を定期的に集計して、データベースに保存しています。この実績データは査定システムから定期的に取得してきているため「よく取引される商品」というのは商品データに紐づく実績データの件数の多さと読み替えることが出来ます。 実績データをベースにした「実績スコア」をデータ同期用のバッチの中で算出し、ドキュメントのフィールドに追加するようにしました。取引量が多い程、実績スコアは高くなります。
これで、取引数の観点で商品データを比較することができるようになりました。次は「検索時にどうやってこのスコアを加味して検索結果を返すか」という点が問題になります。前提として、Elasticsearch は検索クエリを投げると、クエリで指定したパラメータとドキュメントのマッチ度合いを自動的にスコア計算してくれます。検索結果はスコアが高い順にソートされて返ってくる仕様になっているため、デフォルトでは検索結果は検索パラメータとのマッチ度合いが高い順で並びます。
今回目指すのは「商品名を検索パラメータに指定して検索したとき、同じ商品名でも商品が実績の多い商品が検索結果の上位に出てくる」ことでした。 Elasticsearch がデフォルトで計算してくれるスコアと、実績スコアの両方を加味したスコアを出せれば、やりたいことが実現できそうです。
Function score クエリを使った独自スコアの算出
Elasticsearch が自動で算出したスコアを加工した独自のスコアを出したいときは「Function score クエリ」というものを使って検索を行います。
下記に Function score クエリを用いた商品検索クエリの例を示します。name
に「ネヴァー」を含むドキュメントを検索する際に、実績スコアを加味して最終的なスコアを算出しています。
{ "query": { "function_score": { "query": { "bool": { "must": [ { "match": { "name.trigram_field": "ネヴァー" } } ] } }, "boost_mode": "multiply", "field_value_factor": { "field": "tenant_scores.company-a", "factor": 0.1, "modifier": "ln1p", "missing": 0 }, "max_boost": 5 } }, "size": 30 }
function_score.query
内のクエリで算出されたドキュメントとのマッチ度スコアを、field_value_factor
などのオプションを駆使して組み合わせた結果が最終的なスコアになり、そのスコアが高い順に結果がソートされて返却されます。
例の中で指定しているオプションについて説明します。
field_value_factor
- ドキュメント内の任意のフィールドの値を独自スコアの算出のために使うことができる
- 中のオプションで算出方法をいじることができる
field
- スコア算出のために使う値のフィールド名
modifier
field
で指定した値に対して適用する計算オプションln1p
を指定するとfield
で指定した値に 1 を足してその自然対数をとる
factor
modifier
を計算する前にフィールドの値に掛ける値
missing
field
で指定した値が存在しなかった場合に使用される値
boost_mode
function_score.query
のスコアとfield_value_factor
の計算結果をどう組み合わせるかのオプションmultiply
は掛け算
max_boost
field_value_factor
の計算結果がこの値を超えたら自動的にこの値が使われる
工夫したのはmax_boost
を設定し、modifier
に対数を使用することで、取引量が極端に多い場合にもfield_value_factor
に差が出すぎないようにした点です。実績スコアが高すぎて、検索パラメータとあまり一致しないものが検索の上位に出てこないようにするためこのようなオプションを使いました。また、公式ドキュメントにも記載がありますが、field_value_factor
で算出された値は負の数になっているとエラーが返却されます。それを防ぐためにmodifier
にln1p
を使用しています。
おわりに
商品マスタを査定する際の「同じ商品名の商品が多く探すのが手間である」という課題に対して、Elasticsearch を用いた事例を紹介しました。取引件数をベースにした実績スコアをもとに Function score クエリで独自スコアを算出することで、検索パラメータに一致する商品の中でもよく取引される商品を上位に持ってくることが実現できました。
今回紹介した機能は実際に本番環境へリリースされ、査定中の商品検索に利用されています。今後はこの機能の導入効果の検証や、査定時間の短縮のための他の施策を検討していきたいと思います。
今後 Promas の商品検索は既存の買取基幹システムだけではなく、現在開発中である新しい査定システムにも組み込まれる見込みです。また別の検索に関する課題やその解決のアプローチが生まれてくれば紹介したいと思います。