27
votes

Im trying to write a lambda function to add new data to a DynamoDB Table. From reading the docs at:

http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#put-property The PUT method: "Creates a new item, or replaces an old item with a new item by delegating to AWS.DynamoDB.putItem()."

Other than doing a check for an object before 'putting' is there a setting or flag to fail the object exists when the PUT is attempted?

I can see in

params -> Expected -> Exists (Bool)

but can't see any documentation on what this does.

What would be the best architecture (or fasted) to prevent an item overwrite?

Query the table first and if no item exists then add the item

or

Attempt to insert the item and on failure because of duplicate entry report this back? (Is there a way to prevent item overwrite?)
3

3 Answers

36
votes

The ConditionExpression can be used to check whether the key attribute values already exists in table and perform the PUT operation only if the key values are not present in the table.

When you run the below code, first time the put operation should be successful. In the second run, the put operation should fail with "Conditional request failed" exception.

My movies table has both partition and sort keys. So, I have used both the attributes in conditional expression.

Sample code with conditional put:-

var table = "Movies";

var year = 1502;
var title = "The Big New Movie";

var params = {
    TableName:table,
    Item:{
        "yearkey": year,
        "title": title,
        "info":{
            "plot": "Nothing happens at all.",
            "rating": 0
        }
    },
    ConditionExpression: "yearkey <> :yearKeyVal AND #title <>  :title",
    ExpressionAttributeNames: { 
        "#title" : "title" 
     },
    ExpressionAttributeValues: {
        ":yearKeyVal" : year,
        ":title": {"S": title}
    }
};

console.log("Adding a new item...");
docClient.put(params, function(err, data) {
    if (err) {
        console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
    } else {        
        console.log("Added item:", JSON.stringify(data, null, 2));
    }
});

Exception when put operation is performed second time:-

Unable to add item. Error JSON: {
  "message": "The conditional request failed",
  "code": "ConditionalCheckFailedException",
  "time": "2017-10-02T18:26:26.093Z",
  "requestId": "7ae3b0c4-3872-478d-908c-94bc9492a43a",
  "statusCode": 400,
  "retryable": false,
  "retryDelay": 0
}
9
votes

I see that this question relates to JavaScript language, anyway I will write also for Java (maybe it will be useful for someone):

DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression();
Map<String, ExpectedAttributeValue> expectedAttributes =
        ImmutableMapParameter.<String, ExpectedAttributeValue>builder()
                .put("hashKeyAttrName", new ExpectedAttributeValue(false))
                .put("rangeKeyAttrName", new ExpectedAttributeValue(false))
                .build();
saveExpression.setExpected(expectedAttributes);
saveExpression.setConditionalOperator(ConditionalOperator.AND);

try {
    dynamoDBMapper.save(item, saveExpression);
} catch (ConditionalCheckFailedException e) {
    e.printStackTrace();
}

ConditionalCheckFailedException will be thrown in case we will try to save item with already existing pair of hashKey and rangeKey in DynamoDB.

5
votes

As an addition to the correct answer of notionquest, the recommended way to express a condition that makes a putItem fail in case the item already exists is to assert that the partition key is not present in any potentially existing item:

"ConditionExpression": "attribute_not_exists(pk)",

This reads "if this item already exists before the putItem, make sure it does not already have a partition key" (pk being the partition key in this example). Since it is impossible for an item to exist without a partition key, this effectively means "make sure this item does not already exist".

See also the "Note" block at the beginning of this page: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/put-item.html

There's an example here when used to guarantee uniqueness of non key columns:

https://aws.amazon.com/blogs/database/simulating-amazon-dynamodb-unique-constraints-using-transactions/