DynamoDBのupdate処理で、テーブルに存在しない項目を指定すると、新規項目としてテーブルに追加されてしまいます(UpdateItem)。
条件付き書き込み(ConditionExpression)を使用し、updateで存在しない項目を指定した時に、テーブルへの追加を防ぎ、何も変化しない状態になるよう実装します。
環境
- macOS
- AWS Cloud9
- Node.js 12
AWS.DynamoDB.DocumentClient
JavascriptでDynamoDBを操作するとき、AWS.DynamoDBを使う方法とAWS.DynamoDB.DocumentClientを使う方法の2パターンがあるのですが、AWS.DynamoDB.DocumentClientを使う方が、DBを操作するときにデータの型を指定せずにコードを書くことができて便利なので、今回はこちらを使用しています。
AWS.DynamoDBとの違いは、データ型の指定が不要な点と、メソッド名がやや異なる(〇〇Itemが〇〇になる)くらいで、メソッドの具体的な仕様は全く同じと考えて良さそうです(AWS.DynamoDB.DocumentClient 公式ドキュメント)。
実行コード
const AWS = require('aws-sdk');
const DB = new AWS.DynamoDB.DocumentClient();
exports.handler = async(event, context) => {
const dbParams = {
TableName: "tableName",
Key:{
timestamp: XXXXXX
message: "message"
},
ExpressionAttributeNames: {
'#s': 'status'
},
ExpressionAttributeValues: {
':status': true
},
ReturnValues: 'ALL_NEW',
UpdateExpression: 'SET #s = :status',
ConditionExpression: 'attribute_exists(#s)' // ここで条件を指定
};
const data = await DB.update(dbParams).promise();
return data
}
ここで、
ConditionExpression: 'attribute_exists(#s)'
によって、「timestamp
属性の値がXXXXXX、message
属性の値が"message"である項目のうち、statusという属性を持つ項目が存在している場合」のみ、updateが実行されるようになります。
つまり、そもそも指定したtimestamp
やmessage
を持つ項目が存在しなければ、何も実行されずに済むということです。
ConditionExpression
ConditionExpressionに使用できる演算子・関数は、
比較演算子: = | <> | < | > | <= | >= | BETWEEN | IN
論理演算子: AND | OR | NOT
関数: attribute_exists | attribute_not_exists | attribute_type | contains | begins_with | size
となっています。(参考:公式ドキュメント)
各関数について簡単に説明しておくと、
- attribute_exists(path):キーで指定した項目にpathという属性が存在する場合に実行
- attribute_not_exists(path):キーで指定した項目にpathという属性が存在しない場合に実行
- attribute_type(path, type):pathという属性の値がtypeで指定したデータ型であれば実行
- begins_with(path, substr):pathという属性の値がsubstrで指定した文字列で始まる場合に実行
- contains(path, operand):pathという属性の値がoperandを含んでいれば実行
- size(path):pathで指定した属性のサイズを返す。例えば、
size(path) < 3
のようにして使う
となっております。
各演算子や関数に関する詳細は、こちらのリファレンスをご覧ください。
実行に失敗したとき
指定したDBの操作がConditionExpressionの制約に反し、実行されなかった場合には、ConditionExpressionという例外が吐かれます。
例外時に何か処理を施したい場合は、try~catchでConditionExpressionをcatchしてあげると良さそうです。
ご参考までに。