curlコマンドでElasticsearchの基本操作を動かしながら学ぶ！(ドキュメント操作とインデックス管理編)　

# Elasticsearch の基本操作

:::note warn
この記事はElasticsearchのサーバをローカルで立ち上げていることを前提としています。
実際に動かして検証したい読者の方はまずそちらを終えてから本記事を読み進めてください([サーバの立ち上げ方法はこちらの記事でも紹介しています！](https://qiita.com/Saito-3815/items/0ded24c2079b1abe9881))。　
:::

Elasticsearch は REST API であり基本の CRUD 操作でその内容を操作することができる。
以下、 curl コマンドを用いて基本の操作を行う。
curl コマンドを使う場合は、リクエストボディを `-d` オプションで指定し、その後に `'` で囲んだ JSON データを記述する。

## ドキュメントの基本操作

ドキュメントの基本操作は`_doc`エンドポイントを用いて行う。

### ドキュメントの登録

ドキュメントの登録は PUT と POST の二つの方法がある。

#### PUT メソッド

- PUT メソッドは、ドキュメント ID を指定する必要がある。

```bash
$ curl -XPUT 'http://localhost:9200/test_index/_doc/1' \
-H 'Content-Type: application/json' -d'
{
  "user_name": "John Doe",
  "date": "2025-05-03T12:00:00",
  "message": "Hello, World!"
}
'
```

#### POST メソッド

- POST メソッドは、ドキュメント ID を指定しない。
- ID は Elasticsearch が自動で生成する。
- 自動生成された ID はレスポンスボディに含まれる。

```bash
$ curl -XPOST 'http://localhost:9200/test_index/_doc' \
-H 'Content-Type: application/json' -d'
{
  "user_name": "John Doe",
  "date": "2025-05-03T12:00:00",
  "message": "Hello, World!"
}
'
```

```bash: レスポンス
{
  "_index": "test_index",
  "_type": "_doc",
  "_id": "4KrXk5YBJDQ18msI6B1G",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}
```

### ドキュメントの取得

ドキュメントの取得はドキュメント ID を指定して GET メソッドを用いる。

```bash
$ curl -XGET 'http://localhost:9200/test_index/_doc/1'
```

```bash: レスポンス
{"_index":"test_index","_type":"_doc","_id":"1","_version":1,"_seq_no":0,"_primary_term":1,"found":true,"_source":
{
  "user_name": "John Doe",
  "date": "2025-05-03T12:00:00",
  "message": "Hello, World!"
}
}
```

#### 登録したドキュメント部分のみを取得

`_source` を指定すると登録したドキュメント部分のみを取得することができる。

```bash
$ curl -XGET 'http://localhost:9200/test_index/_doc/1/_source'
```

```bash: レスポンス
{
  "user_name": "John Doe",
  "date": "2025-05-03T12:00:00",
  "message": "Hello, World!"
}
```

### ドキュメントの検索

- `_search` エンドポイントを用いてドキュメントを検索する。
- 検索条件は リクエストボディの `query` キーに内容を記述する。

```bash
# messageフィールドに"Hello"が含まれるドキュメントを検索
$ curl -XGET 'http://localhost:9200/test_index/_search' \
-H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "message": "Hello"
    }
  }
}
'
```

```bash: レスポンス
# ドキュメントが2件ヒットした例
{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 3,
    "successful": 3,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "test_index",
        "_type": "_doc",
        "_id": "4KrXk5YBJDQ18msI6B1G",
        "_score": 0.2876821,
        "_source": {
          "user_name": "John Doe",
          "date": "2025-05-03T12:00:00",
          "message": "Hello, World!"
        }
      },
      {
        "_index": "test_index",
        "_type": "_doc",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "user_name": "John Doe",
          "date": "2025-05-03T12:00:00",
          "message": "Hello, World!"
        }
      }
    ]
  }
}
```

### ドキュメントの更新

#### 既存のドキュメント全体を置き換える更新操作

- 既存ドキュメント全体を置き換える場合は、既存のドキュメント ID を指定して PUT メソッドを用いる。
  置き換え操作後は以前のドキュメントは参照できなくなる。

```bash
$ curl -XPUT 'http://localhost:9200/test_index/_doc/1' \
-H 'Content-Type: application/json' -d'
{
  "user_name": "New User",
  "date": "2025-05-03T12:00:00",
  "message": "This message is updated"
}
'
```

#### 既存のドキュメントの一部を更新する操作

- 既存のドキュメントの一部を更新する場合は、`_update` エンドポイントへ POST メソッドを発行する。

```bash
$ curl -XPOST 'http://localhost:9200/test_index/_doc/1/_update' \
-H 'Content-Type: application/json' -d'
{
  "doc": {
    "message": "This message is updated"
  }
}
'
```

### ドキュメントの削除

ドキュメントの削除は対象のドキュメント ID を指定して DELETE メソッドを用いる。

```bash
$ curl -XDELETE 'http://localhost:9200/test_index/_doc/1'
```

## インデックスの管理

ドキュメントを登録する際には、インデックスを事前に作成していなくてもエンドポイントに記述したインデックス名が存在しない場合は自動で作成される。
しかし、**デフォルト値とは異なるレプリカ数やシャード数をもつインデックスを作成したい場合**などは、
事前に手動でインデックスを作成する必要がある。

### インデックスの作成

- 手動でインデックスを作成するためには、エンドポイントにインデックス名を指定して PUT メソッドを用いる。
- リクエストボディに`settings`キーを用いてインデックスの設定を記述する。
- プライマリシャード数(`number_of_shards`)とレプリカ数(`number_of_replicas`)を指定することができる(省略可能。デフォルトは 5 シャードと 1 レプリカ)。

```bash
$ curl -XPUT 'http://localhost:9200/new_index' \
-H 'Content-Type: application/json' -d'
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}
'
```

#### インデックスの設定情報の取得

インデックスの設定情報は `_settings` エンドポイントに GET メソッドを用いて取得することができる。

```bash
$ curl -XGET 'http://localhost:9200/new_index/_settings'
```

### インデックスの更新

- インデックスの一部の設定情報は `_settings` エンドポイントに PUT メソッドを用いて更新することができる。
- ただし、プライマリシャード数はインデックス作成後に変更できない。

```bash
$ curl -XPUT 'http://localhost:9200/new_index/_settings' \
-H 'Content-Type: application/json' -d'
{
  "index": {
    "number_of_replicas": 4
  }
}
'
```

### インデックスの削除

インデックスの削除はエンドポイントにインデックス名を指定して DELETE メソッドを用いる。

```bash
$ curl -XDELETE 'http://localhost:9200/new_index'
```

::: note warn
インデックス削除の誤操作を防止する設定

インデックス削除においてインデックス名に `*` や `_all` を指定すると,例えば「log-stash-\*」のような指定をすることで「log-stash」で始まる全てのインデックスが削除対象となる。
この機能は便利だが,誤って指定した場合に意図しないインデックスが削除されてしまう可能性がある。
そのような誤操作を防止するために、以下の設定で複数インデックスを指定した削除操作を無効化することができる。

```yml: elasticsearch.yml
action.destructive_requires_name: true
```

以下のクラスタ API を発行することでも同じ効果が得られる。

```bash
# persistent キーを用いて永続的に設定を変更する
# 再起動された場合に元に戻したい場合など一時的な設定を行いたい場合は transient キーを用いる
$ curl -XPUT 'http://localhost:9200/_cluster/settings' \
-H 'Content-Type: application/json' -d'
{
  "persistent": {
    "action.destructive_requires_name": true
  }
}
'
```

:::

### インデックスのマッピング定義の操作

#### マッピング作成

- インデックスのマッピングはインデックス作成時に `_mapping` キーにマッピング情報を記述することで作成する。

```bash
$ curl -XPUT 'http://localhost:9200/new_index' \
-H 'Content-Type: application/json' -d'
{
  "mappings": {
    "properties": { "user_name": { "type": "text" } }
  }
}
'
```

#### マッピング定義の確認

インデックスのマッピング定義は `_mapping` エンドポイントに GET メソッドを用いて確認することができる。

```bash
$ curl -XGET 'http://localhost:9200/new_index/_mapping'
```

```bash: レスポンス
{
  "new_index": {
    "mappings": {
      "properties": {
        "user_name": {
          "type": "text"
        }
      }
    }
  }
}
```

#### マッピング定義の更新

- Elasticsearch では一度定義したマッピング定義を差し替える変更はできない(その場合はインデックスを再作成しデータを移行する)。
- 既存のマッピングへフィールドを追加することはできる。`_mapping` エンドポイントに POST メソッドを用いてマッピング定義を追加する。

```bash
$ curl -XPOST 'http://localhost:9200/new_index/_mapping' \
-H 'Content-Type: application/json' -d'
{
  "properties": { "user_mail": { "type": "keyword" } }
}
'
```

```bash: 追加後
{
  "new_index": {
    "mappings": {
      "properties": {
        "user_mail": {
          "type": "keyword"
        },
        "user_name": {
          "type": "text"
        }
      }
    }
  }
}
```

### インデックステンプレート

例えばログの管理を Elasticsearch 上で行いたい場合、日次毎にインデックスを作成するユースケースがある。
この時、インデックス・マッピング定義を指定したい場合、日次毎に手動で作成するのは手間がかかる。
インデックステンプレートを使用することで、共通のデータソースに対して自動生成されるインデックスに共通のマッピングを暗黙的に自動定義できる。

#### インデックステンプレートの作成

- インデックステンプレートは、作成されるインデックス名の文字列を元にして、ある条件を満たした場合に事前に準備したマッピング定義を自動的に適用する。
- テンプレートに指定できるインデックス名の条件には`*`を使用できる(ワイルドカード)。
- テンプレート作成時は、`_template`エンドポイントに PUT メソッドを発行する。この時、テンプレート自身にも名前をつける必要がある。

```bash: test_templateという名前のテンプレートを作成
$ curl -XPUT 'http://localhost:9200/_template/test_template' \
-H 'Content-Type: application/json' -d'
{
  "index_patterns": "access-log-*",
  "settings": {
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "host": { "type": "keyword" },
      "uri": { "type": "keyword" },
      "method": { "type": "keyword" },
      "access_time": { "type": "date" }
    }
  }
}
'
```

#### インデックステンプレートを使用したドキュメントの登録

```bash: 上記で設定したテンプレートのインデックス名にマッチするドキュメントを登録
$ curl -XPOST 'http://localhost:9200/access-log-2025-05-03/_doc' \
-H 'Content-Type: application/json' -d'
{
  "host": "web01",
  "uri": "/user/25012",
  "method": "PUT",
  "access_time": "2025-05-01T00:00:00Z"
}
'
```

```bash: インデックステンプレートで自動作成されたマッピング定義の確認
$ curl -XGET 'http://localhost:9200/access-log-2025-05-03/_mapping'
```

```bash: レスポンス
{
  "access-log-2025-05-03": {
    "mappings": {
      "properties": {
        "access_time": {
          "type": "date"
        },
        "host": {
          "type": "keyword"
        },
        "method": {
          "type": "keyword"
        },
        "uri": {
          "type": "keyword"
        }
      }
    }
  }
}
```

#### インデックステンプレートの定義の確認

インデックステンプレートの定義は `_template` エンドポイントに GET メソッドを用いて確認することができる。

```bash
$ curl -XGET 'http://localhost:9200/_template/test_template'
```

```bash: レスポンス
{
  "test_template": {
    "order": 0,
    "index_patterns": [
      "access-log-*"
    ],
    "settings": {
      "index": {
        "number_of_shards": "1"
      }
    },
    "mappings": {
      "properties": {
        "method": {
          "type": "keyword"
        },
        "host": {
          "type": "keyword"
        },
        "uri": {
          "type": "keyword"
        },
        "access_time": {
          "type": "date"
        }
      }
    },
    "aliases": {}
  }
}
```

#### テンプレートの複数定義

- インデックステンプレートは複数の定義をしておくことも可能。
- 複数の定義を適用する場合は、`order` キーを用いて適用順序を指定する。
- 適用順序は小さいほど先に適用される。
- 後から定義されたテンプレートは前に定義されたテンプレートの内容を上書きする。

```bash
# orderが小さいので先に適用される
$ curl -XPUT 'http://localhost:9200/_template/my_default_template' \
-H 'Content-Type: application/json' -d'
{
  "index_patterns": "*",
  "order": 1,
  "settings": {
    "number_of_shards": 3
  }
}
'

# orderが大きいので後に適用され、my_default_templateの設定を上書きし、シャード数が1に設定される
$ curl -XPUT 'http://localhost:9200/_template/my_tweet_template' \
-H 'Content-Type: application/json' -d'
{
  "index_patterns": "my_index-*",
  "order": 10,
  "settings": {
    "number_of_shards": 1
  }
}
'
```

#### テンプレートの削除

- テンプレートの削除は `_template` エンドポイントに削除したいテンプレート名を指定して DELETE メソッドを発行する。
- テンプレートを削除しても、テンプレートで作成されたインデックスは削除されない。

```bash
$ curl -XDELETE 'http://localhost:9200/_template/my_default_template'
```