50
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DynamoDB 条件付き書き込み

Last updated at Posted at 2017-03-02

DynamoDBでは新規アイテム1の作成はputItemで行います。DynamoDBの各アイテムは必ずユニークになるプライマリキー2(以下単にキーと表記)を持っている必要があり、アイテム作成時も必ずキーの指定が必要です。アイテム作成時に既存のアイテムとキーが衝突したときに、RDBの感覚だと一意性制約でエラーになるのを期待しますが、DynamoDBではなんと!上書きしてしまいます。RDBに親しんだ脳みそだと、それじゃ困るでしょ、ありえないでしょ、と考えますよね。もちろん上書き禁止の方法は用意されていて、ConditionExpressionというオプションを使うことで実現できます。キー重複以外にも様々な条件が指定できます。

ConditionExpressionについての公式ドキュメント
http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.SpecifyingConditions.html

ConditionExpressionは、処理対象のアイテムに条件を適用します。書き込み処理の場合は、条件を満たしていれば処理が実行され、満たしていなければ処理を実行せずエラーを返します。読み込み系のオペレーションで使用した場合はフィルターとして機能します。今回のサンプルコードは全てputItemですが他のオペレーションでも使えます。


上書き禁止のputItemをPHPで書くと以下のようになります。attribute_not_exists関数を使います。キーが重複した時にfatal errorになるので、処理を続けたい場合はtry〜catchでエラーハンドリングします。$dynamodbは生成済みのDynamoDbClientインスタンスです。

上書き禁止
try {
    $dynamodb->putItem([
	    'TableName' => 'SampleTable',
	    'Item'	=> [
            'Id'   => ['N' => '1'],//パーティションキー
            'Attr' => ['S' => 'ABC'],
	    ],
	    'ConditionExpression' => 'attribute_not_exists(Id)',
    ]);
} catch (Exception $e) {
    echo $e->getMessage();
}

公式ドキュメントを読んでちょっと戸惑ったのが、attribute_not_existsの説明が2つあって内容が違うように見えてしまうこと。なんか分かりづらいと思うんですがそんなことないですか。キーの重複をチェックしているのはattribute_not_exists関数ではなく、putItemオペレーションだと理解しましたがよいでしょうか。


上記の理由で、プライマリキーがパーティションキーとソートキーで構成されるテーブルでも、パーティションキーの指定だけで上書き禁止になります。下記の例だとattribute_not_existsで指定しているのはIdのみですが、キーの組み合わせが1-bのアイテムが存在しても、書き込みは成功します。書き込みが失敗するのは1-aのアイテムが存在する時だけです。

ソートキーを持つテーブルでもパーティションキーの指定だけでOK。
$dynamodb->putItem([
    'TableName' => 'SampleTable',
    'Item'  => [
        'Id'      => ['N' => '1'],//パーティションキー
        'SortKey' => ['S' => 'a'],//ソートキー
        'Attr'    => ['S' => 'ABC'],
    ],
    'ConditionExpression' => 'attribute_not_exists(Id)',
]);

属性の有無ではなく属性の値を条件にする場合は、プレースホルダーを使って下記のように書きます。下記の例だとIdが1のアイテムが存在し、そのアイテムのAttrがXYZの時だけ失敗します。それ以外の場合、アイテムが存在すればAttrをABCに更新し、アイテムが存在しなければ新規アイテムを作成します。

属性の値を条件に指定
$dynamodb->putItem([
    'TableName'	=> 'SampleTable',
    'Item'		=> [
        'Id'   => ['N' => '1'],//パーティションキー
        'Attr' => ['S' => 'ABC'],
    ],
    'ConditionExpression' => 'Attr <> :val',
    'ExpressionAttributeValues' => [':val' => ['S' => 'XYZ']],
]);

複数の条件を指定する場合はAND、ORでつなげます。NOTも使えます。

'ConditionExpression' => 'attribute_not_exists(Id) AND attribute_not_exists(SortKey)'

batchWriteItemでConditionExpressionは使えない

batchWriteItemでもConditionExpressionが使えるかなと思って、下記のようなコードを試したのですが、ダメでした。batchWriteItemではConditionExpressionを指定しても無視されるようです。

ダメコード
$dynamodb->batchWriteItem([
    'RequestItems' => [
        'SampleTable' => [
            [
                'PutRequest' => [
                    'Item' => [
                        'Id'   => ['N' => '1'],
                        'Attr' => ['S' => 'ABC'],
                    ],
                    'ConditionExpression' => 'attribute_not_exists(Id)'
                ],
            ],
        ]
    ]
]);

  1. アイテム: RDBのレコードをDynamoDBではアイテムと言う。公式ドキュメントでは項目と表記している。

  2. プライマリキー: DynamoDBではパーティションキー、またはパーティションキーとソートキーの組み合わせ。パーティションキーはハッシュキー、ソートキーはレンジキーとも呼ばれているよう。

50
26
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?