エンジニアリング

CJK アナライザーの辞書更新時の挙動について

満開の桜を楽しもうと、春先に日本を訪れる外国人旅行者の数が増加しています。お花見スポットを探すには、”さくら” という単語で情報を検索することが多いでしょう。Elasticsearch で ”さくら” を検索するには、全文検索の機能を使います。これには、日本語に対応したアナライザーが必要です。このブログを読むと、デフォルトのスタンダード アナライザーが CJK(中国語, 日本語, 韓国語)には適しておらず、それぞれの言語に対応したアナライザーが必要であることがわかります。言語アナライザーが文章を単語に正しく分割するためには、辞書の存在が重要です。

例えば、日本語アナライザー kuromoji では、デフォルトの動作として、東京スカイツリー東京, スカイツリー の 3 つのトークンに分割されます。しかしながら、スカイツリー日本一高い電波塔として、意味のある単語です。しかし、単に "スカイ" と "ツリー" というのは、この検索クエリとして、あまり意味のある分割ではないと考えられます("スカイツリー" の検索結果がほしいが、"スカイ" と "ツリー" の結果がほしいわけではないからです)。 そのような分割を行うには、kuromoji のトークナイザーの設定にて、ユーザー辞書を指定することで実現できます。

最近、辞書更新する際の挙動について、良くお問い合わせを頂いておりますので、本ブログにて、基本的な考え方などについて、ご説明したいと考えております。ぜひご覧いただき、より辞書更新への理解を深めていただければ幸いです。

前提知識

アナライザーに対する設定は、デフォルトの場合、インデクシング時と検索時の両方が適用されます。インデクシング時のアナライズ対象は、元データであり、検索時のアナライズ対象は、検索キーワードとなります。 このため、辞書に対する変更も、インデクシング時と検索時の両方に影響があることを、意識しておく必要があります。 この部分は前提知識となります。

辞書更新時の挙動

Elasticsearch のアナライザーは、起動時に辞書を読み込み、その後は設定を読み直すことは基本的にしない動作となります。辞書を反映させるには、Elasticsearch ノードの再起動、あるいは、対象インデックスの _close_open API を実行することで実現できます。 補足ですが、インデックスがクローズされている間に、そのインデックスに対して、インデクシングや検索処理ができません。また、データロスのリスクもあります。回避策として、エリアスと併用することで、ゼロダウンタイム変更が実現できますので、必要に応じてご参考いただけますと幸いです。

しかしながら、ここでご留意いただきたいのは、すでに格納されているドキュメントは、その変更の影響を受けません。なぜなら、辞書を更新する前のアナライザーを使用して登録されているためです。既存のインデックスに変更を反映させるためには、既存のインデックスに辞書変更を反映させる方法 をご参照ください。

一方で、既存のインデックスではなく、辞書変更後にインデクシングされるドキュメントは、その変更の影響を受けます。また、検索クエリも、その変更の影響を受け、キーワードが新しい辞書内容に従って展開される動作となります。

検索結果への影響

上述した動作の違いによって、検索時にズレが生じる可能性がございます。

ズレが生じない場合の例

例えば、下記のような辞書を新たに定義した場合、

japan, sushi

定義前にインデックスされたドキュメントに、japan または sushi という文字列が含まれていても、辞書にはまだ存在していなかったため類義語展開されずにインデックスされます。 しかし、定義後に japan で検索した場合、類義語展開により japan または sushi という条件で検索キーワードが類義語展開されて検索され、定義前にインデックスされたドキュメントも期待通りに検索にヒットしてくれます。また、sushi で検索した場合も同様の挙動になります。

ズレが生じる場合の例

一方で、下記のようなユーザー辞書を新たに定義した場合、東京大学東京大学 のように分割されず、1 つの 東京大学 のトークンになります。

東京大学,東京大学,トウキョウダイガク,カスタム名詞

この場合、(新たに定義前の) 既存のインデックスでは、東京大学東京大学 の 2 つのトークンとして登録されていたため、インデックスには 東京大学 のようなトークンが存在しません。 更に、新たにユーザー辞書定義後、東京大学 の単語で検索する場合、4 文字の “東京大学” が 1 つのトークンとして検索が行われる動作となり、検索にヒットしない結果になります。

既存のインデックスに辞書変更を反映させる方法

ドキュメントを再度登録すれば、内部的には一回削除 => 新規登録の動作となりますので、アナライズ処理が再度行われます。 実現ための手段として、_update_by_query の API を実行すれば、特定のドキュメントのみ、またはインデックス単位での更新(内部的には削除 => 新規登録)ができます。そうすることによって、ドキュメントが再度新しい辞書でアナライズされます。

上述の状況を踏まえ、皆様にはどうか、この辺りの動作をご理解いただいたうえで、辞書作成・更新の計画を立てていただければ幸いです。 補足として、言語アナライザーの詳細説明に関して、マニュアル (Japanese Kuromoji analyzer, Korean Nori analyzer, Chinese IK analyzer) をご参考いただけますと幸いです。 何かご質問やご不明な点がございましたら、弊社のフォーラムにてお気軽にお問い合わせいただければ幸いです。