every Tech Blog

株式会社エブリーのTech Blogです。

DELISH KITCHENチラシの郵便番号・地域名・店舗名検索実装について

f:id:nanakookada:20210721185210p:plain はじめまして。DELISH KITCHEN開発部でバックエンド開発等に携わっている南です。

今回は2021年4月の中旬にリリースされた、「DELISH KITCHENチラシの郵便番号・地域名・店舗名検索実装」の裏側をお話したいと思います。

f:id:takahiro_minami:20210720132515p:plain
DELISH KITCHEN チラシ

検索エンジンによる、郵便番号・地域名・店舗名検索

DELISH KITCHENチラシにはもともと郵便番号検索機能がありましたが、今回、その郵便番号検索の入力欄に郵便番号・地域名・店舗名、いずれの文字をいれても検索できるよう機能拡張しました。 1つの入力欄で郵便番号・地域名・店舗名検索をできるようにするにあたり、今回はElasticsearchを用いました。

f:id:takahiro_minami:20210720132442p:plain
Elasticsearch

n-gram vs. 形態素解析

検索エンジンで用いる際は、文字列をどのようにトークン化するかが重要になってきます。 検索エンジンのトークン化といえば半角スペース区切り、形態素解析、n-gramあたりが主流です。

昔の話ですが、カーナビの目的地住所検索ではn-gramを使用していると聞いたことがありました。 そこで最初にn-gramによるトークン化を試してみましたが、本来1位付近に表示したかった店舗が、他の店舗に埋もれてしまうという悪い結果に終わりました。

地域名・店舗名で検索される方は、短いワードで検索することが予想されます。 その短いワードに「海、川、木、山」や「東、西、南、北」など地域名に頻出するワードが含まれていると、大量の結果が返ってきてしまいます。 (昔のカーナビの住所検索は、住所を8-9割入力してようやく絞り込みができたな・・・、ということを思い出しました。)

n-gramでは良い検索体験を得られないことが分かったので、DELISH KITCHENチラシの郵便番号・地域名・店舗名検索では形態素解析することにしました。

郵便番号・地域名・店舗名検索と形態素解析

まず郵便番号は数字&記号であるため、形態素解析ではなくもっとシンプルな解析器を用いました。これについては後述いたします。

地域名・店舗名は、いずれも日本語の非分かち書きではありますが、文章ではありません。 形態素解析をするというより、辞書を充実させて形態素解析器に名詞判定してもらいトークン化する作戦です。 辞書の優劣が結果の優劣に直結してきます。

郵便番号の解析器

郵便番号検索は日本語を含まないため形態素解析も辞書も不要です。 ただし郵便番号検索では、例えば 「106-6238」とハイフン付きの7桁で検索するユーザーと「106」と3桁で検索するユーザーへの対応が求められます。

そこで「106-6238」のハイフンを「106 6238」(半角スペース)に置換したのち、半角スペース区切りでトークン化する解析器を用意しました。 indexに [106, 6238] とリストとして情報をもたせておくことで、106-6238(106 AND 6238) で検索されても106のみで検索されても〒106-6238の店舗を検索結果に含めることができます。

{
    "settings": {
        "analysis": {
            "char_filter": {
                "hyphen_to_space" : {
                    "type" : "mapping",
                    "mappings" : ["-=>%"]
                }
            },
            "analyzer": {
                "postal_code_analyzer": {
                    "type": "custom",
                    "tokenizer": "standard",
                    "char_filter": [
                        "hyphen_to_space"
                    ],
                    "filter": [
                        "split_delimiter"
                    ]
                }
            }
        }
    }
}

地名辞書データの作成

エブリーにはデータ分析業務を行っているData&AIチームがあり、地名の読み仮名データを過去に作成していたため、それを活用して地名辞書を作成しました。 ただし漢字一文字の地名は、細かくトークン化されてしまいn-gramのようになってしまう恐れがあるため削除しました。 また長すぎる住所は、辞書の1単語としてふさわしくないためこちらも削除しました。

検索対象となる店舗の住所の大半は市街地になります。「漢字2-5,6文字の地名さえ網羅できればよいだろう」くらいの気持ちで辞書を作成しました。

...

左曽,左曽,サソ,地域
巨勢,巨勢,コセ,地域
布佐,布佐,フサ,地域
布勢,布勢,フセ,地域
布太,布太,フダ,地域
布施,布施,フセ,地域
布木,布木,フキ,地域
布瀬,布瀬,フゼ,地域
布良,布良,メラ,地域

...

店舗名辞書データの作成

こればかりは、人力で作成するほかなかったため、DELISH KITCHENのデータベースから店舗名一覧を取り出して1つ1つ読み仮名を振っていきました。 幸いにも膨大な数ではなかったので手作業でこなすことができましたが、単純作業というわけにはいきませんでした。

たとえば店舗名が「エブリー商店」だった場合、「エブリー商店」で検索するユーザーもいれば、「エブリー」のみで検索するユーザーもいるでしょう。 そこで「エブリー」で1単語、「商店」で1単語、辞書作成することにしました。そうすることで「エブリー」でも、「エブリー商店」でも検索できるようになります。

店舗名は凄くユニークな店舗名もあれば、一般名詞や人名の店舗名もあります。1つずつ店舗名を確認し「どうのように検索されるだろうか?」「自分ならどんな検索をするだろうか?」と考えながら辞書作成を行いました。

kuromoji_iteration_mark filterに注意

リリース直前に「代々木」で検索できないという報告があがりました。 これはkuromoji_iteration_markをfilterに設定していたことが原因でした。iteration_markとは踊り字、つまり代々木の「々」を意味します。 kuromoji_iteration_markを設定すると、検索エンジンが踊り字を前の漢字に変換してしまいます。「代々木」で検索すると、「代代木」に変換されます。「代代木」という単語は地域名辞書には存在しないため、「代/代/木」とトークン化されていたのが不具合の原因でした。

地域名辞書に「々」を含む地域がいくつあるか確かめてみたところ170個ほど存在しました。 さほど多くはないのですが、幸運にも「代々木」という有名な地域名があったため気がついてくれた方がいました。安易にkuromoji_iteration_markを使うと辞書とマッチしなくなるため注意しないといけません。どうしてもkuromoji_iteration_markを使わなければならない場合は、辞書に「代々木」と「代代木」の両方を含めないといけません。

...

久々知,久々知,ククチ,地域
久百々,久百々,クモモ,地域
久野々,久野々,クノノ,地域
代々木,代々木,ヨヨギ,地域
佐々木,佐々木,ササキ,地域
佐々生,佐々生,サソウ,地域
佐々礼,佐々礼,サザレ,地域

...

最後に

今回は、郵便番号・地域名・店舗名検索公開に至るまでに悩んだことや躓いたことの地道な活動をまとめてみました。

検索結果の良し悪しにゴールはありません。今後、提携する店舗が増えていけば、それに伴った調整も必要になりますし、ユーザーの声にあわせた調整も必要なります。良い検索結果を返し続けるためにも、絶え間なく改善活動を続けていきたいと思っています。