Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly use list_append to keep values unique in DynamoDB?

In the following code, the contacts are added to the contacts attribute even if they already exists in it. But I would like to prevent duplicates because it would make no sense to have the same contact twice. I tried to use contains with no success. Do you have any idea on how I could manage that?

const contact = event.contactId
const params = {
    TableName,
    Key: {
        userId: event.userId
    },
    ReturnValues: 'UPDATED_NEW',
    UpdateExpression: 'set #contacts = list_append((if_not_exists(#contacts, :empty_list), :contact)',
    ExpressionAttributeNames: {
        '#contacts': 'contacts'
    },
    ExpressionAttributeValues: {
        ':contact': [contact],
        ':empty_list': []
    }
} // TODO: Must be unique IDs

dynamodb.update(params, (err, data) => {
    if (err) {
        console.log(err)
        callback(err)
    } else {
        console.log(data)
        const response = {
            statusCode: 200,
            message: "Successfully added contact " + contact,
            updatedContacts: data.Attributes.contacts
        }
        callback(null, response)
    }
})
like image 355
RoiGoujat Avatar asked Dec 11 '18 11:12

RoiGoujat


People also ask

How to set unique key in DynamoDB?

Storing the unique values This means we need to create a new table that has the unique values (the email address, in this example) as keys. Since DynamoDB supports composite keys, where the key is made up of a partition key and a sort key, it's best to store the type of the unique constraint as well as the value.

Does DynamoDB support Upsert?

UpdateItem behaves as an “UPSERT” operation. This means that if you try to update an item that doesn't exist, DynamoDB will automatically create it for you. Like with PutItem , you can add conditions to your UpdateItem API calls to modify this behavior, but there is no way to implement it service-side.

Can I update the sort key in DynamoDB?

Can we update the sort key in DynamoDB? No, you can not update the sort key after the table is provisioned. However, you can create a new table and put the existing data in the newly created table, and delete the old table.

What is atomic counter in DynamoDB?

You can use the UpdateItem operation to implement an atomic counter—a numeric attribute that is incremented, unconditionally, without interfering with other write requests. (All write requests are applied in the order in which they were received.) With an atomic counter, the updates are not idempotent.


1 Answers

To the best of my knowledge, you need to use a Set instead of a List of contact IDs to achieve what you want in only one call to DynamoDB. That being said, there are two ways to accomplish it with a Set, and it is possible to do this with a list if you’re okay making a read before you save the new contact.

Option 1

DynamoDB supports using ADD for Set attributes, and a Set will guarantee that no duplicate IDs are present. You can use ADD to silently succeed without causing a duplicate contact using this method.

If you use ADD, your update expression would be

ADD #contacts :contact

This method will work whether your contact IDs are a Number or a String.

Option 2

Use a condition expression to prevent the contact from being added if it’s already present. DynamoDB has a contains(path, operand) function that supports String and StringSet attributes.

You would use the same update expression as in Option 1, and the condition expression for this would be

attribute_not_exists(#contacts) OR NOT contains(#contacts, :contact)

This option will let you know that the contact is already present because if the contact is already present, you will get a ConditionalCheckFailed response from DynamoDB.

Option 3

Begin by reading the current List of contacts from DynamoDB. Verify in your application that you will not be adding a duplicate. If the new contact would be a duplicate, then you have nothing more to do. If the contact would be a duplicate, then you can save the updated contact list using these expressions

UpdateExpression: “SET #contacts = :updatedContacts”
ConditionExpression: "#contacts = :oldContacts”

In this case, :oldContacts should be the list that you initially read from DynamoDB.

The drawback of this approach is that a concurrent update to the list could cause a valid update to fail.

Option 4

This is the most involved of all the approaches. You can add a version attribute to the items in the table, and like Option 3, you first read the item, verify that the new contact is not a duplicate, and then make a conditional write. However, in this case, your conditional write would include

UpdateExpression: “SET #contacts = :updatedContacts, #version = :newVersion”,
ConditionExpression: “#version = :oldVersion”

This is known as Optimistic Locking. While not part of the JavaScript SDK, it is simple enough to implement on your own in this case.

Concluding Notes

You should be aware that Options 3 and 4 won’t work for a global table which is being concurrently update in multiple regions. Option 2 may fail to inform you of an attempt to add a duplicate under those same conditions, but it will still guarantee that there are no duplicates present. (Global Tables are eventually consistent between regions, so the condition expression may evaluate to true for one region, but not for another.)

Finally, you may find the Comparison Operator and Function Reference helpful if you want more details about any of the functions that are available to use in a condition or update expression.

like image 156
Matthew Pope Avatar answered Oct 16 '22 02:10

Matthew Pope