Darkstar
Darkstar

Reputation: 765

How to PutItem in dynamodb only if the item does not exist

I've got a table in dynamodb that only has a partition key, and I want to put an item in the db only if there isn't another item with the same partition key in the db.

I've tried using ConditionExpression attribute_not_exists to no avail.

The issue is that a certain item may not already exist in the db, so attribute_not_exists fails with an Invalid ConditionExpression: Operator or function requires a document path; operator or function: attribute_not_exists

Edit: Can you please post the full condition expression you used + the name of the table's partition key? –

Key: {
  username: event.pathParameters.username
},
ExpressionAttributeValues: {
  ":username": event.pathParameters.username
},
Item: {
    userId: event.requestContext.identity.cognitoIdentityId
},
ConditionExpression: "attribute_not_exists(:username)"

Upvotes: 8

Views: 10205

Answers (1)

Itay Maman
Itay Maman

Reputation: 30723

I just tried it with an attributes_not_exists condition and it seems to work as expected:

$ aws create-table --table-name example1 \
  --attribute-definitions AttributeName=pk,AttributeType=S \
  --key-schema AttributeName=pk,KeyType=HASH  --billing-mode PAY_PER_REQUEST
...
$ aws dynamodb put-item --table-name example1 \
    --item '{"pk": {"S": "abc"}, "city": {"S": "NYC"}}'
$ aws scan --table-name example1
{
    "Items": [
        {
            "city": {
                "S": "NYC"
            },
            "pk": {
                "S": "abc"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}

$ aws dynamodb put-item --table-name example1 \
  --item '{"pk": {"S": "abc"}, "city": {"S": "SF"}}' \
  --condition-expression "attribute_not_exists(pk)"

An error occurred (ConditionalCheckFailedException) ... 

$

Why did your request fail?

Based on the request you posted, I believe the culprit is your condition expression.

Instead of "attribute_not_exists(:username)" it should be attribute_not_exists(username). the : prefix denotes a value place holder whereas the attribute_not_exists function does not need a value, it needs an attribute name. Once you make the change you will also need to remove the ExpressionAttributeValues field because the value placeholder it defines (namely: :username) is no longer used anywhere in the request.

So, to summarize, this request should work for you:

Key: {
  username: event.pathParameters.username
},
Item: {
  userId: event.requestContext.identity.cognitoIdentityId
},
ConditionExpression: "attribute_not_exists(username)" 

One last (super minor) comment: the request you posted looks like an update request. I believe that for your use case you can use put which needs a somewhat simpler request. Specifically, in put you just specify the entire item, you do not need to specify the key separately from the other attributes. Note that just like update, put also supports a ConditionExpression which is the critical piece in the solution.

Upvotes: 5

Related Questions