辞書の更新についての注意点

Posted by johtani on Monday, April 27, 2020

目次

先日、Elasticsearchでのカスタム辞書の利用方法についてブログを書きました。

辞書の設定方法について記載しましたが、今回は辞書の更新について書いていなかったので、書いてみようと思います。 ここで「辞書」としているのは、Kuromojiのユーザー辞書、Synonym Graph Token FilterのSynonym辞書(いわゆる類義語辞書)のことになります。サードパーティのAnalyzer等に関する話ではありません。

辞書更新に関する制限事項

辞書の更新について、大原則と制限事項が存在します。

大原則(辞書の更新=データも更新)

ElasticsearchはAnalyzerが切り出した単語を元に転置インデックスを作成して、検索を行っています(この仕組みに関するスライドはこちらを参照のこと)。 Analyzerが辞書を持っている場合、その辞書を元に単語を切り出して転置インデックスに利用します。 また、検索クエリの単語に対してもこのAnalyzerの辞書が利用されます。

辞書に新しい単語を追加するということは、その単語に関連するドキュメントも更新しないと行けないということになります。

例えば、Kuromojiを利用していて、「グランベリーパーク」という単語「グランベリー」「パーク」という単語に分割できるような新しい単語として辞書に追加する場合を考えてみましょう。ユーザーが「グランベリー」で検索しても検索結果として出てきてほしいという場合です。

辞書に「グランベリーパーク」を登録していない頃に登録されたドキュメントは「グランベリーパーク」という1単語として転置インデックスの見出し語を切り出します(Kuromojiはカタカナの連続している文字列については未知語として1単語にし、「名詞-一般」の品詞を付与)。

更新前でのドキュメントのAnalyze結果

「グランベリーパーク」「で」「ショッピング」

もし、辞書に「グランベリーパーク」を「グランベリー」「パーク」から構成される新規の単語として登録しそれを使用した場合、辞書を更新したあとから、「グランベリーパーク」という単語がAnalyzerからは出てこなくなります。

更新後でのドキュメントのAnalyze結果

「グランベリー」「パーク」「で」「ショッピング」

ということは、辞書更新以前のドキュメントは「グランベリーパーク」という見出し語に対して登録されているので、辞書更新以前に登録されているドキュメントは検索にヒットしなくなります。

このように転置インデックスを利用している検索エンジンでは、単語の区切りが変更されるような辞書の更新があった場合、最低でも影響があるドキュメントについては再登録が必要となるわけです。

これが大原則(辞書更新=データも更新)となります。 基本的には辞書の更新を行った場合は、ドキュメントの再インデックス(再登録)が必要となります。

Elasticsearchでの制限事項

Elasticsearchでは、辞書の更新に関して実装上の制限事項が存在しています。 内部的な実装として、ElasticsearchではAnalyzerのインスタンス(正確にはAnalyzerのFactoryのインスタンス)の生成がインデックスに関する内部のインスタンスが生成されたタイミングの1回のみとなっています。

このインスタンスの生成時に設定ファイル(辞書を含む)を読み込んでいます。

言い換えると、辞書(ファイル、インデックス設定に関わらず)の読み込みは、インデックスが作られたタイミングのみということになります。 なおここで言う「インデックスが作られたタイミング」というのは、以下の2パターンです。

  1. インデックス新規作成時
  2. インデックスオープン時

では、ここから辞書を更新してそれを既存のインデックスに適用する方法について説明しましょう。

辞書の更新方法(ファイル編)

前回のブログで説明しましたが、Elasticsearch 7.4よりも古いバージョンでは、ファイルでKuromojiのユーザー辞書を設定していました。まずはこちらの方法について説明します。前提として、すでにユーザー辞書を設定したKuromoji Tokenizerがインデックスに設定されているものとします(ユーザー辞書の設定方法については公式リファレンスを御覧ください)。

辞書ファイルに新規にエントリーを追加しただけでは、設定は読み込まれていません。新規辞書を反映させるためには以下の手順が必要となります。

  1. 更新した辞書ファイルの配布
    • 複数ノードでElasticsearchのクラスターを構成している場合はすべてのノードに更新した辞書ファイルを配布する必要があります。
  2. インデックスのクローズ(公式リファレンス)
    • 設定ファイルを再読込させるために一度インデックスをクローズします。
    • クローズするので、書き込み、検索などの処理を停止する必要があります。もし停止していない場合はクライアント側ではインデックスがクローズされているという旨のエラーを受け取ります(400で、index_closed_exception)。
  3. インデックスのオープン(公式リファレンス)
    • 設定ファイルを読み込みます。これで、新規追加された単語が読み込まれます。
  4. 再インデックス
    • _update_by_queryを利用することで、対象のインデックスのデータを再インデックスすることができます。条件無しでAPIを呼び出すとすべてのデータが再度登録されます。
    • _sourcefalseの場合は_update_by_queryは利用できません。元データをもう一度外部からElasticsearchに対して登録する必要があります。

Kibanaでの手順をGistにしてあります。手順はこちらをご覧ください。

辞書の更新方法(インデックス設定編)

ファイルの場合とは少し手順が異なります。 インデックスの設定としてユーザー辞書を登録しているため、ファイルをElasticsearchのクラスターにあるノードに配布する必要がありません。 また、辞書の設定はインデックスの設定に指定してありますが、こちらは動的に設定変更できる項目ではないため、インデックスを先にクローズする必要があります。

  1. インデックスのクローズ(公式リファレンス)
    • 辞書の設定を更新するにはインデックスをクローズする必要があります。辞書の設定は動的に更新できる項目にはなっていないためです。
    • オープンしているインデックスで更新しようとした場合はillegal_argument_exceptionCan't update non dynamic settings...というメッセージが返ってきます。
  2. 辞書の更新(公式リファレンス:インデックス設定の更新)
    • user_dictionary_rulesに単語と追加します。
  3. インデックスのオープン(公式リファレンス)
    • 設定ファイルを読み込みます。これで、新規追加された単語が読み込まれます。
  4. 再インデックス
    • _update_by_queryを利用することで、対象のインデックスのデータを再インデックスすることができます。条件無しでAPIを呼び出すとすべてのデータが再度登録されます。
    • _sourcefalseの場合は_update_by_queryは利用できません。元データをもう一度外部からElasticsearchに対して登録する必要があります。

Kibanaでの手順をGistにしてあります。手順はこちらをご覧ください。

第3の方法(新規インデックス作成)

ここまで、インデックスのクローズ、オープンで既存のインデックスに対して辞書を更新する方法について説明しました。 ただ、残念なことにAmazon Elasticsearch ServiceではElasticsearchが提供しているすべてのAPIが利用できるわけではありません(Amazon ESの利用可能なAPIの一覧はこちら)。 (_closeは駄目だけど_openは呼べるのかな???)

ということで、新規にインデックスを作成して、新しい辞書の設定を反映したインデックスを用意し、そこにデータをコピーもしくは登録するという方法になります(Amazon ESのカスタム辞書のドキュメントに手順がありますね)。

手順としては以下のとおりです。

  1. 辞書の更新(用意)
    • 新しい単語などを登録した辞書を用意します。
    • ファイル、インデックス設定どちらでもOKです。
    • ファイルの場合は、既存のファイル名とは異なるファイル名にしたほうが混乱がなくなります。
  2. 新規インデックス作成
    • 1.で作成した辞書を元に新規インデックスを作成します。
  3. 新規インデックスにデータコピー
    • _reindex APIを利用するとデータコピーが簡単です。sourcedestを指定するだけです。
    • _sourcefalseの場合は_update_by_queryは利用できません。元データをもう一度外部からElasticsearchに対して登録する必要があります。
  4. アプリケーション側で新規インデックスを利用するように変更

考慮すべき点としては、サービスを提供しながら行う場合は、3.の_reindexを実行し始めたタイミング以降の登録・更新データの扱いについてでしょうか。

まとめ

辞書の更新に関する大原則、制限事項、手順などについて説明しました。 辞書の変更は検索に大きく影響がでます。そのあたりをきちんと考慮しながら更新しましょう。 ユーザー辞書、カスタム辞書を扱う際の参考にしていただければと。 他にもユーザー辞書で気をつけないといけないこともありますが、今日はこのあたりで。


comments powered by Disqus