Elasticsearchで「めうはめうめうめう」を探す (形態素解析+N-Gram)
ホンモノのElasticsearch初心者です。ログの解析はしないのでKibanaもFluentdも出てきません。関係無いけど常体で記事書くの、慣れていないせいか割りと難しく感じます……。だいたい敬体で書いてから常体に修正してしまってます。この段落も敬体で書いてしまいました。
目的
「めうはめうめうめう」を検索できるようにする。
形態素解析 vs. N-Gram
形態素解析とN-Gramの特徴に関しては下の記事が分かりやすかった。
第6回 N-gramと形態素解析との比較:検索エンジンを作る|gihyo.jp … 技術評論社
形態素解析(今回はプラグインも用意されているKuromojiという形態素解析器を利用した)単体である程度求めている結果を得ることは出来た。
elasticsearch-kuromojiについては導入から設定まで @9215 さんの記事がかなり詳しく、非常に助かった。自分はとりあえず全てデフォルト設定のままKuromojiを使っている。
Elasticsearch 日本語で全文検索 その1 — Hello! Elasticsearch. — Medium
Elasticsearch 日本語で全文検索 その2 — Hello! Elasticsearch. — Medium
Elasticsearch 日本語で全文検索 その3 — Hello! Elasticsearch. — Medium
ところが、形態素解析の性質上、未知語(辞書、今回はIPAdicに載っていない単語)にあたると的外れな分かち書きをした上に、正しくない活用語尾でインデックスしてしまうため、「ぽよぽよ」や「めう」が検索できなくなってしまう。硬めのテキストならさほど問題にならないのかもしれないが、検索対象がTwitterのツイートなので、これはかなり手痛い。
実際によく検索しそうで辞書に載っていなさそうな文字列をAnalyzerに通してみると、「ぽよぽよ」→「ぽい/ぽい」、「めう」→「め」、「めうはめうめうめう」→「め/はめ/うめ/うめる」、と、割りと残念な結果が返ってくる。
N-Gram の導入
これは結構キツい……ので、インデックスの肥大化覚悟でN-Gramも導入した。ElasticsearchのnGramトークナイザーはデフォルトで1/2-Gramを作るようだが、日本語では3-Gramが適切らしいので、そんな感じにAnalyzerとTokenizerを設定。
ja_ngram
というCustom Analyzerからja_ngram_tokenizer
というnGram(Elasticsearch組み込み)ベースのCustom Tokenizerを呼ぶように。
{ "index":{ "analysis":{ "analyzer":{ "ja_ngram":{ "type":"custom", "tokenizer":"ja_ngram_tokenizer" } }, "tokenizer":{ "ja_ngram_tokenizer":{ "type":"nGram", "min_gram":"2", "max_gram":"3", "token_chars":[ "letter", "digit" ] } } } } }
こんな感じですね。Elasticsearchではmulti-fieldsと言って、一つのフィールドに複数のAnalysisを設定できるっぽい。便利。
と、いうことで、実際にMappingをそのように設定する。content
で形態素解析を利用したデータ、content.ngram
で先ほど設定したja_ngram
でAnalyzeされたデータにアクセスできる。
{ "mappings":{ "tweets":{ "properties":{ "content":{ "type":"string", "analyzer":"kuromoji", "fields":{ "ngram":{ "type":"string", "analyzer":"ja_ngram" } } } } } } }
実際に検索する
"fields": ["content", "content.ngram"]
みたいな感じのフィールド指定を加える事で両方から拾ってくるようになります。体感的にはN-Gramから出た結果を色濃くする(["content", "content.ngram^3"]
)と、求めていた結果に近いものが得られるんじゃないかな、という感じです。
お疲れ様でした。