はじめに
DynamoDBから特定のデータをエクスポートし、値を一部変更した上で再度インポートする
という要件があったので、これを AWS CLI で行ってみました。
DynamoDB のエクスポート/インポートは、AWS Data PipelineやLambda と S3を使用することで可能になります。
しかし、どちらもちょっと手間がかかります。
そこで、手軽にできる AWS CLI を使用して検証してみました。
結論
データエクスポートにquery or scan、
データ整形に jq
、
データインポートに batch-write-itemを使用しました。
スクリプトは以下の通りです。
# 初期化
next_token=
function scan() {
# わかりやすいように scan API を関数に格納。インラインでもOK
# batch-write-itemが一度に書き込めるのが25件なので、`--max-items 25` を使用
# 前回のデータを取得時に、next_tokenが存在した場合はそれを指定。存在しない場合は空欄
aws dynamodb scan \
--table-name $your_table_name \
--max-items 25 \
$([ -z $next_token ] && echo "" || echo "--starting-token $next_token")
}
while [ "$next_token" != "null" ]; do
# scan結果を、batch-write-itemで読める形に整形
request_items=$(
echo $cli_output \
| jq '.Items[] | {PutRequest: {Item:.}}' \
| jq -n "{\"$your_table_name\": [inputs]}"
)
aws dynamodb batch-write-item --request-items "$request_items"
# next_token を格納 (無い場合はnullが入る)
next_token=$(echo $cli_output | jq -r ".NextToken")
done
詳細
scanコマンドを使用した場合、以下のような形式のデータが出力されます。
{
"Items": [
{ /** ここにデータ*/ },
{ ... },
...,
],
"Count": 0,
"ScannedCount": 0,
"ConsumedCapacity": null,
"NextToken": "aaaaaaaa" // or "null"
}
一方、batch-write-itemでインポートする際の形式は、以下の通りとなります。
そのため、jq
コマンドを使用して上の形式を下の形式に変更しています。
{
"your_table_name": [
{
"PutRequest": {
"Item": { /** ここにデータ*/ },
}
},
{
"PutRequest": {
"Item": { /** ここに2件目のデータ*/ },
}
},
...,
]
}
また、scan した件数より出力した件数が少ない場合、nextToken
が返されるので、2 回目以降はそのnextToken
を--starting-token
パラメータに指定します。
参考: AWS CLI ページ分割オプションの使用 - AWS コマンドラインインターフェイス
デモ
実際にテーブルを用意し、データのエクスポートとインポートを行います。
要件は、yaerが2013年のデータをエクスポートし、2021年に書き換えた上で、同じテーブルに違うデータとしてインポートする
とします。
1. 準備
こちらのページを参考にテーブルの作成とデータの追加を行います。
# テーブル作成
$ aws dynamodb create-table \
--attribute-definitions '
[
{ "AttributeName": "year", "AttributeType": "N" },
{ "AttributeName": "title", "AttributeType": "S" }
]' \
--table-name Movies \
--key-schema '
[
{ "AttributeName": "year", "KeyType": "HASH" },
{ "AttributeName": "title", "KeyType": "RANGE" }
]' \
--provisioned-throughput '
{
"ReadCapacityUnits": 10,
"WriteCapacityUnits": 10
}'
サンプルデータは、こちらのmoviedata.zip
を使います。
# データの登録
# 1 行ずつ処理するために、json オブジェクトを 1 行で表示する `-c`オプションを使用
$ IFS=$'\n'; for item in $(cat moviedata.json| jq -c '.[:20] | .[]'); do
aws dynamodb put-item \
--table-name Movies \
--item "{
\"year\": {\"N\": \"$(echo $item | jq .year)\"},
\"title\": {\"S\": $(echo $item | jq .title)}
}"
done
2. データのエクスポート/インポート
# データ確認用
OUTPUT_FILE="output.json"
# 初期化
next_token=
function scan() {
# わかりやすいように scan API を関数に格納。インラインでもOK
# yearが2013年のデータを抽出
# year は予約後なので、 `--expression-attribute-names` を用いて `#year`というプレースホルダーを用意する
# 前回のデータを取得時に、next_tokenが存在した場合はそれを指定。存在しない場合は空欄
aws dynamodb scan \
--table-name Movies \
--filter-expression '#year = :year' \
--expression-attribute-values '{":year":{"N":"2013"}}' \
--expression-attribute-names '{"#year": "year"}' \
--max-items 25 \
$([ -z $next_token ] && echo "" || echo "--starting-token $next_token")
}
while [ "$next_token" != "null" ]; do
# scan結果を、year書き換え後にbatch-write-itemで読める形に整形
request_items=$(
echo $cli_output \
| jq '.Items[].year.N="2021"' \
| jq '.Items[] | {PutRequest: {Item:.}}' \
| jq -n '{"Movies": [inputs]}'
)
echo $request_items >> $OUTPUT_FILE
aws dynamodb batch-write-item --request-items "$request_items"
# next_token を格納 (無い場合はnullが入る)
next_token=$(echo $cli_output | jq -r ".NextToken")
done
実際に DB を確認すると、データが増えているのが確認できるかと思います。
参考
- AWS Data Pipeline を使用した DynamoDB データのインポートとエクスポート - AWS Data Pipeline
- Amazon DynamoDB への CSV 一括取り込みの実装 | Amazon Web Services ブログ
- ステップ 1: JavaScript 用の AWS SDK を使用して DynamoDB でテーブルを作成する - Amazon DynamoDB
- create-table — AWS CLI 1.19.73 Command Reference
- put-item — AWS CLI 1.19.73 Command Reference
- 【jq】bash で json 配列をループさせる - Qiita
- javascript - Scan Function in DynamoDB with reserved keyword as FilterExpression NodeJS - Stack Overflow
- query — AWS CLI 1.19.73 Command Reference
- scan — AWS CLI 1.19.73 Command Reference
- batch-write-item — AWS CLI 1.19.73 Command Reference
- AWS CLI ページ分割オプションの使用 - AWS コマンドラインインターフェイス