Amazon DynamoDBのUpdateItemでのif_not_exists構文の使いどころを確認してみた

2022.12.13

こんにちは、CX事業本部 IoT事業部の若槻です。

Amazon DynamoDBではアイテム更新(UpdateItem)時にAttributeの上書きを防止するための機能があります。

If you want to avoid overwriting an existing attribute, you can use SET with the if_not_exists function. (The function name is case sensitive.) The if_not_exists function is specific to the SET action and can only be used in an update expression. The syntax is as follows.

- if_not_exists (path, value)

If the item does not contain an attribute at the specified path, if_not_exists evaluates to value; otherwise, it evaluates to path.

第一引数に指定したパスのAttributeがアイテムに未設定であれば、第二引数に指定した値で置き換えるというものです。

今回は、このif_not_exists構文を利用するパターンをいくつか試して、使いどころを確認してみました。

環境準備

sampleTableテーブル作成。

aws dynamodb create-table \
  --table-name sampleTable \
  --attribute-definitions AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

アイテム作成。

aws dynamodb put-item --table-name sampleTable --item '{"id":{"S":"s001"}}'
aws dynamodb put-item --table-name sampleTable --item '{"id":{"S":"s002"}}'
aws dynamodb put-item --table-name sampleTable --item '{"id":{"S":"s003"}}'

if_not_exists構文を使う

値の設定

SET path = if_not_exists (path, value)というUpdateExpressionの場合。

Attribute(username)が未設定の場合、指定した値(mesoko)が設定されました

$ aws dynamodb update-item \
    --table-name sampleTable \
    --key '{"id":{"S":"s001"}}' \
    --update-expression "SET username = if_not_exists(username, :n)" \
    --expression-attribute-values '{":n": {"S": "mesoko"}}' \
    --return-values ALL_NEW
{
    "Attributes": {
        "id": {
            "S": "s001"
        },
        "username": {
            "S": "mesoko"
        }
    }
}

Attribute(username)が設定済みの場合も、指定した値(mesoko2)が設定されませんでした

$ aws dynamodb update-item \
    --table-name sampleTable \
    --key '{"id":{"S":"s001"}}' \
    --update-expression "SET username = if_not_exists(username, :n)" \
    --expression-attribute-values '{":n": {"S": "mesoko2"}}' \
    --return-values ALL_NEW
{
    "Attributes": {
        "id": {
            "S": "s001"
        },
        "username": {
            "S": "mesoko"
        }
    }
}

Attributeが未設定の場合のみ更新されるという、上書き防止の動作を確認することができました。

ちなみにSET path = valueとすれば、Attributeが設定済みであるか未設定であるかに関わらず、指定の値を設定できました。

aws dynamodb update-item \
    --table-name sampleTable \
    --key '{"id":{"S":"s001"}}' \
    --update-expression "SET username2 = :n" \
    --expression-attribute-values '{":n": {"S": "mesoko2"}}' \
    --return-values ALL_NEW
{
    "Attributes": {
        "username": {
            "S": "mesoko"
        },
        "username2": {
            "S": "mesoko2"
        },
        "id": {
            "S": "s001"
        }
    }
}

数値加算

SET path = if_not_exists (path, value) + value2という数値加算を行うUpdateExpressionの場合。

Attribute(price)が未設定の場合if_not_exists第二引数(:i + 加算数(:p)の値で更新されました。

$ aws dynamodb update-item \
    --table-name sampleTable \
    --key '{"id":{"S":"s002"}}' \
    --update-expression "SET price = if_not_exists(price, :i) + :p" \
    --expression-attribute-values '{":p": {"N": "100"}, ":i": {"N": "10"}}' \
    --return-values ALL_NEW
{
    "Attributes": {
        "id": {
            "S": "s002"
        },
        "price": {
            "N": "110"
        }
    }
}

Attribute(price)が設定済みの場合if_not_exists第一引数(price + 加算数(:p)の値で更新されました。

$ aws dynamodb update-item \
    --table-name sampleTable \
    --key '{"id":{"S":"s002"}}' \
    --update-expression "SET price = if_not_exists(price, :i) + :p" \
    --expression-attribute-values '{":p": {"N": "100"}, ":i": {"N": "10"}}' \
    --return-values ALL_NEW
{
    "Attributes": {
        "id": {
            "S": "s002"
        },
        "price": {
            "N": "210"
        }
    }
}

数値加算では、Attributeが未設定の場合に、加算される数に既定値が使用される動作を確認できました。

リストへの要素追加

SET path = list_append(if_not_exists (path, value), value2)というリストへの要素追加を行うUpdateExpressionの場合。

Attribute(office_list)が未設定の場合if_not_exists第二引数(:init_listに要素(:e)が追加されました。

$ aws dynamodb update-item \
    --table-name sampleTable \
    --key '{"id":{"S":"s003"}}' \
    --update-expression "SET office_list = list_append(if_not_exists(office_list, :init_list), :e)" \
    --expression-attribute-values '{":init_list": {"L": [{"S": "sakuma"}]}, ":e": {"L": [{"S": "iwamoto-cho"}]}}' \
    --return-values ALL_NEW
{
    "Attributes": {
        "id": {
            "S": "s003"
        },
        "office_list": {
            "L": [
                {
                    "S": "sakuma"
                },
                {
                    "S": "iwamoto-cho"
                }
            ]
        }
    }
}

Attribute(office_list)が設定済みの場合if_not_exists第一引数(office_listに要素(:e)が追加されました。

$ aws dynamodb update-item \
    --table-name sampleTable \
    --key '{"id":{"S":"s003"}}' \
    --update-expression "SET office_list = list_append(if_not_exists(office_list, :init_list), :e)" \
    --expression-attribute-values '{":init_list": {"L": [{"S": "sakuma"}]}, ":e": {"L": [{"S": "hibiya"}]}}' \
    --return-values ALL_NEW
{
    "Attributes": {
        "id": {
            "S": "s003"
        },
        "office_list": {
            "L": [
                {
                    "S": "sakuma"
                },
                {
                    "S": "iwamoto-cho"
                },
                {
                    "S": "hibiya"
                }
            ]
        }
    }
}

リストへの要素追加では、Attributeが未設定の場合に、追加されるリストに既定値が使用される動作を確認できました。

後始末

テーブル削除。

aws dynamodb delete-table --table-name sampleTable

おわりに

Amazon DynamoDBのUpdateItemでのif_not_exists構文の使いどころを確認してみました。

Attributeが未設定の場合を考慮する必要がある場合に役に立ちそうです。

参考

以上