Elasticsearch多言語化その1
英語でもそこそこの検索結果が出て欲しい
以前、Elasticsearch の analyzer 関連の投稿を書いた。
Elasticsearch の analyzer 関連の設定で知ってることを全て書く
Elasticsearch を何に使っているかなどは、詳しくはそちらを参照してもらうとして、ポイントだけ抜粋しておく。
- GitHub, Slack, Google Drive, ChatWork, Backlog などからAPI経由でデータを取ってきて、インデックスを作成
- 言語は、日本語がメインだけど、海外の人とのやりとりや、英語のwebページ(StackOverflowとか)からのコピペもあるので、英語もある程度使われている。
現状は、日本語にのみ対応した設定だけど、英語もそこそこ使われているので、英語の検索結果もそれなりの精度になって欲しい、というのが今回の話。
やったこと: 英語の stemmer を filter として追加
先に結論を書いておくと、使用している analyzer に、”english” と “possessive_english” という2つの stemmer を filter のところに追加した。
設定方法などは、詳しくは以下のページを参照。
Stemmer Token Filter | Elasticsearch Reference [5.0] | Elastic
多言語化のいくつかのポイント
結論を先に書いたところで、Elasticsearch で多言語を扱うときのポイントなどを、公式ドキュメントを元に書いてみる。
以下の公式ドキュメントを全て読んだ人は、本ブログ投稿は無用だと思う。
Dealing with Human Language | Elasticsearch: The Definitive Guide [2.x] | Elastic
あと、Elasticsearch の基本的な知識があることを前提としている。
敵を知り己を知れば云々
まずは、敵(検索対象のドキュメント、ユーザーのユースケース)を知って分類・整理すると良さそう。
ドキュメントに関しては、以下のような観点で分類できそう。
- どの単位で言語が異なるか
- ドキュメント毎(1ドキュメント=1言語)
- フィールド毎(1フィールド=1言語)
- 1フィールドに複数言語が混在(今回はこれ)
- メインの言語、サブの言語で分けられるか
- 分けられる(今回はこちら。日本語メインで、英語がサブ)
- 分けられない
- 同じ内容のドキュメントが複数言語で存在する(Wikipedia とか)
- 各国毎にローカライズされている企業のホームページ(各国支社によって、ページの内容が異なる)
ユーザーに関しては、こんな感じ?
- メイン言語あり、なし
- あり
- 例えば、ある事柄のWikipediaページが複数言語であれば、日本語版を優先して出したい
- (その他、ドキュメントにメイン、サブの言語がある場合は、必然的に「あり」になる)
- なし(使用者は、複数言語の文章を読むことが可能)
- あり
- 言語設定を取得可能
- システムの設定で、使用言語が設定できる(Google, Facebook などのように)
- そのような設定はない
- ブラウザの accept-language で判定
- IP アドレスなどで判定
ドキュメント毎 vs フィールド毎
「どの単位で言語が異なるか」は、公式ドキュメントの分類をそのまま援用したが、「ドキュメント毎」と「フィールド毎」は、特殊な例を除くと本質的には一緒な気がする。title と body という2つの情報があった場合、title だけが複数言語で body が1言語のみ、という事はあまりないはず。
従って、複数言語のデータがある title, body を、
- 1つのドキュメントの複数フィールド(title_en, title_ja, body_en, body_ja, etc.)に格納する
- 複数インデックス(index_en, index_ja, etc.)の同一フィールド(title, body)に格納する
のどちらの方法で格納するか、という実装上の選択になると思う。
どちらが良いかだが、長所短所をまとめてみた。
- 単一インデックス複数フィールド
- (公式ドキュメントには記載がないが)複数インデックスに比べると、単一インデックスの方が管理が楽だと思う。snapshot & restore, reindex など。
- フィールドの追加は後からでも出来るが、フィールドに analyzer を指定するのは、インデックス作成時にしか出来ないので、何らかの workaround が必要
- 複数インデックス同一フィールド
- 言語の追加は、インデックスを作成するだけなので楽
ちなみに、複数言語を複数「タイプ」に入れてはいけない。詳しくは、以下のページのDon’t Use Types for Languages の項を参照。
One Language per Document | Elasticsearch: The Definitive Guide [2.x] | Elastic
1つの文章に複数言語が混じる場合
1つの文章に複数言語が混じるパターンは、大きく分けて2つあると思う。
- 外国語の単語が混じる
- サブ言語の文章が混じる
前者だと、日本語文章に英語の単語(hello, index, database)が混じったり、英語にフランス語由来の単語(deja vu, faux pas など)が混じったりとか。後者の例としては、日本語の文章に英語のマニュアルからの引用を入れたりとか。今回の自分達のユースケースは後者。
外国語の単語が混じる場合は、analyzer としてはメイン言語に合わせて設定して、外国語の単語を辞書として登録していくのが良さそう。(試してはいない)
複数言語の文章が混じるパターンでは、複数のフィールドなりドキュメントに分けて(前項を参照)、別々の analyzer でインデックスするというのが基本になると思う。
「複数の言語」のパターン
ただ、複数言語でも、英語・フランス語のパターンと、日本語・英語のパターンでは事情が異なる。
例えば、英語とフランス語の場合、使うアルファベットは一緒で、フランス語の場合はアクセント記号 (accent aigu, accent grave, etc.)が混じる。従って、例えば英語向けの stemmer をフランス語のフィールドに適用しても良い結果は得られないし、かと言って同じフィールドに対して英語とフランス語の stemmer を適用しても、これまた良い結果にはならない。詳しくは以下のページを参照。
Pitfalls of Mixing Languages | Elasticsearch: The Definitive Guide [2.x] | Elastic
つまり、同じような文字を使う複数の言語を扱う場合は、前項の通り異なるフィールドなり異なるインデックスに別々に配置する必要がある。
それに対して、日本語と英語の場合、使う文字が全く異なる。上のページの Stemmer per Script のところでイスラエルではヘブライ語、アラビア語、ロシア語(キリル文字)、英語という異なる文字を使っている例が出ているが、これと同じことが日本語と英語にも言える。
ということで、日本語メインで英語が混じるテキストの場合、1つのドキュメント・フィールドに格納して、日本語向けの設定と英語向けの stemmer を両方適用しても問題は起きない。(これは、「異なる言語は異なるフィールドあるいはインデックスに格納する」という原則から外れる例外と言っても良いかもしれない。)
まとめ
一言で Elasticsearch を多言語対応すると言っても、色んなパターンがあることが分かった。ユースケースを整理して、最適な実装上の選択をする必要がある。
今回の自分達のケースでは
- 使われるのは日本語がメイン、英語がサブ
- 1つの文章で日本語と英語が混じる
だったので、1つのフィールドで、日本語向けの設定に英語の stemmer を組み合わせて使った。
今後
本投稿のタイトルに「その1」と含めてあるが、その2以降も各予定。
今後は、他国のチームでも使うことを想定して、システムを拡張していきたい。
- メイン言語+英語というのは変わらない
- 「メイン言語」の特徴は色々ありそう
- 非アルファベットの言語(タイ語、中国語、ロシア語)
- アルファベットメインの言語(フィリピン語、ベトナム語も?)
その場合、メイン言語とサブ言語でフィールドを分ける必要がありそう。