[GAS×DynamoDB] BatchWriteItemでテーブルデータを操作する
GASでDynamoDBのテーブルデータを操作(新規作成、更新、削除)するツールを作成してみました。
1. DynamoDBのテーブルとツール(GAS)の概要
(1) DynamoDBのテーブルについて
テーブル構成は以下の通りで、ロボットID(RobotId)とその設定ID(SettingId)を紐づけるために使用します。
- テーブル名: Robots-xxx (xxxは、環境(開発、テスト、本番)で変わるものとする)
- 属性1: RobotId, 文字列型, パーティションキー
- 属性2: SettingId, 文字列型
(2) ツール(GAS)について
テーブル: Robots-xxxのデータを新規作成、更新または削除します。
2. 手順
(1) テーブル作成
①まず、AWSコンソールにログインし、AWS Cloud Shellを用いて、テーブルを作成します。
②以下のコマンドを実行します。
今回、環境(開発、テスト、本番)に応じて、テーブル名の一部が異なる内容にしました。
# 1. 環境変数を確認(実効の結果、設定済の場合は4へ)
printenv | grep DynamoDBTableName
# 2. テーブル名を環境変数に設定: 設定済みの場合は不要
export DynamoDBTableName=Robots-dev # 開発環境
export DynamoDBTableName=Robots-test # test環境
export DynamoDBTableName=Robots-prod # 本番環境
# 3. 環境変数を確認設定済みの場合は不要
printenv | grep DynamoDBTableName
# 4. テーブル作成
aws dynamodb create-table \
--table-name $DynamoDBTableName \
--attribute-definitions \
AttributeName=RobotId,AttributeType=S \
--key-schema \
AttributeName=RobotId,KeyType=HASH \
--provisioned-throughput \
ReadCapacityUnits=5,WriteCapacityUnits=5
# 5. テーブルが作成されたか確認
aws dynamodb list-tables
(2) Google Sheetsの編集
今回、Google Sheetsに入力した内容を使用します。
以下の通り作成しました。
作成シート
→ シート名は写っていませんが、「Input」としています。
説明
①C3列: データ数を入力(BatchWriteItemは最大25個)
②7行目~32行目のB, C列: RobotId, SettingIdをそれぞれ入力
③7行目~32行目のD列: 「データ」>「データの入力規則」からプルダウンを選択し、「新規」、「追加」、「削除」を設定
④7行目~32行目のE列: H6~I9セルに「作成シート」のキャプチャの通り入力し、以下の通り関数を設定
=INDEX(I$6:J$9,MATCH(D7,I$6:I$9,0),2)
⑤H2~H4列: スクリプトを実行すると、コマンドが出力される
(3) GASでスクリプト作成
GASでスクリプトを作成します。
①(2)のGoogle Sheetsで、「拡張機能」(①) > 「Apps Script」(②)をクリック
②DynamoDBのテーブルデータ操作用のスクリプトを作成します。
エディタに以下の通り入力し、保存(適宜、コメントアウトの内容を消す or 使用してください)
/**
* DynamoDB Robots-xxxテーブルデータ操作用
*/
function generateAndDownloadJson() {
// 対象シート名を設定
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Input");
/** テーブル名を環境変数名より取得する場合 */
const tableName = `$DynamoDBTableName`;
/** テーブル名が全環境同じ場合
const tableName = sheet.getRange("C2").getValue();
*/
// 対象データ数を取得
const dataCount = parseInt(sheet.getRange("C3").getValue(), 10);
// データ数が25個より多い場合はアラートを出して処理を終了
if(dataCount > 25){
SpreadsheetApp.getUi().alert(`データ数は25個以内にしてください`);
return;
}
const keyNameRobotId = 'RobotId';
const keyNameSettingId = 'SettingId';
const dataRange = sheet.getRange(7, 2, dataCount, 4).getValues();
let items = [];
for (let i = 0; i < dataCount; i++) {
const row = dataRange[i];
if (!row || row.length < 2 || !row[0] || !row[1]|| !row[2]|| !row[3]) {
// 不完全な行がある(B~E列のいずれかが欠落している)場合にアラートを表示し、処理を中断
SpreadsheetApp.getUi().alert(`エラー: ${i + 7} 行目のデータが不完全です`);
return;
}
let item = {};
if(row[3] == 'PutRequest'){
// PutRequest(新規作成、更新)の場合
item[keyNameRobotId] = {"S": row[0]};
item[keyNameSettingId] = {"S": row[1]};
items.push({[row[3]]: {"Item": item}});
}else if (row[3] == 'DeleteRequest'){
// DeleteRequest(削除)の場合
item[keyNameRobotId] = {"S": row[0]};
items.push({[row[3]]: {"Key": item}});
}else{
// PutRequest, DeleteRequestが指定された場合は処理を中断
SpreadsheetApp.getUi().alert(`${i + 7} 行目は不正なリクエストです`);
}
}
/** テーブル名が全環境同じ場合
let jsonData = {};
jsonData[tableName] = items;
*/
// 現在の日時を取得し、ファイル名を生成
const fileName = `Robots-xxx_${Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyyMMddHHmmss")}.json`;
// Google DriveにJSONファイルを保存
/** テーブル名が環境ごとに異なり、直に指定する場合 */
/** テーブル名を環境変数名より取得する場合 */
const file = DriveApp.createFile(fileName, JSON.stringify(items), "application/json");
/* テーブル名が全環境同じ場合
const file = DriveApp.createFile(fileName, JSON.stringify(jsonData), "application/json");
*/
// ダウンロードリンクの作成
const url = file.getDownloadUrl();
/** テーブル名が全環境同じ場合
sheet.getRange("H2").setValue(`aws dynamodb batch-write-item --request-items file://${fileName}`);
*/
/** テーブル名が環境ごとに異なり、直に指定する場合
sheet.getRange("H2").setValue(`aws dynamodb batch-write-item --request-items "{\\"Robots-dev\\": $(cat ${fileName})}"`);
sheet.getRange("H3").setValue(`aws dynamodb batch-write-item --request-items "{\\"Robots-test\\": $(cat ${fileName})}"`);
sheet.getRange("H4").setValue(`aws dynamodb batch-write-item --request-items "{\\"Robots-prod\\": $(cat ${fileName})}"`);
*/
/** テーブル名を環境変数名より取得する場合: 必要に応じて、環境変数の設定コマンド(export DynamoDBTableName=Robots-xxx)は消すこと */
sheet.getRange("H2").setValue(`export DynamoDBTableName=Robots-dev && aws dynamodb batch-write-item --request-items "{\\"$DynamoDBTableName\\": $(cat ${fileName})}"`);
sheet.getRange("H3").setValue(`export DynamoDBTableName=Robots-test && aws dynamodb batch-write-item --request-items "{\\"$DynamoDBTableName\\": $(cat ${fileName})}"`);
sheet.getRange("H4").setValue(`export DynamoDBTableName=Robots-prod && aws dynamodb batch-write-item --request-items "{\\"$DynamoDBTableName\\": $(cat ${fileName})}"`);
// モーダルの表示
showDownloadModal(url);
}
function showDownloadModal(url) {
const html = `<html><body><a href="${url}" target="_blank" style="color: blue; text-decoration: underline; padding: 10px 20px; text-align: center; display: inline-block; font-size: 16px; cursor: pointer;">ダウンロード</a></body></html>`;
const ui = HtmlService.createHtmlOutput(html).setWidth(300).setHeight(100);
SpreadsheetApp.getUi().showModalDialog(ui, ' ');
}
③任意でスクリプト名を変更します。
「︙」(①)>「名前を変更」をクリック(②)し、任意の名前に変更(③)(今回、関数名に合わせ、generateAndDownloadJson にしました)
④続いて、スクリプトをメニューバーから実行できるようにします。
「ファイルを追加」(①) > 「スクリプト」をクリックし、任意の名前を入力(③)(今回、createMenuにしました)
⑤エディタに以下の通り入力し、保存
function onOpen() {
const ui = SpreadsheetApp.getUi();
// メニューの項目を作成
ui.createMenu('AWS操作')
.addItem('DynamoDBデータ操作用', 'generateAndDownloadJson')
.addToUi();
}
※addItemの第2引数が②で作成した関数名と一致させてください
⑥これによって、シートが開かれた時にメニューバーに「AWS操作」というタブが追加され、「DynamoDBデータ操作用」をクリックすると、②で作成した関数(generateAndDownloadJson)が実行されます。
(4) お試し実行
最後に、お試し実行してみます。
①テーブルは作成済で、以下のデータが存在している状態で行います。
②Google Sheets(「Input」シート)に以下の通り入力
- C3セル: データ数を入力(①)
- B列~D列: データ数で指定した行数分、操作データに関する情報を入力 or 選択(②)
③メニューバーにて、「AWS操作」(①)>「DynamoDBデータ操作用」をクリック(②)
④スクリプトの実行に成功すると、JSONダウンロード用のモーダルが表示されるので、「ダウンロード」リンクをクリック(ダウンロードが完了したら閉じること)
⑤「実行コマンド」欄に出力されたコマンドをコピー
※今回、テーブル名をRobots-devにしたので、H2セル(①)の内容をコピーしました(②)
⑥AWSコンソールにログインし、「CloudShell」をクリック
⑦コンソールが起動したら、「アクション」(①)>「ファイルをアップロード」をクリックし(②)、④で作成&ダウンロードしたファイルをアップロード
※ファイルのアップロードに成功すると、以下のようなメッセージが表示されます。
⑧⑤でコピーしたコマンドをコンソールに貼り付け、Enter
⑨実行後のDynamoDB Robots-devテーブルです。
実行前の内容と比較すると、
- RobotId: 0001のアイテム SettingId: jp-002 → jp-003 に更新
- RobotId: 0002のアイテム 削除
- RobotId: 0003 SettingId:ch-001 のアイテム 新規作成
- RobotId: 0004 SettingId:ch-002 のアイテム 新規作成
されています。
うまくいきました!
※テーブルデータについては、AWS CloudShellから以下のコマンドでも確認できます
aws dynamodb scan --table-name $DynamoDBTableName
3. 終わりに
実務で使用するサンプルとして作成しました。
最初、テーブル名、データ型、属性はシートから取得する内容にしていたのですが、作業者が誤って書き換え → 事故が起こるとたいへんなので、ソースコード内に埋め込みました。
本ツールによって、DynamoDBのテーブル操作が少しでも楽になったら嬉しいです。
4. 参考
- INDEX | Googleドキュメント エディタヘルプ
- BatchWriteItem - Amazon DynamoDB | aws
- [AWS CLI] DynamoDB のテーブルに複数の項目を一括で追加する | DocumentRoot
- Amazon DynamoDB でサポートされるデータ型と命名規則 - Amazon DynamoDB | aws
- aws.dynamodb create-table | AWS CLI Command Reference
- aws.dynamodb scan | AWS CLI Command Reference
※1: 他、本記事の執筆にあたって大部分をChatGPT: GPT-4のお力をお借りしました
※2: BatchGetItem を使う可能性も加味し、対象ページへのリンクを貼っておきます(調べればすぐ出ますが、、
batch-get-item - Amazon DynamoDB | aws
Discussion