How to remove an item from a StringSet in DynamoDB? - node.js

I have this table in DynamoDB:
{
"lectures": {
"L": [
{
"M": {
"lecture_id": {
"S": "CC0"
},
"students": {
"SS": [
"",
"test"
]
}
}
}
]
}
I want to remove "test" from the StringSet students.
Looking at the docs, I tried this code:
function removeLecture(event){
const students = 'lectures[' + Number.parseInt(event.lecture_id) + '].students';
console.log('test: ' + students);
const params = {
TableName: 'TableName',
Key: {
'Key': key
},
UpdateExpression: `DELETE ${students} :student`,
ExpressionAttributeValues: {
':student' : {
'SS': event.student
}
},
ReturnValues : 'UPDATED_NEW'
}
return ddb.update(params).promise();
}
Anyway, I receive a ValidationException:
START RequestId: 3857a2c2-23a8-4c0c-b080-516669c6e615 Version: $LATEST
2021-08-18T10:21:39.078Z 3857a2c2-23a8-4c0c-b080-516669c6e615 INFO test: lectures[0].students
2021-08-18T10:21:39.831Z 3857a2c2-23a8-4c0c-b080-516669c6e615 INFO ValidationException: Invalid UpdateExpression: Incorrect operand type for operator or function; operator: DELETE, operand type: MAP, typeSet: ALLOWED_FOR_DELETE_OPERAND
I have also tried with ExpressionAttributeValues: {':student' : event.student}, but still I receive the same error with STRING instead of MAP.
How can I remove the String from the StringSet?

After long trying, I discovered the function createSet() of the AWS.DynamoDB.DocumentClient class. I tried to use it, and it works. So the answer is: if you want to remove a String from a StringSet, you have to create another StringSet and remove the latter from the original.
Here is the code:
function removeLecture(event){
const students = 'lectures[' + Number.parseInt(event.lecture_id) + '].students';
console.log('test: ' + students);
const params = {
TableName: 'TableName',
Key: {
'Key': key
},
UpdateExpression: `DELETE ${students} :student`,
ExpressionAttributeValues: {
':student' : ddb.createSet(event.student)
},
ReturnValues : 'UPDATED_NEW'
}
return ddb.update(params).promise();
}

Related

AWS Update item with list on DynamoDB using lambda Nodejs

I have an item in my DynamoDB table that looks like this:
{
"qid": {
"S": "0"
},
"options": {
"L": [
{
"S": "Summer"
},
{
"S": "Winter"
}
]
},
"votes": {
"L": [
{
"N": "11"
},
{
"N": "13"
}
]
}
}
With a lambda function, I want to update one of the elements in the votes list. I want to update the index that matches the index of the option I get from the event (the event has qid, option and number of votes). So here is what I tried:
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context) => {
const qid = event.qid;
const option = event.option;
const vote = event.vote;
const params = {
TableName : 'QnV',
Key: {
qid: qid
}
}
try {
const data = await docClient.get(params).promise()
const options = data.Item.options;
const index = options.findIndex(option)
const updateParams = {
TableName: 'QnV',
Key: {
'qid': qid
},
UpdateExpression: 'set votes[:index] = :vote',
ExpressionAttributeValues: {
':index': index,
':vote': vote
},
ReturnValues: 'UPDATED_NEW'
};
const updateData = await docClient.update(updateParams).promise();
return {
statusCode: 200,
body: JSON.stringify(updateData)
};
} catch (err) {
return {
statusCode: 500,
body: JSON.stringify(err)
};
}
};
I tried to test it with this:
{
"qid": "0",
"option": "Winter",
"vote": 14
}
But I get an error of 500 and an empty body. It looks like the update params variable is empty, but I don't understand why.
I tried to search online but it looks like I'm doing everything correctly (but if that was the case I didn't have a problem). I also didn't forget to grant permission to the function.
Edit: As suggested, I change the body of the error to err.message, and got this:
{
"statusCode": 500,
"body": "\"Winter is not a function\""
}
Please help me find the bug, I would really appreciate it!
Thank you
After more debugging I found the problems. There were two.
Incorrect index method.
const index = options.findIndex(option)
Changed to:
const index = options.indexOf(option)
The way I tried to find the index in updateParams was incorrect. Instead of:
UpdateExpression: 'set votes[:index] = :vote',
ExpressionAttributeValues: {
':index': index,
':vote': vote
},
It should be:
UpdateExpression: `set votes[${index}] = :vote`,
ExpressionAttributeValues: {
':vote': {N: vote},
},

Dynamodb sdk update entry at specific index in list in a map using node js

I am trying to update a specific list entry using a counter.
const taskParams = {
TableName: CONST.NAMES.SUJET_TABLE_NAME,
Key: {
id: defectId
},
UpdateExpression: "SET #list.#epicType.#tasks[#taskIndex].#tracking =:attrValue ",
ExpressionAttributeNames: {
'#list': 'epicsList',
'#tasks': 'tasks',
'#epicType': epicType,
'#taskIndex': taskCounter,
'#tracking': 'tracking',
},
ExpressionAttributeValues: {
':attrValue': epicData["tasks"][0]["tracking"],
},
};
try {
await documentClient.update(taskParams).promise();
console.log("Task successfully created.")
} catch (err) {
console.log("Unable to create Task ", err);
}
When executing I get the following error :
ValidationException: Invalid UpdateExpression: Syntax error; token: "#taskIndex", near: "[#taskIndex]"
Is the syntax wrong or is there something else I am not aware of?
I finally found the answer, for those who are having trouble with this :
const taskParams = {
TableName: CONST.NAMES.SUJET_TABLE_NAME,
Key: {
id: defectId
},
UpdateExpression: "SET #list.#epicType.#tasks[" + taskIndex+ "].#tracking =:attrValue ",
ExpressionAttributeNames: {
'#list': 'epicsList',
'#tasks': 'tasks',
'#epicType': epicType,
'#tracking': 'tracking',
},
ExpressionAttributeValues: {
':attrValue': epicData["tasks"][0]["tracking"],
},
};

update List of maps in Dynamodb

Below is my DynamoDB structure:
{
id : "Sample_id",
details: {
wf: [{
wid: "12345",
wname: "name_1"
}]
}
}
Now I want add a new map object to "wf", I have tried both ADD and SET with update but both replacing the existing object with the new one.
Below is the code i tried
var params = {
TableName: p_table,
Key: {
"id" : "Sample_id"
},
UpdateExpression: "SET details.wf = list_append(details.wf, :vals)",
ExpressionAttributeValues: {
":vals" : [{'wid': "98765", 'wname': 'name_2'}]
}
};
ddb.update(params, function(err, data) {
if(err)
console.log(err);
else
console.log(data)
});
Current output with the above code:
{
id : "Sample_id",
details: {
wf: [{
wid: "98765",
wname: "name_2"
}]
}
}
Expected Output:
{
id : "Sample_id",
details: {
wf: [{
wid: "12345",
wname: "name_1"
},
{
wid: "98765",
wname: "name_2"
}]
}
}
How can I achieve the expected out
Updated my param object to below and it worked for me
Added an index 99999 (some big random index beyond which I do not expect my list to grow), in-spite of index 99999 while appending the data in lambda to the existing list it sits with next available index
var params = {
TableName: p_table,
Key: {
"id" : "Sample_id"
},
UpdateExpression: "SET details.wf[999999] = :vals",
ExpressionAttributeValues: {
":vals" : {'wid': "98765", 'wname': 'name_2'}
}
};
ddb.update(params, function(err, data) {
if(err)
console.log(err);
else
console.log(data)
});

How to Append value to an empty list attribute in AWS DynamodB

I am trying to append a message to an empty list in AWS DynamoDB.
Here is the error that I get when I run the function
Invalid UpdateExpression: Incorrect number of operands for operator
or function; operator or function: list_append, number of operands: 1
Data structure in DynamodB
Below is the code:
var caseId = "1734009";
var chatMessage = { "2018-04-20T15:02:48Z":
{
"userId": "wQnUJrklzwWBDOsx83XVETSS7us2",
"message": "How are you"
}
}
var params = {
TableName : 'CHATS',
Key: {
"CASE_ID" : caseId
},
UpdateExpression : "SET CHAT_MESSAGES = list_append(:i)",
ExpressionAttributeValues : {
':i': [chatMessage],
},
ReturnValues:'UPDATED_NEW' // OTHER OPTIONS: NONE | ALL_OLD | UPDATED_OLD | ALL_NEW | UPDATED_NEW
};
documentClient.update(params, function(err, data) {
if(err) {
var message = "Chat message could not be saved, error:" + JSON.stringify(err, null, 2);
res.json({"status": "failure", "statusCode" : 400, "message": message});
} else {
next();
}
});
list_append takes two arguments, not one: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html
By using the below params and referring to the AWS Docs provided by Jason Livesay, I was able to add items to an empty list.
var params = {
TableName : 'CHATS',
Key: {
"CASE_ID" : caseId
},
UpdateExpression : "SET #ri = list_append(#ri, :vals)",
ExpressionAttributeNames: {
"#ri": "CHAT_MESSAGES"
},
ExpressionAttributeValues : {
':vals': [chatMessage]
},
ReturnValues:'UPDATED_NEW' // OTHER OPTIONS: NONE | ALL_OLD | UPDATED_OLD | ALL_NEW | UPDATED_NEW
};
Look at the following example. Attribute 'graph' is created as empty list which can be later updated using list_append(). Please note I am using Boto3(python) to update my dynamodb table.
dynamoTable = dynamodb.Table('abc')
dynamoTable.put_item(
Item={
''<primary_key>'': <primary_key_value>,
'First Name': 'first_name',
'Last Name': 'last_name',
'password_hash': 'password_hash',
'salt': 'salt',
'graph': [],
}
Look at the following code to append element/message to the list :
dynamoTable = dynamodb.Table('abc')
dynamoTable.update_item(
Key={
'<primary_key>': '<primary_key_value>',
},
UpdateExpression="set #Graph = list_append(#Graph, :name)",
ExpressionAttributeNames={
'#Graph': 'graph',
},
ExpressionAttributeValues = {
':name': [# {'myObject':
{
"userId": "wQnUJrklzwWBDOsx83XVETSS7us2",
"message": "How are you"
}
#}
],
}
The code above will append a map in 'graph' with the "userId" and "message"

node.js passing a parameter to DynamoDB updateItem method

I want to write a function that updates given parameter in dynamodb.
For example in a dynamodb table where each userId is the key I have values like
{
"categoryname": "a",
"skillState": "a",
"skipcount": 1,
"userId": "amzn1.ask.account.xxx”
}
I wanna set the "categoryname": "b" although there might be 10-15 fields like this so I dont wanna hard code the field name.
function (userId,itemToUpdate,itemValue,callback) {
var updateExpressionString = "SET #"+itemToUpdate+" =:val1";
var expressionAtt = '#'+itemToUpdate + '';
console.log(updateExpressionString)
console.log(expressionAtt)
this.dynamodb.updateItem({
TableName: constants.dynamoDBDetailTableName,
Key: {
userId: {
S: userId
}
},
UpdateExpression: updateExpressionString,
ExpressionAttributeNames : {
expressionAtt : itemToUpdate
},
ExpressionAttributeValues : {
':val1': {'S':itemValue}
}
}, function (err, data) {
if (err) {
console.log(err)
console.log('Error ')
} else if (data.Item === undefined) {
}else {
console.log(data)
}
});
}
In ExpressionAttributeNames:
{ ValidationException: ExpressionAttributeNames contains invalid key: Syntax error; key: "expressionAtt"
This throws error obviously thinking that expressionAtt is the key while it is a local variable.
I am new to node.js , how can pass the local variable in to ExpressionAttributeNames and ExpressionAttributeValues
One way of dealing with this could be to pull the object out of updateItem, put it into its own variable like so:
var item = {
TableName: constants.dynamoDBDetailTableName,
Key: {
userId: {
S: userId
}
},
UpdateExpression: updateExpressionString,
ExpressionAttributeNames: {},
ExpressionAttributeValue: {
':val1': {'S': itemValue}
}
};
item.ExpressionAttributeNames[expressionAtt] = itemToUpdate;
this.dynamodb.updateItem(item);
I believe that will fix your problem

Resources