個人的によく使うOpenSearchのREST APIまとめ

Amazon OpenSearch Service (以下、OpenSearch)を触っていた際に、API周りでかなり苦戦したので個人用にまとめておく。

# !! 注意 !!

本内容はAmazon OpenSearch Service ver 1.2での挙動です。
OpenSearch ServerlessやElasticsearch、OSSのOpenSearchでは挙動が異なる場合があるのでご注意ください。

# 事前準備

API実行に必要な情報を環境変数に登録しておく。

```bash
$ ENDPOINT="OpenSearchのVPCエンドポイント"
$ USERNAME="aesadmin" # OpenSearch Dashboardsのログインユーザー名
$ read -sp "Please input your password: " PASSWORD # ログインパスワード
```
OpenSearchのVPCエンドポイントに関してはAmazon OpenSearch Serviceのコンソールから確認可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157652/3f99f51c-6d10-c899-e625-e816af28be59.png)

# REST APIの実行方法

## Index操作系
個人的によく利用したのがIndexの作成・削除・存在確認の3つ。

### Indexの作成
以下のコマンドが成功すると`test-index-202212`というIndexが作成される。

```bash
$ curl -u "$USERNAME:$PASSWORD" \
   -H "content-type: application/json" \
   -X PUT "$ENDPOINT/test-index-202212/" \
   -d '
{
 "mappings": {
   "properties": {
     "id": {
       "type": "integer"
     },
     "message": {
       "type": "keyword"
     }
   }
 }
}'
```

### Indexの確認
`test-index-202212`というIndexが存在するか確認。
```bash
$ curl \
   -u "$USERNAME:$PASSWORD" \
   -H "content-type: application/json" \
   -X GET "$ENDPOINT/test-index-202212"
```
<details><summary>Indexが存在する場合のレスポンス例</summary><div>

```json
{
  "test-index-202212": {
    "aliases": {},
    "mappings": {
      "properties": {
        "id": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "message": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    },
    "settings": {
      "index": {
        "creation_date": "1670898851743",
        "number_of_shards": "5",
        "number_of_replicas": "1",
        "uuid": "yGJ6rDiZQ7SO9JDRexHCkg",
        "version": {
          "created": "135238227"
        },
        "provided_name": "test-index-202212"
      }
    }
  }
}
```
</div></details>

<details><summary>Indexが存在しない場合のレスポンス例</summary><div>

```json
{
  "error": {
    "root_cause": [
      {
        "type": "index_not_found_exception",
        "reason": "no such index [test-index-202211]",
        "index": "test-index-202211",
        "resource.id": "test-index-202211",
        "resource.type": "index_or_alias",
        "index_uuid": "_na_"
      }
    ],
    "type": "index_not_found_exception",
    "reason": "no such index [test-index-202211]",
    "index": "test-index-202211",
    "resource.id": "test-index-202211",
    "resource.type": "index_or_alias",
    "index_uuid": "_na_"
  },
  "status": 404
}
```
</div></details>

### Indexの削除
`test-index-202212`というIndexを削除。
```bash
$ curl \
   -u "$USERNAME:$PASSWORD" \
   -H "content-type: application/json" \
   -X DELETE "$ENDPOINT/test-index-202212/"
```

## Bulk Insert

複数のデータを一括でOpenSearchのIndex (`test-index-202212`) に対して投入する。

```bash
$ curl \
   -u "$USERNAME:$PASSWORD" \
   -H "content-type: application/json" \
   -X POST "$ENDPOINT/_bulk" \
   -d '
{ "index": { "_index": "test-index-202212", "_id": "1" } }
{ "id": "1", "message": "test message 1."}
{ "index": { "_index": "test-index-202212", "_id": "2" } }
{ "id": "3", "message": "test message 2."}
{ "index": { "_index": "test-index-202212", "_id": "3" } }
{ "id": "3", "message": "test message 3."}'
```

成功するとDiscoverで以下のようなデータを確認することができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157652/752dba0f-08bc-1528-b014-a8cbbf4d4695.png)

## データの検索

存在するIndex (`test-index-202212`) に入っているデータを検索する。
以下は、『`message`フィールドに入っている文字列に`test`もしくは`message`という単語が含まれている場合に検索結果に含める』というクエリの例。
(何も指定しないとORになる模様。明示的に`test AND message AND 1`みたいにすると`test message 1`のみを取得できる。)
```bash
curl \
 -u "$USERNAME:$PASSWORD" \
 -H "content-type: application/json" \
 -X POST "$ENDPOINT/test-index-202212/_search" \
 -d '
{
 "query": {
   "query_string": {
     "default_field": "message",
     "query": "test message"
   }
 }
}' | jq .
```

成功すると以下のような出力が得られる。
<details><summary>検索結果</summary><div>

```json
{
  "took": 96,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 3,
      "relation": "eq"
    },
    "max_score": 0.5753642,
    "hits": [
      {
        "_index": "test-index-202212",
        "_type": "_doc",
        "_id": "3",
        "_score": 0.5753642,
        "_source": {
          "id": "3",
          "message": "test message 3."
        }
      },
      {
        "_index": "test-index-202212",
        "_type": "_doc",
        "_id": "2",
        "_score": 0.5753642,
        "_source": {
          "id": "3",
          "message": "test message 2."
        }
      },
      {
        "_index": "test-index-202212",
        "_type": "_doc",
        "_id": "1",
        "_score": 0.5753642,
        "_source": {
          "id": "1",
          "message": "test message 1."
        }
      }
    ]
  }
}
```
</div></details>


## ダッシュボードのImport

(手元にndjson形式のダッシュボードのファイルが存在する想定です。)
ダッシュボードImport用APIを実行するために必要な認証情報の作成を実施。

```bash
$ curl --cookie-jar auth.txt \
   -H "osd-xsrf: true" \
   -H "content-type: application/json" \
   -d "{\"username\":\"$USERNAME\", \"password\":\"$PASSWORD\"}" \
   -X POST $ENDPOINT/_dashboards/auth/login | jq
```

上記コマンドが成功すると、カレントディレクトリに`auth.txt`が生成される。
生成された認証情報を利用してAPIを実行することで、OpenSearch Dashboardsにダッシュボードがインポートされる。
```bash
$ DASHBOARD="dashboard.ndjson"
$ curl --cookie auth.txt \
   -H "osd-xsrf: true" \
   -H "securitytenant: global" \
   -X POST \
   "$ENDPOINT/_dashboards/api/saved_objects/_import?overwrite=true" \
   -F file=@$DASHBOARD
```