dynamoDBのupdate時、配列やオブジェクトの値に対して条件をつけて書き込み(ConditionExpression)したい

dynamoDBの条件付き書き込みで、オブジェクト内の特定要素に対して制約を付ける方法をメモする

#dynamoDBのConditionExpressionについて

dynamoDBのあるレコードを更新する場合に下記のように条件をつけることができる

- レコードに特定キーが存在する場合のみput
- レコードの特定キー値がある値の場合のみput

例：productTable

```js
{
 name:"apple", // hash key
 price: 300,
 isBought: false
}
```

実行コード(lambda)

```js

const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();

exports.handler = async(event, context) => {
     const params = {
        TableName: "productTable",
        Key:{
            name: "apple"
        },
        ExpressionAttributeNames: {
            '#isBought': 'isBought'
        },
        ExpressionAttributeValues: {
            ':isBought': true,
            ':expectIsBought': false
        },
        UpdateExpression: 'SET #isBought = :isBought',
        ConditionExpression: '#isBought = :expectIsBought' // まだ購入されていない商品である場合のみ更新する
    };
    const data = await dynamoDB.update(params).promise();
}

```

#ConditionExpressionの条件をオブジェクトの１要素にしたい場合

例：productTable

```js
{
 name:"apple", // hash key
 price: 300,
 info: {
  reservation: "Aさん",
  isBought: false
 }

}
```

実行コード(lambda)

```js

const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();

exports.handler = async(event, context) => {
     const params = {
        TableName: "productTable",
        Key:{
            name: "apple"
        },
        ExpressionAttributeNames: {
            '#info': 'info'
            '#reservation': 'reservation'
            '#isBought': 'isBought'
        },
        ExpressionAttributeValues: {
            ':isBought': true,
            ':expectReservation': 'Aさん'
            ':expectIsBought': false
        },
        UpdateExpression: 'SET #info.#isBought = :isBought',
        ConditionExpression: '#info.#reservation = :expectReservation AND #info.#isBought = :expectIsBought' // Aさんが予約し、まだ購入されていない商品である場合のみ更新する
    };
    const data = await dynamoDB.update(params).promise();
}

```

オブジェクトの要素参照は`#key1.#key2`のような形で参照できる

#ConditionExpressionの条件を配列の１要素にしたい場合

例：productTable

```js
{
 name:"apple", // hash key
 price: 300,
 stocks: [true, false]

}
```

実行コード(lambda)

```js

const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();

exports.handler = async(event, context) => {
     const index = 1
     const params = {
        TableName: "productTable",
        Key:{
            name: "apple"
        },
        ExpressionAttributeNames: {
            '#stocks': 'stocks'
        },
        ExpressionAttributeValues: {
            ':isBought': true,
            ':expectIsBought': false
        },
        UpdateExpression: `SET #stocks[${index}] = :isBought`, //
        ConditionExpression: `#stocks[${index}]= :expectIsBought` // 指定した配列番号のストックがまだ購入されていない場合のみ更新する
    };
    const data = await dynamoDB.update(params).promise();
}

```

配列要素は`#key1[1]`のように参照できる。conditionExpressionは文字列型なので、配列番号は文字列への埋め込みが必要

#ConditionExpressionの条件を配列内オブジェクトの１要素にしたい場合

例：productTable

```js
{
 name:"apple", // hash key
 price: 300,
 stocks: [
  {reservation: 'Aさん', isBought: true},
  {reservation: 'Bさん', isBought: false}
 ]

}
```

実行コード(lambda)

```js

const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();

exports.handler = async(event, context) => {
     const index = 1
     const reservation = 'Bさん'
     const params = {
        TableName: "productTable",
        Key:{
            name: "apple"
        },
        ExpressionAttributeNames: {
            '#stocks': 'stocks',
            '#isBought': 'isBought',
            '#reservation': 'reservation'
        },
        ExpressionAttributeValues: {
            ':isBought': true,
            ':expectIsBought': false,
            ':expectReservation': reservation
        },
        UpdateExpression: `SET #stocks[${index}].#isBought = :isBought`, 
        ConditionExpression: `#stocks[${index}].#isBought = :expectIsBought AND #stocks[${index}].#reservation = :expectReservation` // 指定した配列番号のストックがまだ購入されていない場合かつBさん予約の商品である場合のみ更新する
    };
    const data = await dynamoDB.update(params).promise();
}

```