Append to or create StringSet if it doesn't exist - amazon-web-services

So this should be simple...
I want to append a string to a StringSet in a DynamoDB if it exists, or create the StringSet property if it doesn't and set the value. If we could initialize the StringSet on creation with an empty array, it would be fine, but alas we can not.
Here's what I have so far:
const companiesTable = 'companies';
dynamodb.updateItem({
TableName: companiesTable,
Key: {
id: {
S: company.id
}
},
UpdateExpression: 'ADD socialAccounts = list_append(socialAccount, :socialAccountId)',
ExpressionAttributeValues: {
':socialAccountId': {
'S': [socialAccountId]
}
},
ReturnValues: "ALL_NEW"
}, function(err, companyData) {
if (err) return cb({ error: true, message: err });
const response = {
error: false,
message: 'Social account created',
socialAccountData
};
cb(response);
});
I've also tried...
UpdateExpression: 'SET socialAccounts = list_append(socialAccounts, :socialAccountId)',
ExpressionAttributeValues: {
':socialAccountId': {
S: socialAccountId
}
},
and...
UpdateExpression: 'ADD socialAccounts = :socialAccountId',
ExpressionAttributeValues: {
':socialAccountId': {
S: socialAccountId
}
},
and...
UpdateExpression: 'SET socialAccounts = [:socialAccountId]',
ExpressionAttributeValues: {
':socialAccountId': socialAccountId
},
and...
UpdateExpression: 'ADD socialAccounts = :socialAccountId',
ExpressionAttributeValues: {
':socialAccountId': socialAccountId
},
Among about every other variation of the above. Am I dumb? Is DynamoDB not capable of simple write/updates to an array type field? Do I REALLY have to lookup the item first to see if it has that field before I try to either add or set that field, because I can't instantiate that field with an empty array?

The ADD action handles the create/update logic, but only supports numbers and sets. You are trying to add a string type 'S'. You need to wrap this string in an array and pass it as a string set 'SS'. You also don't need the equals sign.
Your UpdateExpression and ExpressionAttributeValues should look like this:
UpdateExpression: 'ADD socialAccounts :socialAccountId',
ExpressionAttributeValues: {
':socialAccountId': {
'SS': [socialAccountId]
}
},
More information about updating items can be found here

Related

Nested attributes query on DynamoDB

I have something like this on Dynamo:
{
"mail": "user#anonymous.com",
"data": {
"type": 1
}
}
I have an index on "mail" attribute and I'm trying to query over all data found with an specified mail filtering the attribute "data". Something like this:
const params = {
TableName: 'tableName',
IndexName: "mail_index",
KeyConditionExpression: "#mail = :mail",
FilterExpression: '#status = :val',
ExpressionAttributeNames: {
'#mail': 'mail',
'#status': 'data.type'
},
ExpressionAttributeValues: {
':mail': 'user#anonymous.com',
':val': {N: 5}
}
};
dynamoDoc.query(params, (err, data) => {
console.log(data);
});
But I'm always getting an empty result. What am I doing wrong?
Try this:
const params = {
TableName: 'tableName',
IndexName: "mail_index",
KeyConditionExpression: "#mail = :mail",
FilterExpression: '#data.#type = :val',
ExpressionAttributeNames: {
'#mail': 'mail',
'#data': 'data',
'#type': 'type'
},
ExpressionAttributeValues: {
':mail': 'user#anonymous.com',
':val': {N: 5}
}
};
Because both data and type are reserved words, DynamoDB needs both of them to be escaped.

Is it possible to atomically add several items to DynamoDB array with checking their occurance in it (to avoid duplication)?

I have email black list stored as one item in DynamoDB
// item example
{
id: "blackList" // PrimaryKey of item
list: [ "email_1#example.com", "email_2#example.com" ]
}
It is possible to add new email to the list and the same time check if it's not already presented in the list (to avoid duplication) by atomic update:
const email = "email_new#example.com";
const params = {
TableName: "myTable",
Key: {
id: "blackList"
},
AttributeUpdates: {
list: {
Action: "ADD",
Value: [email] // several emails can also be added with incorrect Expected check
},
},
Expected: {
list: {
ComparisonOperator: "NOT_CONTAINS",
Value: email
},
}
};
await docClient.update(params).promise();
The question is whether it's possible to perform the same atomic operation for several emails at once?
Use a string set if you want there to be no duplicates. If you want to see if they existed in the set before you added them, return the old item.
const email = "email_new#example.com";
const params = {
TableName: "myTable",
Key: {
id: "blackList"
},
UpdateExpression: "ADD #email :email",
ExpressionAttributeNames: {
"#email": "email"
},
ExpressionAttributeValues: {
":email": docClient.createSet(email)
}
};
await docClient.update(params).promise();
This will add the email_new#example.com to the email attribute as a string set. If the email attribute doesn't exist on the object it will be created.
See https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.ADD for the documentation.

DynamoDB update list of list items

I need your help to update/add item in a list of list of DynamoDB.
I want to add and update a list that is composed by a list.
How can i do that? Thanks for your help
Here my example:
DATABASE
Item{1}
idProject:"15azeze-55ze"
dateCreationProjectString: 08/01/2018 14:6:32
environnements: List[1]
0 MAP {3}
idEnvironnement: "11-aa",
name:"Exemple Environnement"
tasks[0]
NodeJS code :
let newTask = {
authorTask : "Toto",
dateCreationTask: "01/01/1960",
idTask: "154-141-aa41",
nameTask: "Task name ..."
};
dynamodbdc.update({
TableName: "projects",
Key: { idProject: "15azeze-55ze", idEnvironnement:"11-aa" },
ReturnValues: 'ALL_NEW',
UpdateExpression: 'set #environnements.#tasks = list_append(if_not_exists(#environnements.#tasks, :empty_list), :newTask)',
ExpressionAttributeNames: {
'#environnements' : 'environnements',
'#tasks' : 'tasks',
},
ExpressionAttributeValues: {
':newTask': [newTask],
':empty_list': []
}
}, function(error, stdout) {
if(error){
console.log("error==", error)
else {
console.log("Nice thank you !!")
}
});
I would suggest to keep the tasks attribute as SET ('SS') which will simplify to NOT add the duplicate values. Otherwise, it would be difficult to fulfil the scenario.
Also, you may the index of the environments attribute list to form the update expression correctly. There is no options in the dynamodb api to workout the index automatically.
The below code should work if you define the tasks attribute as SET.
Insert item code:-
var params = {
TableName : table,
Item : {
"idProject" : "15azeze-55ze",
"dateCreationProjectString" : Date(),
"environnements" : [{
"idEnvironnement" : "11-aa",
"name" : "Example Environments",
"tasks" : docClient.createSet(["1"])
}]
}
};
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));
}
});
Sample update item working code:-
The ADD operation will ensure that it doesn't add duplicates to the SET
var docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: "projects",
Key: {
"idProject" : "15azeze-55ze"
},
UpdateExpression: 'ADD environnements['+0+'].tasks :tasksVal',
ExpressionAttributeValues: {
":tasksVal": docClient.createSet(["2"])
},
ReturnValues: "UPDATED_NEW"
};
console.log("Updating the item...");
docClient.update(params, function (err, data) {
if (err) {
console.error("Unable to update item. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("UpdateItem succeeded:", JSON.stringify(data));
}
});

Dynamo DB - Is is possible to update/insert nested field

I have a document in the dynamo DB in the following format.
{
key1: "value1",
key2: {sKeya: "A", sKeyB: "B" }
}
Is it possible to update (via JavaScript) directly nested field in the dynamo DB document? BCOZ I don't want to update key2 with all values again.
Example: I tried the below query, but did not work. It rather created one more field key2.sKeya: "ABC"
UpdateExpression: "set #field = :value",
ExpressionAttributeNames: {
"#field": "key2.sKeya"
},
ExpressionAttributeValues: {
":value": "ABC",
}
Has any one faced similar issue?
You need to define the object key as separate Attribute names.
So one alias for "key2" and one alias for "sKeya".
const AWS = require("aws-sdk");
const DOCCLIENT = new AWS.DynamoDB.DocumentClient();
exports.handler = (event, context, callback) => {
let entry = {
TableName: "test",
Key: {
"key1": "value1",
},
UpdateExpression: "set #parent.#child = :value",
ExpressionAttributeNames: {
"#parent": "key2",
"#child": "sKeya"
},
ExpressionAttributeValues: {
":value": "ABC",
},
ReturnValues: "UPDATED_NEW"
};
DOCCLIENT.update(entry, function(err, data) {
callback(null, 'done');
});
};

AWS DynamoDB Attempting to ADD to a Set - Incorrect Operand

I am creating an API using Nodejs and DynamoDB as a back end. I am attempting to update an item to add to a set of "friends". When I update the user, I get the error, "Invalid UpdateExpression: Incorrect operand type for operator or function; operator: ADD, operand type: MAP". My understanding is that when adding to a set that does not exist, the set will be created. If it already exists, the new value should be added to the set. I do not understand why the set I attempt to ADD is being read as a map.
How users are created:
var params = {
TableName: "users",
Item:{
"id": Number(id),
"name": name,
"password": password
}
};
documentClient.put(params, function(err, data) {
if(err)
res.json(500, err);
else
res.json(200, data);
});
How friends are added:
var params = {
TableName: "users",
Key: {
"id": id
},
UpdateExpression: "ADD friends :friendId",
ExpressionAttributeValues: {
":friendId": { "NS": [friendId] }
},
ReturnValues: "UPDATED_NEW"
};
documentClient.update(params, function(err, data) {
if(err)
res.json(500, err);
else
res.json(200, data);
});
This question has an answer here
https://stackoverflow.com/a/38960676/4975772
Here's the relevant code formatted to fit your question
let AWS = require('aws-sdk');
let docClient = new AWS.DynamoDB.DocumentClient();
...
var params = {
TableName : 'users',
Key: {'id': id},
UpdateExpression : 'ADD #friends :friendId',
ExpressionAttributeNames : {
'#friends' : 'friends'
},
ExpressionAttributeValues : {
':friendId' : docClient.createSet([friendId])
},
ReturnValues: 'UPDATED_NEW'
};
docClient.update(params, callback);
If the set doesn't exist, then that code will create it for you. You can also run that code with a different set to update the set's elements. Super convenient.
Here is the working code. You don't need ADD here. Just use "set friends = :friendId" as friends attribute is not already present in the table (i.e. before the update you have only id, name and password in the table). The friend attribute is being added newly as part of the update.
var docClient = new AWS.DynamoDB.DocumentClient();
var table = "users";
var userid = 1;
var friendId = [123];
var params = {
TableName : table,
Key: {
"id" : userid
},
"UpdateExpression": "set friends = :friendId",
"ExpressionAttributeValues": {
":friendId": {"NS": friendId}
},
"ReturnValues" : "UPDATED_NEW"
};