AmazonCloudSearchに複数のドキュメントバッチを一括アップロード

Amaozon CloudSearchは、比較的簡単な準備で検索を実装することができるフルマネージドサービスです。データのアップロードに関して、単体のデータのアップロードやDynamoDBやS3との連携については探すとすぐに出てきたのですが、数百ファイルからなるGB単位のドキュメントバッチをアップロードする方法は調べても出てこなかったので、書き残します。jsonファイルをローカルからCLIを用いてアップロードしますが、そのコマンドをsubprocessとして呼び出すpythonスクリプトを書きます。

# 前提
・以下に目を通している

https://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/uploading-data.html

https://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/limits.html

https://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/preparing-data.html#creating-document-batches

・MacOS
・CLIがインストールされている。
```
% aws --version
aws-cli/1.23.4 Python/3.8.10 Darwin/21.6.0 botocore/1.25.4
```

・アップロードする先のCloudSearchはインデックスフィールド作成済み
・アップロードするJSONドキュメントバッチは1つのフォルダ(ここでは「json_folder」)下に存在している

```
<実行ディレクトリ>
├── json_folder
│   ├── tmp100.json
│   ├── tmp105.json
│   ├── tmp110.json
│   ├── tmp1120.json
│   ├── tmp1125.json
│   ├── tmp1130.json
│   ├── tmp1135.json
│   ├── tmp1140.json
│   ├── tmp1145.json
│   ├── tmp115.json
│   ├── tmp1150.json
│   ├── tmp1155.json
│   ├── tmp1160.json
│   ├── tmp1165.json
│   ├── tmp1170.json
│   ├── tmp1175.json
│   ├── tmp1180.json
│   ├── tmp1185.json
・　　　　　　・
・　　　　　　・

```
・CloudSearchのポリシー設定でCLI操作が許可されている。
以下はこのjsonアップロード時のCloudSearchドメインのポリシーです。Acrtionにアップロードの許可が含まれていれば大丈夫です。
```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::00123456789:user/testuser"
      },
      "Action": "cloudsearch:*"
    }
  ]
}
```



# やり方


以下のpythonスクリプトを用意します。
```upload.py
import os, subprocess

json_files = []
directory = 'json_folder'
current_dir = os.getcwd()

for dirpath, _, filenames in os.walk(directory):
    for filename in filenames:
        if filename.endswith('.json'):
            json_files.append(os.path.join(current_dir,dirpath, filename))

#print(json_files)


for file in json_files:
    filepath = os.path.join(dirpath, file)
    subprocess.call([
        'aws', 'cloudsearchdomain', '--endpoint-url', "http://doc-hogehoge-nkjbre5g4wk45whu5tri28h4w5h.us-east-1.cloudsearch.amazonaws.com",
        '--region', 'us-east-1',
        'upload-documents', '--content-type',
        'application/json', '--documents', filepath
    ])
```

エンドポイントのurl、リージョンについてはCloudSearchの各ドメインに合わせてください。
実行するとアップロード処理が開始されます。("adds": 5000というのは各jsonドキュメントバッチ内のレコード数です)


```
% python upload.py
```

```
{
    "status": "success",
    "adds": 5000,
    "deletes": 0
}
{
    "status": "success",
    "adds": 5000,
    "deletes": 0
}
{
    "status": "success",
    "adds": 5000,
    "deletes": 0
}
{
    "status": "success",
    "adds": 4999,
    "deletes": 0
}
{
    "status": "success",
    "adds": 4999,
    "deletes": 0
}
{
    "status": "success",
    "adds": 5000,
    "deletes": 0
}
{
    "status": "success",
    "adds": 4999,
    "deletes": 0
}
{
    "status": "success",
    "adds": 5000,
    "deletes": 0
}

・
・
・
```
## 更新後のドメインのコンソール画面の様子
![スクリーンショット 2023-02-27 10.27.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158553/1b0a898a-5b7c-5586-d94e-406196b882e2.png)


## ハマったこと
jsonをアップロードする際に、CLIが以下のエラーを吐き出しました。

1つめ
```
CloudFront "Field "hogefield" must have array type to have multiple values
```
これは、インデックスフィールドの設定が誤っていたことが原因でした。
CloudSearchのドメインを作る際に、インデックスフィールドはテストデータから自動作成できます。自分はこのやり方で作成したのですが、本来text-arrayで作成されるはずのインデックスタイプが、textで作成されていました。そのため、型エラーが発生していました。
自動作成されるインデックスフィールドはちゃんと確認しないといけないですね。

2つめ
```
Error parsing parameter '--documents': Blob values must be a path to a file.
```
こちらは、書いてる途中のupload.pyを打った際に吐かれたものです。jsonファイルを絶対パスで指定する(current_dir の部分)ことで解消しました。

## その他
公式サイトではドキュメントバッチのアップロードは「10秒に1回まで」というようなことが書いてありますが、間隔を開けずとも問題なく処理が終わりました。