はてだBlog(仮称)

私的なブログど真ん中のつもりでしたが、気づけばWebサイト系のアプリケーション開発周りで感じたこと寄りの自分メモなどをつれづれ述べています。2020年6月現在、Elasticsearch、pandas、CMSなどに関する話題が多めです。...ですが、だんだんとより私的なプログラムのスニペット置き場になりつつあります。ブログで述べている内容は所属組織で販売している製品などに関するものではなく、また所属する組織の見解を代表するものではありません。

Elasticsearchのアナライザーtypeのsearch、normal、extendedについてのなんとなくの話

はじめに

Elasticsearchの形態素解析(kuromoji)による分かち書き時のアナライザーのtype設定にsearch、normal、extendedというものがあります。

日本語環境で、このブログで記事を書く時に仮置きしている「レストラン検索サイト風サイト」などをイメージすると、私の中ではsearch一択ではと思っており&このブログでも勢いでそのように断言していたりしたのですが、ある方達のブログを見ていてオヤと思うことがあったのと、実のところ「雰囲気でsearchを使っていた」ので、改めて自分なりに咀嚼しなおしてみることにしました。

確認したElasticsearchのバージョンは6.4です。また、kuromojiは6.4.3です。

形態素解析関連の分かち書き+α

拙い説明よりもイメージ図ということで、search、normal、extendedによる分かち書きの例です。

f:id:azotar:20190504180822p:plain

なお、実際には辞書は使われないと思いますが、whitespaceとkeywordの例も入れておきます。

  • whitespace: スペースが入っている箇所を分かち書きの区切れとみなします。私が確認した範囲では全角スペースも区切りと認識されました。まあ日本語圏では使いがってがよくないかもしれません。
  • keyword: 分かち書きしない、つまりnoopですね。どちらかと言えば、当該フィールドは、あらかじめこの単位に扱って欲しいというワードのみ元ドキュメントに入っているというものを想定しているという方が正しいかもしれません。また、外面の動きで言うとmatch系のクエリでterm系クエリっぽいことができるということになるかもしれません。

アナライザーの設定例

この記事ではこの後に直接続くものはないのですが、今回のようにいくつかのアナライザーを比べながら試してみるという時に、とりまわしやすいかなという設定方法の例を示します。 *1

PUT aa
{
  "mappings": {
    "_doc": {
      "dynamic_templates": [
        {
          "dtmpl": {
            "match_mapping_type": "*",
            "match": "*",
            "mapping": {
              "fielddata": true,
              "analyzer": "sch",
              "store": true,
              "fields": {
                "nrm": {
                  "type": "text",
                  "analyzer": "nrm"
                },
                "sch": {
                  "type": "text",
                  "analyzer": "sch"
                },
                "ext": {
                  "type": "text",
                  "analyzer": "ext"
                },
                "2ng": {
                  "type": "text",
                  "analyzer": "2ng"
                },
                "eng": {
                  "type": "text",
                  "analyzer": "eng"
                }
              }
            }
          }
        }
      ]
    }
  },
  "settings": {
    "analysis": {
      "analyzer": {
        "nrm": {
          "type": "custom",
          "tokenizer": "normal_t"
        },
        "sch": {
          "type": "custom",
          "tokenizer": "search_t"
        },
        "ext": {
          "type": "custom",
          "tokenizer": "extended_t"
        },
        "2ng": {
          "type": "custom",
          "tokenizer": "ngram"
        },
        "eng": {
          "type": "custom",
          "tokenizer": "e_ngram"
        },
        "noop":{
          "type":"custom",
          "tokenizer":"noop"
        },
        "ws":{
          "type":"custom",
          "tokenizer":"ws"
        }
      },
      "tokenizer": {
        "normal_t": {
          "type": "kuromoji_tokenizer",
          "mode": "normal"
        },
        "search_t": {
          "type": "kuromoji_tokenizer",
          "mode": "search"
        },
        "extended_t": {
          "type": "kuromoji_tokenizer",
          "mode": "extended"
        },
        "ngram": {
          "type": "ngram",
          "min_gram": 2,
          "max_gram": 3
        },
        "e_ngram": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 10
        },
        "noop":{
          "type":"keyword"
        },
        "ws":{
          "type":"whitespace"
        }
      }
    }
  }
}

search、normal、extendedおよびその他の分かち書きの擬人化

何か少し根拠になる例を示そうと思ったのですが、随分退屈になりそうな予感がしたので、少し飛躍していきなり結論めいたものを示すことにします。

ここでは、search、normal、extendedによる分かち書きを擬人化して示して見ました。

ただし、最近の擬人化は、お絵かきも含むニュアンスになるみたいですが、ここではそのようなスキルもないので、「人」に例えて見ましたぐらいの意味です。

f:id:azotar:20190504180848p:plain

ngramとedge_ngramも合わせて配置しています。

例え話になった時点で厳密ではない部分もあるのですが、比較的うまく例えられているように自分では感じています。

活躍しどころや編成を考える

例え話を「定理」「定説」風に使ってさらに別の例えというのは我ながらアレなのですが、先の擬人化キャラクターの見定めがボチボチあっているとして、これらのアナライザーの活躍しどころを見定めてみます。

原理原則的なもの

まずは相性が良い悪いで言うと、この組み合わせは悪いだろうというところを表現してみます。

話が前後しますが、Elasticsearchではデフォルトでは、検索時の検索語をあるフィールドにぶつける際のアナライズとインデックス時のドキュメントの当該フィールドのアナライズは同じものになるというのがデフォルトです。

ただし、検索時のアナライズとインデクス時のアナライズを別のものにすることもできます。この記事のモチベーションはこれらのアナライズを別にすることで、ちょっと面白い調整ができるのではというものだったのですが、正直そこまでは至っていません。

とにかく、検索時のアナライズとインデクス時のアナライズを別のものにするという探求以前に、あきらかに別の組み合わせにした場合に相性が悪いパターンがあります。

その組み合わせについて、なんとなく表現してみたのが次の図です。

注:ここでは、前提として、複数ワード時あるいは複数の単語を組み合わせたひとつの単語の場合の検索では、「AND検索」の戦略であるとします。 例えば、細かい当たり方はともかく、「関西国際空港」の場合、「関西の国際(的)な空港」を探しているという理解で述べています。*2

注:アナライズの話なので、全文検索系のクエリ、もっというとmatch系のクエリを念頭に置いています。ふと忘れがちですが、match系のクエリは、アナライズされたワードに対して「完全一致」相当でないと「ヒット」しません。

f:id:azotar:20190504180912p:plain

まあ、この図だけでは説明しきれていませんが、例えば、「関西国際空港」ドキュメントを、「関西国際空港」で検索したとして、検索時ngramで、インデクス時にsearchとく組み合わせの場合は、

ngram ['関西', '関西国', '西国', '西国際', '国際', '国際空', '際空', '際空港', '空港', '空港周', '港周', '港周辺', '周辺']

の全部について

search ['関西', '関西国際空港', '国際', '空港', '周辺']

の組み合わせが一致するかの検索になるので、

実際はヒットしないですね。 (なんかヒットしそうな気もしましたが、AND検索ですし。)

アカデミックな方からは説明の仕方がおかしいとか例えるまでもなく例えたことで逆にまぎらわしいくなっているではないか?みたいなご指摘をうけそうですが、まあ、ご容赦ください。

組み合わせではなく分業で相乗効果を出すパターンはあるか

前項は検索時・インデックス時のアナライザーの組み合わせの噛み合わなそうなパターンを消去法的に示しました。

本来なら、実は噛み合うかもしれない隠れたパターンの考察が続くべきなのですが、そこは一旦諦めて、それぞれのキャラクターを踏まえて、「分業」することでうまく機能する例を考えます。

... といっても、例えの例えにさらに組織論・チーム論の怪しい例えの重ねがけなので、納得してもらうというよりは、もし前提があっているなら確かにそうかもしれんという例になります。

で、その例がこちら↓

f:id:azotar:20190504180932p:plain

以下、上図を怪しい論法で解説をしてみます。

アプローチ1

searchさんタイプのチームはおおよそいい感じで検索してくれて大きな不満もないです。ただ、この事業部としてはもっと上を狙うために、searchさんチームが苦手な「新語」や雑なお願い・言い回しをしてくるクライアントの対策として、別行動のngramさんチームに、ある程度事務的に徹してもらって、もろもろすくい上げることで、検索漏れをカバーするという鉄壁の布陣になるのではという話です。 (まあ、形態素解析N-gramのハイブリッド検索で、前者のスコアリングを高めにするという話ですね。)

アプローチ2

アプローチ1は少しコストがかかります。事業の規模から言うと今は我慢の時です。といっても、極端にサービス品質は下げたくないです。また、アプローチ1の時に、なんでヒットしているかわからない検索結果が紛れ込んでいるというクレームがしばしば発生しており、このまぎれ込みは実はそれなりに正当な理由ではあったものの、いらぬ心配は無い方が良いのでなんらか対応した方が良いのかもしれません。 ということで、考えすぎずかといって雑になることもなくコンスタントに対応できるextendedさんのチームに運営をお願いしてみることにしましょうか、という話です。 レストラン検索サイトだと迷うのですが、実はビジネスユーザー向けの食材検索などある程度規格化されたワードが中心になりそうなサイトでの全文検索であれば、アプローチ1よりこのアプローチの方がはまるかも?と机上の理屈では思いますがさて。

アプローチ3

アプローチ2をもっとコストがシビアな場合に寄せたらのアプローチです。 また、term検索だと厳密すぎるので全文検索は入れたいぐらい...の検索要件や、「関西国際」で「関西国際空港」はヒットさせてほしくないんじゃというような場合に、アプローチ1や2よりもこのアプローチに寄せても良いかもしれないというものです。

edge_ngram

ここは他と組み合わせるというよりは専任ですかね。 メインの検索よりも、検索ワードのサジェスト・オートコンプリートの使い勝手がよさそうです。 match系でも、match_phraseやmatch_phrase_prefixをメインの検索で使う実質のあいまい検索要件に対応させて、edge_ngramは順序や「関西国際空港」のようにヒトの目で見ると明らかに1単語である...というような固有名詞などを意識してと言う用途からに向いている気がします。加えて、関西国際空港のような例では、1語増えると急激に検索で求められている内容が絞り込まれてくる場合もあります。(違うよと言われるかもしれませんが)データのドメインによっては、「関西国際空」で「関西国際空港」に一意にきまるので、これでヒットさせないという手はないというところかと思います。

ひとまずおしまい

例えに例えなので少ししまらない感じですが、この記事はひとまずここまで。

あとで、やや雑な、続きの記事を書きなぐる予定。

*1:aaというインデックス名はあまりに手抜きですね...

*2:ORは面白みがないですし。