Victor
Victor

Reputation: 14573

Condition Expression seems not to be taken into account on DynamoDB PutItem

It's one of the first times I'm using DynamoDB and have created a table for user accounts in my system.

Table definition follows:

{
    "Table": {
        "AttributeDefinitions": [
            {
                "AttributeName": "AccountId",
                "AttributeType": "B"
            },
            {
                "AttributeName": "Email",
                "AttributeType": "S"
            }
        ],
        "KeySchema": [
            {
                "AttributeName": "AccountId",
                "KeyType": "HASH"
            }
        ],
        "GlobalSecondaryIndexes": [
            {
                "IndexName": "EmailAddress",
                "KeySchema": [
                    {
                        "AttributeName": "Email",
                        "KeyType": "HASH"
                    }
                ],
                "Projection": {
                    "ProjectionType": "ALL"
                },
                "IndexStatus": "ACTIVE"
            }
        ]
    }
}

Some fields have been omitted for brevity.

I am using Rusoto DynamoDB client and this is what the PutItem call looks like:

dynamodb_client.put_item(PutItemInput{
    item: account_doc.as_hashmap(),
    table_name: accounts_datastore_name,
    condition_expression: Some("attribute_not_exists(Email) and attribute_not_exists(AccountId)".to_string()),
    ..PutItemInput::default()
});

As far as I understand, this should have the following behavior: fail the transaction if there are documents that contain Email with the value that I'm supplying in the new document or AccountId with the value I'm supplying in the new document.

I've been able to submit the same document multiple times without any error from the service. The documents are all in the table.

Any ideas why this condition expression is allowing documents to pass?

Upvotes: 0

Views: 99

Answers (1)

Jason Wadsworth
Jason Wadsworth

Reputation: 8887

You're misunderstanding what the condition does. It does NOT look at other records in the table, only the record you are putting. Imagine the following two records:

{
  "AccountId": "<some_unique_id_1>",
  "Email": "[email protected]"
}

and

{
  "AccountId": "<some_unique_id_2>",
  "Email": "[email protected]"
}

Each of those will be saved because the AccountId value is different, meaning it's a different record in the table.

What your condition WILL do is keep you from writing a record with the same AccountId a second time. Because AccountId is the key to the record, and you are making sure the record you are writing doesn't have AccountId set, you will only be able to write the record if it is the first time it's being written. The Email part of the condition is actually not doing anything in this case.

There are two options you can consider for what you want.

  1. Switch the key to be the email address. That will make sure the email address is unique.
  2. Use a transaction to write your record. This isn't quite as simple as it might sound. Alex DeBrie has a pretty good article on using transactions.

Upvotes: 1

Related Questions