お金をかけずにサーバーの勉強をしよう

OpenSearchで日本語の検索をする

2024年11月5日
メニューへ戻る

こんにちは。

OpenSearchインストール」でデータベースが動くようにし、
OpenSearch Dashboardsインストール」で、ダッシュボードも使えるようになり、
OpenSearchのユーザー追加」で、一般ユーザーを登録することができ、
OpenSearchのインデックスに Pythonでデータを突っ込む」で、日本語データが入ったインデックスを作ることができました。

しかし、日本語検索結果はノイズが入ってしまいイマイチなところがありますので、日本語形態素解析エンジンを OpenSearchで使えるようにしましょう。

それがこれ。
kuromoji

OpenSearchはプラグインとして kuromojiをインストールすることができます。

プラグインのドキュメントはこちら
Installing plugins

このドキュメントを見ると、Additional plugins の表中に [analysis-kuromoji]プラグインが載っています。
おかげでコマンド一発で導入することができます。

OpenSerchの実行ファイルがある [/usr/share/opensearch/bin]ディレクトリにプラグイン管理用の [analysis-kuromoji]があります。
このディレクトリに移って、プラグインをインストールします。
後述しますが「Additional plugins」にある [analysis-icu]プラグインも一緒にインストールします。

subro@UbuntuServer2404-1:/usr/share/opensearch/bin$ sudo ./opensearch-plugin install analysis-kuromoji
-> Installing analysis-kuromoji
-> Downloading analysis-kuromoji from opensearch
[=================================================] 100%  
-> Installed analysis-kuromoji with folder name analysis-kuromoji

subro@UbuntuServer2404-1:/usr/share/opensearch/bin$ sudo ./opensearch-plugin install analysis-icu
-> Installing analysis-icu
-> Downloading analysis-icu from opensearch
[=================================================] 100%  
-> Installed analysis-icu with folder name analysis-icu

簡単にインストールできました。

プラグインをインストールしたら、OpenSearchを再起動して下さい。

subro@UbuntuServer2404-1:/usr/share/opensearch/bin$ sudo systemctl restart opensearch

何も出ませんが、再起動しています。


検証に「OpenSearchのインデックスに Pythonでデータを突っ込む」で作った [homepage_index]インデックスを使いますが、コイツは現時点では OpenSearchのデフォルトのアナライザを使って検索するようになっているはずなので、インデックスの設定を変えて kuromojiを使うようにします。

curlコマンドから OpenSearchの WebAPIを叩くのでどこからでも構いません。
私はクライアントPCの Lubuntu 24.04でやっています。

現在の [homepage-index]インデックスの状態がこちら。

subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X GET https://UbuntuServer2404-1:9200/homepage_index?pretty
{
  "homepage_index" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "filename" : {
          "type" : "text"
        },
        "htmltext" : {
          "type" : "text"
        }
      }
    },
    "settings" : {
      "index" : {
        "replication" : {
          "type" : "DOCUMENT"
        },
        "number_of_shards" : "1",
        "provided_name" : "homepage_index",
        "creation_date" : "1730804562021",
        "number_of_replicas" : "1",
        "uuid" : "M9C1KIeVRYONWtg2PLFfYg",
        "version" : {
          "created" : "136387927"
        }
      }
    }
  }
}

特に出てませんが、設定変更した後のものとの比較用にとっておきました。

この時点で検索文字列がどう見られているか確認してみます。

subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X POST https://UbuntuServer2404-1:9200/homepage_index/_analyze?pretty -H "Content-Type: application/json" -d '
{
  "text" : "お金をかけず"
}'  ← ここまでがコマンドです。

{            ← ここからが結果です。
  "tokens" : [
    {
      "token" : "お",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "",
      "position" : 0
    },
    {
      "token" : "金",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "",
      "position" : 1
    },
    {
      "token" : "を",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "",
      "position" : 2
    },
    {
      "token" : "か",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "",
      "position" : 3
    },
    {
      "token" : "け",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "",
      "position" : 4
    },
    {
      "token" : "ず",
      "start_offset" : 5,
      "end_offset" : 6,
      "type" : "",
      "position" : 5
    }
  ]
}

どうやら 1文字ずつ分けて捉えられているようです。


では kuromojiプラグインを使って検索してみようと思います。

しかしながら [homepage_index]インデックスの現時点のデータは、デフォルトのアナライザーの [standard] を使って分析されたものですので、インデックスの再作成をします。

インデックスを作る時に [analysis-kuromoji] を設定すれば良かったんですが、作ってしまったあとなので少し手間がかかります。

まずインデックスをクローズします。

subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X POST https://UbuntuServer2404-1:9200/homepage_index/_close?pretty
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "indices" : {
    "homepage_index" : {
      "closed" : true
    }
  }
}

クローズできました。

インデックスにアナライザーの設定を追加します。

Elasticsearchのドキュメントですが、こちらに全角英数字がある時の対応として、[analysis-kuromoji]プラグインと [analysis-icu]プラグインを使って分析を行うセッティングの例がありましたのでそれを使ってみます。
kuromoji analyzer

subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X PUT https://UbuntuServer2404-1:9200/homepage_index/_settings?pretty -H "Content-Type: application/json" -d '
{
  "index": {
    "analysis": {
      "analyzer": {
        "kuromoji_normalize": {
          "char_filter": [
            "icu_normalizer"
          ],
          "tokenizer": "kuromoji_tokenizer",
          "filter": [
            "kuromoji_baseform",
            "kuromoji_part_of_speech",
            "cjk_width",
            "ja_stop",
            "kuromoji_stemmer",
            "lowercase"
          ]
        }
      }
    }
  }
}'  ← ここまでがコマンドです

{
  "acknowledged" : true  ← コマンドの実行結果です
}

設定できたようです。

インデックスをオープンします。

subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X POST https://UbuntuServer2404-1:9200/homepage_index/_open?pretty
{
  "acknowledged" : true,
  "shards_acknowledged" : true
}

オープンできたようです。

改めてインデックスの設定を見てみます。

subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X GET https://UbuntuServer2404-1:9200/homepage_index?pretty
{
  "homepage_index" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "filename" : {
          "type" : "text"
        },
        "htmltext" : {
          "type" : "text"
        }
      }
    },
    "settings" : {
      "index" : {
        "replication" : {
          "type" : "DOCUMENT"
        },
        "number_of_shards" : "1",
        "provided_name" : "homepage_index",
        "creation_date" : "1730804562021",
        "analysis" : {
          "analyzer" : {
            "kuromoji_normalize" : {
              "filter" : [
                "kuromoji_baseform",
                "kuromoji_part_of_speech",
                "cjk_width",
                "ja_stop",
                "kuromoji_stemmer",
                "lowercase"
              ],
              "char_filter" : [
                "icu_normalizer"  ← ここでanalysis-icuが使われていますね。
              "],
              "tokenizer" : "kuromoji_tokenizer"
            }
          }
        },
        "number_of_replicas" : "1",
        "uuid" : "M9C1KIeVRYONWtg2PLFfYg",
        "version" : {
          "created" : "136387927"
        }
      }
    }
  }
}

設定追加はできたようです。


Update by query

※ここではチョロいデータ量でやるので瞬時に終わりましたが、かなり重い作業のようです。
本番データでやる場合は、実行時間・ストレージの容量に注意して下さい。
ドキュメントIDはインクリメントされるとありますし、当たり前ですが、分析結果が変わることで作業前後のクエリ結果も変わります。
subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X POST https://UbuntuServer2404-1:9200/homepage_index/_update_by_query?pretty
{
  "took" : 669,
  "timed_out" : false,
  "total" : 274,
  "updated" : 274,
  "deleted" : 0,
  "batches" : 1,
  "version_conflicts" : 0,
  "noops" : 0,
  "retries" : {
    "bulk" : 0,
    "search" : 0
  },
  "throttled_millis" : 0,
  "requests_per_second" : -1.0,
  "throttled_until_millis" : 0,
  "failures" : [ ]
}

274件の全件がアップデート完了と出てますので、これで良いのでしょう。


作業後ではどういう風に検索文言が分割されるのか見てみます。


作業後のほう。
[analyzer]に [kuromoji_normalize]を指定しています。

subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X POST https://UbuntuServer2404-1:9200/homepage_index/_analyze?pretty -H "Content-Type: application/json" -d '
{
  "analyzer" : "kuromoji_normalize",
  "text" : "お金をかけず"
}'  ← ここまでがコマンドです

{
  "tokens" : [
    {
      "token" : "お金",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "かける",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "word",
      "position" : 2
    }
  ]
}

「お金」「かける」と日本語で意味のある分け方になってますね。
おまけに「かけず」は「かける」に変換されています。
なるほど、こういう風に索引も作ってるんだな、と思いました。


試しに「html_text」フィールドに「受信の規則」とあるものを検索してみます。
Windowsサーバー系のものがヒットするはずで、上手く行かない場合は「の」で拾ってきてしまうのではないでしょうか。
(沢山出るので headコマンドで最初の方だけ表示しています)

subro@Lubuntu2404:~$ curl -s -u 'subro:パスワード' -k -X GET https://UbuntuServer2404-1:9200/homepage_index/_search?pretty -H "Content-Type: application/json" -d '
{
  "query": {
    "match": { "htmltext": "受信の規則" }
  }
}' | head

{
  "took" : 17,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {

17件だけ引っ張ってきました。
上手くいっているようです。


アナライザの選定に完璧なものはなく、なるべく検索者の意図に近いものを出すにはどうしたら良いかを模索し続けるものなのでしょう。

日本語に関しては kuromoji以外に Sudachi ってのもあるようです。


OpenSearch/Elasticsearchでの日本語検索については ZOZOさんのブログを大いに参考にさせていただきました。
Elasticsearchで日本語検索を扱うためのマッピング定義