Docker 入門
Elasticsearch で全文検索を実行する(Windows PowerShell 7 版)
インデックスの作成とマッピングの生成
まず,シェル変数に API キーが読み込まれていることを確認します.
PS C:\...\elastic202511> Write-Output "${Env:ES_LOCAL_API_KEY}" ⏎
NlZGY3pwb0IyT1FPR1RNREtrRlc6WG1VUmZnVnYxSmYyMlpFMDQwTHJ6UQ==
PS C:\...\elastic202511>
まだ読み込まれていなければ前のページで作成したスクリプトを実行してファイル .env からシェル変数に読み込みます.
PS C:\...\elastic202511> .\load-env.ps1 ⏎
PS C:\...\elastic202511>
インデックスの作成
Elasticsearch におけるインデックスはドキュメントを保存する場所のことです.ただし,ドキュメントの内容がそのまま保存されるのではなく,ドキュメントを単語(や形態素)に分割したり,転置インデックス情報を作成するなどの処理が行われるため,後に全文検索が効率的に実行できるようになります.
まず,インデックスの一覧を確認するためのコマンドを示します.なおコマンドの最後に ?v というオプションを付与しています.このオプションを付与することで結果に列の見出し行が表示されるようになります.(この違いは ?v オプションを削除して実行すると理解できるでしょう.)
curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/indices?v"
コマンドの途中に改行を入れると読みやすくなります.このとき,行末には「`」を入力する必要があることに注意してください.
curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" `
"http://localhost:9200/_cat/indices?v"
上のコマンドを実行してみます.まだインデックスが一切存在しないことが分かります.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/indices?v" ⏎
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
PS C:\...\elastic202511>
続いて,インデックスを作成するためのコマンドを準備します.ここは「インデックスの箱を作り,その中で日本語検索に最適なルールを定義する」段階です.今回は「小倉百人一首」の情報をドキュメントとして登録することを考えます.したがって,インデックス名は「hyaku_index」とします.
curl -X PUT -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" -H "Content-Type: application/json" -d '{
"settings": {
"analysis": {
"analyzer": {
"hyaku_kuromoji_analyzer": {
"type": "custom",
"tokenizer": "kuromoji_tokenizer",
"filter": [
"kuromoji_baseform",
"kuromoji_part_of_speech",
"ja_stop",
"lowercase"
]
},
"kana_reading": {
"type": "custom",
"tokenizer": "kuromoji_tokenizer",
"filter": [
"kuromoji_readingform",
"lowercase"
]
}
},
"normalizer": {
"lowercase_normalizer": {
"type": "custom",
"filter": ["lowercase"]
}
}
}
}
}' "http://localhost:9200/hyaku_index/"
上の設定の目的は和歌本文 (uta) や作者名 (kajin) を自然な形で全文検索できるようにすることです.また読み仮名でも検索できるようにします.さらに,完全一致検索や集計用に keyword フィールドを正規化しています.
上の analysis セクションで,検索に使うアナライザ (analyzer) を定義しています.これは文字列の分割や正規化処理を行います.具体的には日本語の全文検索に適した形態素解析を行うため,kuromoji をベースにカスタム設定を追加しています.
カスタムアナライザは「kyaku_kuromoji_analyzer」と「kana_reading」です.「kyaku_kuromoji_analyzer」は日本語の文書を検索するためのアナライザです.「kuromoji_tokenizer」は日本語形態素解析のトークナイザ,「kuromoji_baseform」は動詞・形容詞を原型に変換します.「kuromoji_part_of_speech」は記号など不要な品詞を除去します.「ja_stop」日本語の「の」「に」「は」などストップワードを除去します.さらに今回扱う百人一首データでは不要ですが「lowercase」は英字を小文字化します.
次に「kana_reading」は読み仮名検索用のアナライザです.具体的には「kuromoji_readingform」によって読み仮名に変換されます.
さらにノーマライザには「lowercase_normalizer」を指定しています.これはkeyword型フィールド用の正規化設定で,完全一致検索や集計で大文字小文字を区別にしないようにしています.
上のコマンドを実際に実行してインデックスを登録します.
PS C:\...\elastic202511> curl -X PUT -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" -H "Content-Type: application/json" -d '{
>> "settings": {
>> "analysis": {
>> "analyzer": {
>> "hyaku_kuromoji_analyzer": {
>> "type": "custom",
>> "tokenizer": "kuromoji_tokenizer",
>> "filter": [
>> "kuromoji_baseform",
>> "kuromoji_part_of_speech",
>> "ja_stop",
>> "lowercase"
>> ]
>> },
>> "kana_reading": {
>> "type": "custom",
>> "tokenizer": "kuromoji_tokenizer",
>> "filter": [
>> "kuromoji_readingform",
>> "lowercase"
>> ]
>> }
>> },
>> "normalizer": {
>> "lowercase_normalizer": {
>> "type": "custom",
>> "filter": ["lowercase"]
>> }
>> }
>> }
>> }
>> }' "http://localhost:9200/hyaku_index/" ⏎
{"acknowledged":true,"shards_acknowledged":true,"index":"hyaku_index"}
PS C:\...\elastic202511>
インデックスの一覧を再び表示して「hyaku_index」が登録されていることを確認します.ただし,ステータスがまだ「yellow(green ではない)」の状態です.この対応は後のステップで行います.現時点では「pri(プライマリ)」の個数が1で「rep(レプリカ)」の個数も1になっていることだけを確認しておいてください.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/indices?v" ⏎ health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size yellow open hyaku_index QZiYybW6TtCvDuGAlIs6Ig 1 1 0 0 227b 227b 227b PS C:\...\elastic202511>
マッピングの生成
Elasticsearch に登録するドキュメントのドキュメントタイプについて,具体的に各フィールドのデータ構造やデータ型を定義した情報がマッピングです.ここでは「小倉百人一首」のドキュメントタイプをマッピングとして生成します.
上の手順で作成したインデックス「hyaku_index」にはまだマッピングが登録されていないことを確認します.そのためのコマンドは次のとおりです.
curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/hyaku_index/_mapping"
実際にコマンドを実行します.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/hyaku_index/_mapping" ⏎
{"hyaku_index":{"mappings":{}}}%
PS C:\...\elastic202511>
なお,コマンドに pretty オプションを付けるとプリティ形式で結果を表示することができます.
curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/hyaku_index/_mapping?pretty"
実際にコマンドを実行します.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/hyaku_index/_mapping?pretty" ⏎
{
"hyaku_index" : {
"mappings" : { }
}
}
PS C:\...\elastic202511>
インデックスの作成ではコマンドの中に様々な情報を埋め込みました.別の方法として JSON ファイルをあらかじめ準備しておき,コマンドにデータとして JSON ファイルを埋め込む方法もあります.ここでは json ディレクトリを作成し,その中に mapping.json ファイルを次のとおり準備します.
json/mapping.json
{
"properties": {
"id": {
"type": "long"
},
"uta": {
"analyzer": "hyaku_kuromoji_analyzer",
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 512 }
}
},
"kana": {
"analyzer": "hyaku_kuromoji_analyzer",
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 512 }
}
},
"kajin": {
"type": "text",
"analyzer": "hyaku_kuromoji_analyzer",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above":128,
"normalizer": "lowercase_normalizer"
}
}
}
}
}
なお,今回取り扱う百人一首のドキュメントは次のようなデータ形式になっています.上のマッピングでは「uta」フィールドと「kana」フィールド,「kajin」フィールドについて,「hyaku_kuromoji_analyzer」というカスタムアナライザを利用すると共に,「"keyword"」も設定することで完全一致検索もできるようにしています.
hyaku_data/001.json
{
"id": "1",
"uta": "秋の田の かりほの庵の 苫をあらみ わが衣手は 露にぬれつつ",
"kana": "あきのたの かりほのいほの とまをあらみ わがころもでは つゆにぬれつつ",
"kajin": "天智天皇"
}
マッピングを生成するためのコマンドを示します.ここで,JSON ファイルを -d オプションで埋め込んでいることに注意してください.
curl -X PUT -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" -H "Content-type: application/json" -d @json\mapping.json "http://localhost:9200/hyaku_index/_mapping"
実際にコマンドを実行してマッピングを生成します.
PS C:\...\elastic202511> curl -X PUT -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" -H "Content-type: application/json" -d @json\mapping.json "http://localhost:9200/hyaku_index/_mapping" ⏎
{"acknowledged":true}
PS C:\...\elastic202511>
マッピングが生成されたことを再び同じコマンドで確認します.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/hyaku_index/_mapping?pretty" ⏎
{
"hyaku_index" : {
"mappings" : {
"properties" : {
"id" : {
"type" : "long"
},
"kajin" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 128,
"normalizer" : "lowercase_normalizer"
}
},
"analyzer" : "hyaku_kuromoji_analyzer"
},
"kana" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 512
}
},
"analyzer" : "hyaku_kuromoji_analyzer"
},
"uta" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 512
}
},
"analyzer" : "hyaku_kuromoji_analyzer"
}
}
}
}
}
PS C:\...\elastic202511>
インデックスの削除
現時点では実行する必要はありませんが,(マッピングも含めて)インデックスごと削除する必要性ができた際には次のコマンドを実行してください.
curl -X DELETE -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/hyaku_index"
レプリカ数の変更
上の作業でインデックスを作成しましたが,status が yellow であることが気になっていました.ここではその対応を行います.
まず,Elasticsearch が動作する各サーバのことをノードと呼び,複数起動したノードは互いにメッセージを交換して自律的にノードのグループを形成します.このノードグループをクラスタと呼びます.現時点では1つのノードしか起動していないはずなので,クラスタに所属するノードは1つのはずです.次のコマンドはクラスタ全体の健全性を確認するコマンドです.
curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/health?v"
実際にコマンドを実行すると次の結果が得られました.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/health?v" ⏎ epoch timestamp cluster status node.total node.data shards pri relo init unassign unassign.pri pending_tasks max_task_wait_time active_shards_percent 1764398313 06:38:33 docker-cluster yellow 1 1 4 4 0 0 1 0 0 - 80.0% PS C:\...\elastic202511>
インデックスの健全性を確認するコマンドです.
curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/indices?v"
上のコマンドを実行します.インデックスはドキュメントを保存する場所ですが,インデックスへの格納はシャードと呼ばれる単位に分割されて各ノードに分散されて保存されます.下の結果の pre = 1, rep = 1 は hyaku_index というインデックスはプライマリのシャードを1個持ち,レプリカシャードも1個持つという設定になっていることを意味しています.ここで,耐障害性などを考慮してレプリカシャードはプライマリシャードと同じノードには配置できないことになっています.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/indices?v" ⏎ health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size yellow open hyaku_index QZiYybW6TtCvDuGAlIs6Ig 1 1 0 0 249b 249b 249b PS C:\...\elastic202511>
次に,現在のインデックスのシャード構成を確認するコマンドを示します.
curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/shards?v"
上のコマンドを実行します.これによって,未割り当てのシャードがどのインデックスに関連しているかを確認します.実行結果を見ると,レプリカシャード(r) が未割り当て (UNASSIGNED) になっていることが分かります.現時点ではサーバ(つまりノード)1台の構成で作業している関係上,レプリカシャードを割り当てるノードは存在しません.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/shards?v" ⏎ index shard prirep state docs store dataset ip node .ds-ilm-history-7-2025.11.29-000001 0 p STARTED 3 9.8kb 9.8kb 172.20.0.2 64a6250ff75e .security-7 0 p STARTED 172.20.0.2 64a6250ff75e .ds-.logs-elasticsearch.deprecation-default-2025.11.29-000001 0 p STARTED 2 20.9kb 20.9kb 172.20.0.2 64a6250ff75e hyaku_index 0 p STARTED 0 249b 249b 172.20.0.2 64a6250ff75e hyaku_index 0 r UNASSIGNED PS C:\...\elastic202511>
したがって,インデックス「hyaku_index」のレプリカシャードが割り当てられていないため次のコマンドでは status が yellow となりました.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/indices?v" ⏎ health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size yellow open hyaku_index QZiYybW6TtCvDuGAlIs6Ig 1 1 0 0 249b 249b 249b PS C:\...\elastic202511>
したがって,この問題に対応するためとりあえずはレプリカ数を 0 に変更します.そのコマンドは次のとおりです.
curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" -XPUT "http://localhost:9200/hyaku_index/_settings" -H "Content-Type: application/json" -d'
{
"index": {
"number_of_replicas": 0
}
}
'
実際にコマンドを実行します.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" -XPUT "http://localhost:9200/hyaku_index/_settings" -H "Content-Type: application/json" -d'
>> {
>> "index": {
>> "number_of_replicas": 0
>> }
>> }
>> ' ⏎
{"acknowledged":true}
PS C:\...\elastic202511>
上の作業でレプリカ数が 0 になったはずです.もう一度インデックスを確認すると,確かにレプリカ数が 0 になっており,その結果 status も green になりました.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/indices?v" ⏎ health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size green open hyaku_index QZiYybW6TtCvDuGAlIs6Ig 1 0 0 0 249b 249b 249b PS C:\...\elastic202511>
クラスタ全体の健全性を最後に確認すると,unassign が 0 になり status も green になりました.
PS C:\...\elastic202511> curl -H "Authorization: ApiKey ${Env:ES_LOCAL_API_KEY}" "http://localhost:9200/_cat/health?v" ⏎ epoch timestamp cluster status node.total node.data shards pri relo init unassign unassign.pri pending_tasks max_task_wait_time active_shards_percent 1764398832 06:47:12 docker-cluster green 1 1 4 4 0 0 0 0 0 - 100.0% PS C:\...\elastic202511>