はじめに
DynamoDBに複数レコードを書き込み(更新)する必要があったので、実装してみた結果と注意的なものを書き留めたいと思い記事を投稿した
またPythonとNode.jsの両方での実装を載せようと思います
前提
テーブル
今回書き込みをするテーブルは以下を想定
テーブル名:Users
カラムと型(スキーマ自体に型はありませんが、登録するデータの型として便宜上決めておきます)は以下の通り
- id(primary key):Number
- name:String
- address:String
- friends: List[Map]
friendはid, name, addressのキーを持つMapの一覧を想定
今回は複雑めな型(Userテーブルのfriendのような型)であっても、書き込みができることも示しておきたいことの一つなので、そもそもテーブルの設計がベストであるかは一旦無視でいきます
Lambdaの準備
今回はLambda上で動くコードを掲載するので、Lambdaは各自で準備ができていることを前提とします
記事の流れ
全体のコード例 → 軽い解説
の手順で書いていき、最後に共通する補足事項を載せる
Pythonでの実装
import boto3
from boto3.dynamodb.conditions import Key
def update_users(table, users_friends):
with table.batch_writer() as batch:
for n in range(3):
batch.put_item(
Item={
"id": n + 1,
"name": "user" + str(n + 1),
"address": "address" + str(n + 1),
"friends": users_friends[n]
}
)
def lambda_handler(event, context):
try:
dynamoDB = boto3.resource("dynamodb")
table = dynamoDB.Table("Users")
user1_friends = [
{ "id": 2, "name": "user2", "address": "address2" },
{ "id": 3, "name": "user3", "address": "address3" }
]
user2_friends = [
{ "id": 1, "name": "user1", "address": "address1" },
{ "id": 3, "name": "user3", "address": "address3" }
]
user3_friends = [
{ "id": 1, "name": "user1", "address": "address1" },
{ "id": 2, "name": "user2", "address": "address2" }
]
users_friends = [user1_friends, user2_friends, user3_friends]
update_users(table, users_friends)
return event["message"] # 返すものは適当
except Exception as e:
print(e)
軽く解説
コードの書き方云々や書き込むデータ自体については本質ではないので、スルーしてもらって大丈夫です
大事なのはupdate_users
関数の中身で、DynamoDBのモデルのインスタンスに生えているbatch_write
メソッドを使っているということ
すなわち、複数書き込みをしたいときは以下のブロック内でPUT
をする
with table.batch_write() as batch:
# ...略
また、PUT
する際には例でいうとbatch.put_item
を呼び出してやる
こいつのItem
という名前の引数に書き込みの内容を記述していく
そしてもう一点注目したいのが、List[Map]という型であっても問題なく書き込むことができるという点
基本的にどんな型でも指定したキーの値としてちゃんとDBに書き込めるので、要するにItem
には更新したい辞書をそのまま渡してあげれば問題ない
最後にちょっとした補足として、batch_write
のブロック内でfor
文を回しているが、当然batch.put_item
をベタで複数回書いていってもよいので、もしfor
文では書けないときはベタでやればいい
また、一度にできる書き込める上限は25件までと言われているが、どうやら25件以上の場合は再送してくれたりよしなにやってくれているらしいので、件数を気にせずコードを書いていけるっぽい
Node.jsでの実装
const AWS = require("aws-sdk");
const dynamoDB = new AWS.DynamoDB.DocumentClient({
region: "ap-northeast-1"
});
const tableName = "Users";
exports.handler = (event, context) => {
const user1Friends = [
{ id: 2, name: "user2", address: "address2" },
{ id: 3, name: "user3", address: "address3" }
];
const user2Friends = [
{ id: 1, name: "user1", address: "address1" },
{ id: 3, name: "user3", address: "address3" }
];
const user3Friends = [
{ id: 1, name: "user1", address: "address1" },
{ id: 2, name: "user2", address: "address2" }
];
const usersFriends = [user1Friends, user2Friends, user3Friends]
const params = {
RequestItems: {
[tableName]: usersFriends.map((e, i) => ({
PutRequest: {
Item: {
id: i + 1,
name: `user${i + 1}`,
address: `address${i + 1}`,
friends: e
}
}
}))
}
};
// callbackは適当
dynamoDB.batchWrite(params, (e, data) => {
if (e) {
context.fail(e);
} else {
context.succeed(data);
}
});
};
軽い解説
Pythonでの解説で述べたようにコードやデータの値そのものに関してはスルー
ここで大事なのはAWS.DynamoDB.DocumentClient
にあるbatchWrite
というプロパティ(関数)
こいつの引数は結構癖があるのでClass: AWS.DynamoDB.DocumentClient — AWS SDK for JavaScriptを参照してください
Node.jsでの複数書き込みについて調べたときによく出てくるのはbatchWriteItem
だったのですが、現在(2020年9月時点)のLambdaで用意されている最新のNode.jsの実行環境だとどうやらないっぽいので、動かず意外と手こずる
batchWriteItem
との大きな違いはItem
のオブジェクトがDynamoDB特有の型付きのオブジェクト(name: { "S": "user" }
みたいなやつ)である必要がなくなっている点
要するに書き込みたい値を気にせずそのままJSONにしてItem
の値として渡せば問題なく動く
そのため、当然Map[List]という複雑な型であってもそのまま渡せば書き込み可能です
全体の補足
バッチ処理で更新(update)をしたいときはPUT
を使うしかない
理由は単純でUPDATE
がないからで、仕方ないので地道にPUT
で記述していく必要がある
またバッチ処理内ではDELETE
をすることができるので、今回はフォーカスしませんでしたが、一応あるということだけ言っておきます・・・
おわりに
いかがでしたでしょうか
複数操作についてはコードと1, 2行程度の解説がある記事はちょこちょこ見かけますが、書き込みに特化して解説されているのはあまり見かけず(あっても複雑な型でもできるのか不明であったり、そもそも実行時エラーになってしまう)、多少やってて手こずったので、備忘の意味も兼ねて書き残してみました
若干ニッチな分野かもしれないですが、少しでも役に立てて貰えれば幸いです
参考
Amazon DynamoDB — Boto3 Docs 1.14.56 documentation
Class: AWS.DynamoDB.DocumentClient — AWS SDK for JavaScript