Search code examples
amazon-dynamodbdynamodb-queries

DynamoDB transactional insert with multiple conditions (PK/SK attribute_not_exists and SK attribute_exists)


I have a table with PK (String) and SK (Integer) - e.g.

PK_id                   SK_version      Data
-------------------------------------------------------
c3d4cfc8-8985-4e5...    1               First version
c3d4cfc8-8985-4e5...    2               Second version

I can do a conditional insert to ensure we don't overwrite the PK/SK pair using ConditionalExpression (in the GoLang SDK):

putWriteItem := dynamodb.Put{
    TableName:                           "example_table",
    Item:                                itemMap,
    ConditionExpression:                 aws.String("attribute_not_exists(PK_id) AND attribute_not_exists(SK_version)"),
}

However I would also like to ensure that the SK_version is always consecutive but don't know how to write the expression. In pseudo-code this is:

putWriteItem := dynamodb.Put{
    TableName:                           "example_table",
    Item:                                itemMap,
    ConditionExpression:                 aws.String("attribute_not_exists(PK_id) AND attribute_not_exists(SK_version) **AND attribute_exists(SK_version = :SK_prev_version)**"),
}

Can someone advise how I can write this?

in SQL I'd do something like:

INSERT INTO example_table (PK_id, SK_version, Data)
SELECT {pk}, {sk}, {data}
WHERE NOT EXISTS (
                    SELECT  1 
                    FROM    example_table
                    WHERE   PK_id = {pk}
                       AND  SK_version = {sk}
                 )
   AND EXISTS    (
                    SELECT  1
                    FROM    example_table
                    WHERE   PK_id = {pk}
                       AND  SK_version = {sk} - 1
                 )

Thanks


Solution

  • A conditional check is applied to a single item. It cannot be spanned across multiple items. In other words, you simply need multiple conditional checks. DynamoDb has transactWriteItems API which performs multiple conditional checks, along with writes/deletes. The code below is in nodejs.

      const previousVersionCheck = {
        TableName: 'example_table',
        Key: {
          PK_id: 'prev_pk_id',
          SK_version: 'prev_sk_version'
        },
        ConditionExpression: 'attribute_exists(PK_id)'
      }
    
      const newVersionPut = {
        TableName: 'example_table',
        Item: {
          // your item data
        },
        ConditionExpression: 'attribute_not_exists(PK_id)'
      }
    
      await documentClient.transactWrite({
        TransactItems: [
          { ConditionCheck: previousVersionCheck },
          { Put: newVersionPut }
        ]
      }).promise()
    

    The transaction has 2 operations: one is a validation against the previous version, and the other is an conditional write. Any of their conditional checks fails, the transaction fails.