Amazon Elasticsearch Service で類義語(Synonym)を扱う

こんにちは、藤本です。

先日、Amazon Elasticsearch Service を利用しているプロダクトで Synonym を扱いたいという要望があり、調査しましたので備忘録としてブログエントリします。

概要

検索システムにおいて類義語を扱うケースは多くあります。例えば、Developers.IO に関わる人には記事執筆者と読者がいます。Developers.IO は多くの記事があるので、読者が目的の記事を探す場合、フリーワード検索などでキーワードを入力して、目的に合致した記事を検索します。当然ですが、記事執筆者と読者は異なる人です。知識や、語彙力が異なります。読者の目的に合致する記事だけど、記事執筆者と読者で異なる単語(類義語)や、略語を扱うことで、検索結果から漏れてしまうことがあります。具体的な例を出すと、記事執筆者は「AWS」という単語を記事に使っていて、読者は「AmazonWebServices」で検索しているような場合に検索としてはヒットさせることができません(極端な例ですが、、、)

そこで Elasticsearch では Synonym Token Filter を利用することで類義語、略語問題を解決することができます。Synonym Token Filter はワードをグルーピングすることでグルーピングしたワードであれば、どのワードでもヒットさせることができるようになります。上記例で言えば、「AWS」、「AmazonWebServices」を Synonym としてグルーピングすることで、ブログ記事には「AWS」を使っていても、「AmazonWebServices」で検索しても検索にヒットします。逆にブログ記事に「AmazonWebServices」を使っていても、「AWS」で検索して検索にヒットします。

Synonym Token Filter に関しては公式ドキュメントの説明が分かりやすいです。

Synonym Token Filter による Synonym の管理

Synonym Token Filter はファイルベース、およびインデックスベースの Synonym 管理ができます。まずはローカル環境の Elasticsearch で試してみます。

ファイルベース

Synonym をテキストファイルとして管理します。Synonym Token Filter の設定にテキストファイルのパスを指定することで Synonym 設定を参照します。

「AWS」、「AmazonWebServices」を Synonym としてグルーピングした Synonym ファイルを作成します。

AWS, AmazonWebServices

ファイルベースの Synonym を設定したインデックスを設定します。synonym_pathで上記作成した Synonym ファイルを指定します。

PUT file_based_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "japanese": {
          "tokenizer": "kuromoji_tokenizer",
          "filter": [
            "my_synonym"
          ]
        }
      }, 
      "filter": {
        "my_synonym": {
          "type": "synonym",
          "synonym_path": "/etc/elasticsearch/synonym.txt"
        }
      }
    }
  }
}
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "file_based_synonym"
}

動作確認として_analyze API を実行します。

GET file_based_synonym/_analyze
{
  "text": ["AWS"],
  "analyzer": "japanese"
}
{
  "tokens": [
    {
      "token": "AWS",
      "start_offset": 0,
      "end_offset": 3,
      "type": "word",
      "position": 0
    },
    {
      "token": "AmazonWebServices",
      "start_offset": 0,
      "end_offset": 3,
      "type": "SYNONYM",
      "position": 0
    }
  ]
}

「AWS」が「AWS」だけでなく、「AmazonWebServices」がSYNONYMとして展開されました。これにより「AWS」で検索されても、「AmazonWebServices」で検索されてもヒットされることになります。

インデックスベース

もう一つがインデックス設定として Synonym を管理します。Synonym Token Filter 設定のsynonymsに直接、Synonym を定義します。

PUT index_based_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "japanese": {
          "tokenizer": "kuromoji_tokenizer",
          "filter": [
            "my_synonym"
          ]
        }
      }, 
      "filter": {
        "my_synonym": {
          "type": "synonym",
          "synonyms": [
            "AWS,AmazonWebServices"
          ]
        }
      }
    }
  }
}
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "index_based_synonym"
}

同じく_analyze API で動作確認します。

GET index_based_synonym/_analyze
{
  "text": ["AWS"],
  "analyzer": "japanese"
}
{
  "tokens": [
    {
      "token": "AWS",
      "start_offset": 0,
      "end_offset": 3,
      "type": "word",
      "position": 0
    },
    {
      "token": "AmazonWebServices",
      "start_offset": 0,
      "end_offset": 3,
      "type": "SYNONYM",
      "position": 0
    }
  ]
}

同じ結果となりました。

記述も同じような感じなので選択の基準としては、定義する Synonym の量が多い場合、インデックスベースはインデックス設定の可読性が悪くなるのでファイルベースが良さそう。ただファイルベースの場合、クラスタ内の全てのノードでファイルを作成、更新する必要があります。

Synonym の更新

システム運用する上で Synonym の更新が発生する機会は珍しくありません。もちろん Synonym は更新可能です。ファイルベースも、インデックスベースもそんなに作業する内容は変わりません。ファイルベースの場合はファイルを更新した上でインデックスへの反映を行います。ファイルを更新しただけではインデックスへは反映されないのでご注意ください。インデックスベースの場合はインデックスをクローズした上でインデックスの更新の API によりインデックスへの反映を行います。

更新した Synonym をインデックスに設定を反映する方法は以下の 2つです。

インデックスの Close/Open

インデックスを Close ステータスに変更し、再度 Open ステータスに変更することで設定が反映されます。(インデックスベースの場合、Close ステータスから Open ステータスに変更する間にインデックスの Synonym を更新します)

先ほどインデックスベースのインデックスを更新してみましょう。まずは Open ステータスのまま更新を試みます。

PUT index_based_synonym/_settings
{
  "analysis": {
    "filter": {
      "my_synonym": {
        "type": "synonym",
        "synonyms": [
          "AWS,AmazonWebServices,アマゾンウェブサービス"
        ]
      }
    }
  }
}
{
  "error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "Can't update non dynamic settings [[index.analysis.filter.my_synonym.synonyms.0, index.analysis.filter.my_synonym.type]] for open indices [[index_based_synonym/8tf7A4J1SX2hFlyp51bNmw]]"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "Can't update non dynamic settings [[index.analysis.filter.my_synonym.synonyms.0, index.analysis.filter.my_synonym.type]] for open indices [[index_based_synonym/8tf7A4J1SX2hFlyp51bNmw]]"
  },
  "status": 400
}

失敗します。エラーメッセージ記載がある通り、Open ステータスのインデックスは Synonym を更新できません。

Close ステータスに変更します。

POST index_based_synonym/_close
{
  "acknowledged": true
}

GET _cat/indices
       close index_based_synonym   8tf7A4J1SX2hFlyp51bNmw                              

Close ステータスになりました。再度 Synonym の更新を試みます。

PUT index_based_synonym/_settings
{
  "analysis": {
    "filter": {
      "my_synonym": {
        "type": "synonym",
        "synonyms": [
          "AWS,AmazonWebServices,アマゾンウェブサービス"
        ]
      }
    }
  }
}
{
  "acknowledged": true
}

Close ステータスなので Synonym 更新に成功しました。

再度インデックスを Open して、_analyzeAPI で動作確認してみましょう

POST index_based_synonym/_open
{
  "acknowledged": true
}

GET _cat/indices
yellow open  index_based_synonym   8tf7A4J1SX2hFlyp51bNmw 5 1       0 0    810b    810b

GET index_based_synonym/_analyze
{
  "text": ["AWS"],
  "analyzer": "japanese"
}
{
  "tokens": [
    {
      "token": "AWS",
      "start_offset": 0,
      "end_offset": 3,
      "type": "word",
      "position": 0
    },
    {
      "token": "AmazonWebServices",
      "start_offset": 0,
      "end_offset": 3,
      "type": "SYNONYM",
      "position": 0
    },
    {
      "token": "アマゾンウェブサービス",
      "start_offset": 0,
      "end_offset": 3,
      "type": "SYNONYM",
      "position": 0
    }
  ]
}

更新したアマゾンウェブサービスが追加されました。

クラスタ内のノードリスタート

もう一つがクラスタ内のノード(プロセス)を再起動することです。再起動後に更新した Synonym を利用可能となります。こちらはインデックスのアップデートができないため、インデックスベースの Synonym 管理の場合は利用できません。

Synonym の記述方法

先ほどから Synonym の記述をしていますが、簡単に Synonym の記述方法を解説します。大きく 2つの書き方があります。

,(カンマ)で区切る

上記の例でも記載していますが、カンマで区切ることで Synonym のグルーピングを行います。動作は上記をご確認いただければ分かるかと思います。

=>

もう一つが Synonym を別の Term に置き換えます。例えば下記のように記述した場合、AmazonWebServicesアマゾンウェブサービスAWSという Term に置き換えられます。

AmazonWebServices,アマゾンウェブサービス => AWS
PUT index_based_synonym2
{
  "settings": {
    "analysis": {
      "analyzer": {
        "japanese": {
          "tokenizer": "kuromoji_tokenizer",
          "filter": [
            "my_synonym"
          ]
        }
      }, 
      "filter": {
        "my_synonym": {
          "type": "synonym",
          "synonyms": [
            "AmazonWebServices,アマゾンウェブサービス => AWS"
          ]
        }
      }
    }
  }
}
{
  "acknowledged": true,
  "shards_acknowledged": true
}

同じく_analyze API で動作確認します。

GET index_based_synonym/_analyze
{
  "text": ["AmazonWebServices"],
  "analyzer": "japanese"
}
{
  "tokens": [
    {
      "token": "AWS",
      "start_offset": 0,
      "end_offset": 17,
      "type": "SYNONYM",
      "position": 0
    }
  ]
}

Amazon Elasticsearch Service と Synonym

本題です。では、Amazon Elasticsearch Service は Synonym を利用できるのでしょうか。また制限なく Synonym を利用できるのでしょうか。

まず Synonym は利用できます。ただし、制限があります。私が調査した限り、2つの制限があります(他にもあるかもしれませんが)

ファイルベースの Synonym 管理ができない

こちらはまぁしょうがないかな、という感じです。Amazon Elasticsearch Service は AWS マネージドサービスです。OS へログインしたり、ファイルを転送したりできません。そのためファイルベースの Synonym 管理はできず、インデックスベースの Synonym 管理のみ扱うことができます。

_close API が利用できない(Synonym を更新できない)

個人的にはこれが厳しい。上記にも関連しているのですが、Amazon Elasticsearch Service はインデックスベースの Synonym 管理しか利用できません。インデックスベース Synonym 管理の場合前述した通り、インデックスを Close した上でインデックスのアップデートを行う必要があります。ただ、Amazon Elasticsearch Service は_close_open API を提供していません。そのため、インデックスベースの Synonym を更新できません。Synonym を更新する場合はインデックスの再生成が必要となりそうです。

POST index_based_synonym/_close
{
  "Message": "Your request: '/index_based_synonym/_close' is not allowed by Amazon Elasticsearch Service."
}

Amazon Elasticsearch Service で_closeAPI をリクエストすると許可されていない旨のエラーメッセージが返ってきます。

Elastic Cloud と Synonym

Elastic Cloud と Synonym に関しても調べてみました。下記ドキュメントを読む限りだと、Elasticsearch クラスタへファイルアップロードができ、ファイルベースの Synonym 管理も行うことができそうです。また_close_openAPI は制限なく利用できるので Synonym の更新も可能です。

試してみたかったのですが、ドキュメントを読んだだけだと試し方が分かりませんでした。。

まとめ

いかがでしたでしょうか?
Amazon Elasticsearch Service は AWS マネージドサービスです。構築や運用を非常に楽にしてくれます。制限は仕方ないです。要件に合ったプラットフォームを選択しましょう。

  • アップロード機能はデフォルトオフになってます。利用する場合はサポートに問い合わせる必要があります。

    • Shinji Fujimoto

      サポート問い合わせベースで利用可能なんですね。ありがとうございます。試してみます!!