「うどん」の検索結果には何を出すべきか

李晟圭(@R3kei)氏:こんばんは。Rettyの李と申します。

みんな私のことを圭(ケイ)と呼んでいるので、みなさんも圭と覚えていただければなと思います。テーマが変わっちゃってます。出る前にうちのVPoEに前のテーマをお見せしたのですが、「ぜんぜんキャッチーじゃないよね」と。

(会場笑)

これはネタバレに近いんですけど、「『“うどん”の検索結果には何を出すべきか』という話にしたらどう?」となって「あぁ、はい」という感じです。

(会場笑)

自己紹介を簡単にします。韓国出身で李晟圭(イ・ソンギュ)と言います。

わかりにくいので最後の文字だけ取って圭(ケイ)と呼ぶことにしています。2016年からRettyに在籍をしていて、今はWebと検索チームが統合され、プラス地盤チームというデータを扱うチームのエンジニアリングマネージャーをやらせていただいております。

Rettyはおすすめしたいお店を実名制で口コミを行う飲食店情報サービスで、去年、月間ユーザー数が4,000万人を超えました!

そのサービスの中で私が携わった「予約検索」というものがあります。

Rettyの検索の全体像を簡単に話したあと、各機能、主に社内でサジェストと呼んでいるIncremental searchと、そもそもの検索のところや、表示順を話していければと思います。そして、最後に課題と今後についての話をしていきたいと思います。

Rettyの検索の全体像

Rettyの検索の全体像はElasticの回し者みたいな感じで出していますが、Elasticsearchベースでやっています。

2.4.6という非常に古い、サポートが切れているバージョンを使っているのですが、現在では7.3へのアップグレードを進行しています。

あとはKuromojiと、社内でカスタマイズしている辞書とシノニムも数個ぐらい使っています。Elasticsearch自体に突っ込んでいるドキュメントの数でいうと1,500万件ぐらいを作っております。

検索の対象ですが、1,500万件のドキュメントの中にはどういうものがあるのかと言うと、エリアやジャンルやランチなどの目的、店舗が入ってます。

エリアとサブエリアはRettyの中で決めたものですが、だいたいみなさんの頭に浮かぶ都道府県などですね。駅は六本木駅などです。ランドマークでいうと、東京タワーとか。あとは市区町村も入れたりはします。そういうところをRettyの中では「エリア」と呼んでいます。

ジャンルはみなさんもご存知のように、カテゴリーでいうと和食などがあります。

あとは、しらす丼やたこ焼きなどのメニューも別途持っています。食材でいうとしらすとか。あとは私がRettyってSEO頑張ってるんだなーと思って、入社したきっかけにもなった水菜とかですね。あとは飲み物としては、日本酒の獺祭や而今とかですね。

目的はランチやディナー、モーニングだったり、食べ放題、飲み放題。あとはお一人様OKなどを見ています。

これらの1,500万件のデータは基本的にデイリーで更新を行っています。Elasticsearchをよく使うので、aliasの置き換え20使うかたちで更新してます。

必要に応じてドキュメント単位で準リアルタイムで更新を行っています。例えば、予約在庫状況です。予約が済んでいる在庫は消していかないといけないので、そういうところをSQSに投下して、それをWorkerで捌いてElasticsearchのデータを新しく更新をしていく形をとっています。わりと単純な仕組みかなと思います。

なんでも検索させようとして、インデックス自体がかなり膨れ上がっている状態になっています。単一クラスターでやっていて、それをいろんなところで使っていたので、それをやめて複数クラスターの構成を検討しようかなと思っています。目的に合わせた必要な内容のみで構成していくとか、単一の一番ネックになるところで、SPoFになることを回避していきたいというところもあります。

複数クラスター構成にするのはいいんだけど、複数クラスターになったときに運用の手間があるので、そういう観点で検討を行っているところです。

SuggestとSearch

全体像はいったんここまでで、そろそろ機能の話をしていきたいと思います。まずはRettyの中でも機能で言うと大きくSuggestとSearchと名前を付けて呼んでいます。

Suggestはみなさんご存知の「ろ」と入れたら「六本木」が出ます。いわゆるIncremental searchを、Rettyの中ではSuggestと呼んでいます。Searchはもちろんみなさんご存知で、検索結果として店舗がガーっと並ぶページのことを言っていますね。

まずSuggestの話をさせてください。Rettyの検索においてSuggestはものすごく重要な役割を果たしています。ユーザーさんのニーズとRettyが持っているものをmappingする。つまり、「ID化」ができるんですね。Incremental searchをやられているところだと、だいたい気付いていらっしゃるかもしれないんですけど、具体的に言うと「恵比寿」と入力されたものに関して「SUB701」というID化ができるのは、このSuggestを使っているからなんですね。

なぜID化が重要なのか?

「なぜRettyではID化がすごく重要なのか」という話をしたいと思います。ここにワードが3つあります。

ユーザーさんが入力される想定のものなのですが、「恵比寿」「ざぶとん」「うどん」をユーザーさんが入力するとします。

「恵比寿」だったら普通はエリアや駅名を想定され、「ざぶとん」だったら「座るざぶとんね」。もしくは「メニューもあるかな?」みたいなところですね。例えば、これは肉の部位でもありますよね。希少部位だったりするので、「まぁ、おいしいよね」みたいな話になると思います。

あとは「うどん」。「食べるうどんだよね」ということなんですけど、基本的に全部店名になり得ますよね。もっと言うと、店名と完全に一致するレベルもあると思います。

それをユーザーさんがエリアとして押しているのか、店名を見ているのかは、我々はこのワードだけを受け取った状態ではまったくわからないので、そのたびにSuggestを通して、今ユーザーさんが求めているのが「エリアですか? それとも店名ですか? またはジャンルですか?」を確認して、そのエリアやジャンルに合わせたID化ができています。

今のRettyのアプリの検索枠は、2つありまして、左枠はエリアです。

エリアのSuggestとかを行うときに入力できる枠と、右枠がジャンルや目的、店名などを入力できるようにになっています。

左枠と右枠はどっちも別々のSuggestを行うことになるんですけど、なんでそうしているのかというと、エリアと店名を区別してID化するために、入力枠が2つあります。

それが一番大きかったんですけど、もう1つとして、ユーザーさんが飲食店を探すときにエリアを基準として探す傾向が強くて、今世の中にあるいろんな飲食店サービスの体験はそれをベースにやっているところが多い。なので、それで慣れているユーザーさんが多くいらっしゃるので、ユーザーさんが手慣れている体験を大きく害することがない仕組みを作っています。

エリアのSuggestにおける工夫

エリアのSuggestの場合はキーワードが入ったとき、クエリが入ったときにどういう感じでSuggestしているのか。

まずは文字列に関しては完全一致。これは当たり前なんですけど完全一致、前方一致、部分一致の順番で優先的に表示しようとしています。その中で正式名称で、先ほど「恵比寿」と漢字で書きましたけど、その恵比寿を漢字で書くのか、平仮名で読みを書くのかによっては正式名称のほうを優先したりしています。

あとはこういうところで必然的にある話は、適合率と再現率の間で明け暮れる日々になっています。

その際にエリアのSuggestにもう1つ要素として入れているのは距離です。現在地から近いところに重さを上げておきます。そこで起きる難点がありまして、同じ地名で、前回のナビタイムさんのPOIの検索で多く出てくる問題と非常に似ているような問題がRettyのエリアSuggestにもあります。

同じ地名が書いてあると、検索している現在地からの距離なのか、検索ボリュームなのか。

「伏見と言われたら京都だよね」みたいな話になるかもしれないんですけど、「愛知のところは店舗数が多いぞ」とか、そういう話はどうなのか。「ろ」と入れたら店舗が多い六本木なのか、現在地から近い駅の芦花公園なのか? こういうところに関してどうやって解決していくのか?

なので、エリアSuggestを行うときには、こちらの検索ボリュームや距離、あとは入力文字列による適合率と再現率との戦いが毎日行われていて、日々改善をしようとしています。

今は検索が動いてなかったので検索できるようにしていくところです。

ジャンルと目的のSuggestについて

エリアはここまでで、あとはジャンルと目的のSuggestですね。ここらへんはエリアと比べたら非常に楽ですね。

ですけど、先ほどRettyの枠は2つある話をしましたが、その右上の枠になります。だいたいエリアの入力情報が入っている状態で、この右上の枠に流れてくる動きになります。

そうなったときに何がいいのかと言うと、すでに入力済みなので「このエリアで何かを探そうとしているんですよね」というレコメンドに近い動きが我々としてはできます。2つの枠を使うというのにはそういう狙いもありました。

例えばエリアで「恵比寿」という言葉を入力したときに、「恵比寿なら焼肉と焼き鳥がおすすめですよ!」と、デフォルトのサジェストとして下からバーンと挙げて、焼き鳥・焼肉・ランチ・ディナーなど、みなさんは何も入力をしなくてもサジェストしている状態が作れます。

また、時間帯によってはカフェやランチなどを優先的にやっていくことも可能です。例えば午後の3時にカフェになるかもしれないですし、11時だったらランチを優先的に出したほうがいいかもしれないし、朝の5時、6時だったら、モーニングや朝食を出すのがユーザーさんにとっていいかもしれない。こういうところをレコメンド要素の強いサジェストが可能になります。枠が2つあったら、そういうことも可能ですね。

あとは最後に店名のSuggestですね。

これは言うまでもなくかなり大変です。心が折れる。

(会場笑)