I have a scenario where I want to create an item if it doesn't exist, or update an item - incrementing a total, if it already exists.
I was running into problems splitting the two operations, so I am now trying to do both using UpdateItem in a single command.
I've tried 3 different approaches none work, and they have different errors listed below, the problem it seems is creating the map and trying to update it in a single command - what should my update params look like?
Attempt one:
{
TableName: TableName,
Key: {
'key': key
},
UpdateExpression: `
ADD #total :change
, mapname.#type.#total :one
`,
ExpressionAttributeValues: {
':change': change,
':one': 1
},
ExpressionAttributeNames: {
'#type': 'dynamicstring',
'#total': 'total'
}
};
With an error of: ValidationException: The document path provided in the update expression is invalid for update
Attempt two:
{
TableName: TableName,
Key: {
"key": key
},
UpdateExpression: `
SET custommap = if_not_exists(custommap, :emptyMap)
SET #total = #total + :change,
custommap.#type.#total = custommap.#type.#total + :one
`,
ExpressionAttributeValues: {
':change': change,
':one': 1,
':emptyMap': {
'M': {
'dynamicstring': {
'M': {
'total': {
'N': 0
}
}
}
}
}
},
ExpressionAttributeNames: {
'#type': 'dynamicstring',
'#total': 'total'
}
}
With an error of: ValidationException: Invalid UpdateExpression: The "SET" section can only be used once in an update expression;
So when I use UpdateItem to create or update (increment) a map within an Item, what syntax is correct?
Thanks
SET will only stop you overwriting an attribute, not an item.
They way to achieve this is:
Use GetItem with your key to see if the item already exists
If the item exists, then do an UpdateItem and increment the counter
If the item does not exist, then use PutItem
Related
Say I want to add a record like below:
{
id:876876,
username:example,
email:xxxxxx#xxx.com,
phone:xxxxxxx,
created_at:current_date,
updated_at:current_date
}
What I want is to check with the id if record exist created_date should not be modified only updated_date should have current_date.
Can we do this with put method without having to be making a get item call?
To create a new item or update an existing item with conditional expression. Prefer to use updateItem rather than putItem.
If the Hash key (Primary key) doesn't exist both putItem and updateItem create a new record. If the record already exist, putItem will completely override the existing row but updateItem only updates the attributes passed in UpdateExpression not a whole record.
In your case, use if_not_exists() function to check whether the created_at field already exists or not. If exists created_at will not be overridden.
Update expression: "SET #email = :email, #created_at = if_not_exists(#created_at, :created_at), #updated_at = :updated_at"
Sample snippet
var params = {
ExpressionAttributeNames: {
"#email": "email",
"#created_at": "created_at",
"#updated_at": "updated_at"
},
ExpressionAttributeValues: {
":email": {
S: "test2#grr.la"
},
":created_at": {
S: date.toISOString()
},
":updated_at": {
S: date.toISOString()
}
},
Key: {
"id": {
S: "T1"
}
},
ReturnValues: "ALL_NEW",
TableName: "stack",
UpdateExpression: "SET #email = :email, #created_at = if_not_exists(#created_at, :created_at), #updated_at = :updated_at"
};
ddb.updateItem(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data);
})
I'm keeping a list of followers and tracking the size of the list simultaneously
I have an attribute that's a String Set and another attribute that tracks the number of elements in the String Set. I do this by updating both attributes simultaneously. As a guard, I only want the number to increment when the new element doesn't already exist in the Set. The Set by nature won't update if the element is already in it.
Here're the params I use for the update call:
const paramsForUpdatingUserBeingFollowed = {
TableName: process.env.usersTableName,
Key: {
userId: userBeingFollowed
},
ConditionExpression: "NOT(contains(isFollowedBy, :userMakingRequest))",
UpdateExpression:
"ADD isFollowedBy :userMakingRequest, isFollowedByCount :num",
ExpressionAttributeValues: {
":userMakingRequest":
dynamoDbLib.docClient.createSet([userMakingRequest]) || null,
":num": 1
},
ReturnValues: "ALL_NEW"
};
I expect the update action to fail if an element already exists in the set. The condition expression appears to have no effect; the attribute still gets updated.
The second argument to contains should always be a string!
I was passing in a SET, hence the unpredictable behavior.
These params produce the behavior I'm looking for:
const paramsForUpdatingUserBeingFollowed = {
TableName: process.env.usersTableName,
Key: {
userId: userBeingFollowed
},
ConditionExpression: "NOT(contains(isFollowedBy, :userMakingRequestStr))",
UpdateExpression:
"ADD isFollowedBy :userMakingRequest, isFollowedByCount :num",
ExpressionAttributeValues: {
":userMakingRequest":
dynamoDbLib.docClient.createSet([userMakingRequest]) || null,
":userMakingRequestStr": userMakingRequest || null,
":num": 1
},
ReturnValues: "ALL_NEW"
};
I need another set of eyes on this. For the life of me I see no issues with this parameter set, used for Dynamo DocumentClient, update method - (here: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#update-property ).
{
TableName: "mygame-dev",
Key: { pk: "09d017aa-cbf7-42ce-be6a-a94ecb58f9a7", sk: "GAME" },
ExpressionAttributeNames: { "#GAMELASTUPDATED": "gameLastUpdated", "#GAMETITLE": "gameTitle" },
ExpressionAttributeValues: { ":gamelastupdated": 1556376010704, ":gametitle": "test title 1" },
UpdateExpression: "SET #GAMELASTUPDATED = :gamelastupdated, #GAMETITLE = :gametitle",
ReturnValues: "ALL_NEW"
};
Error:
ValidationException: ExpressionAttributeNames can only be specified
when using expressions
Any thoughts?
Disregard, this was a copy and paste issue... I was using "query" not "update"
Was:
const updateGameResult = await ddbCall("query", params);
Should have been:
const updateGameResult = await ddbCall("update", params);
I have a column in DynamoDB table which will be of the following type:
{"History": {"L": [{"M": {"id": {"S": "id"}, "Flow": {"L":[{"S": "test2"}]},"UUID": {"S": "1234"}}}]}}
Column History is of type 'List' in which each list element is a map with 3 values - id (string), Flow (List), uuid (String)
My code would trigger update-item multiple times and all I want is, given the same id and uuid, new values are to be appended into the Flow list without disturbing anything else.
I have referred the documentation but unable to figure out how to write the UpdateExpression.
My existing code is as below:
response_update = client.update_item(
TableName = 'tableName',
Key = {
'k1': {
'S': 'v1'
},
'k2': {
'S': 'v2'
}
},
UpdateExpression="SET History=list_append(if_not_exists(History, :empty_list), :attrValue)",
ExpressionAttributeValues = {":attrValue" :{"L":[ { "M" : { "id" : { "S" : "123" }, "UUID" : { "S" : "uuid123" }, "Flow" : { "L" : [ { "S" : "now2" } ] } } } ]},":empty_list":{"L":[]}})
With this code, each time I trigger the update function, a new element in getting appended in History list. Instead, I need my desired string to be appended to the Flow list.
Please let me know how the expression should be.
I am little confused about retrieving data from dynamodb ... connecting is not an issue cause I am getting The provided key element does not match the schema
:the example provided from AWS
var table = new AWS.DynamoDB({params: {TableName: 'MY_TABLE'}});
var key = 'UNIQUE_KEY_ID';
var itemParams = {Item: {id: {S: key}, data: {S: 'data'}}};
table.getItem({Key: {id: {S: key}}}, function(err, data) {
console.log(data.Item); // print the item data
});
in my case the unique key is "time" and what I want to do is retrieve by key (not unique)
getItem only works on the primary key. From the docs: The GetItem operation returns a set of attributes for the item with the given primary key. See docs.
To solve this, create a Global Secondary Index that has "key" as HASH and "time" as RANGE. Then do a query operation using that index as IndexName:
var params = {
IndexName: 'your-new-GSI-index',
KeyConditionExpression: '#key = :key',
ExpressionAttributeNames: { '#key': 'key },
ExpressionAttributeValues: { ':key': { S: yourKeyVar } }
}
table.query(params, callback);
(did not test this code, but should work)