DynamoDB : SET list_append not working using aws sdk - node.js

I need to append a string to a string set in a dynamodb table using the corresponding key. This is the Update expression I use to do updateItem :
var params = {
"TableName" : tableName,
"Key": {
"ID": {
S: "20000"
}
},
"UpdateExpression" : "SET #attrName = list_append(#attrName, :attrValue)",
"ExpressionAttributeNames" : {
"#attrName" : "entries"
},
"ExpressionAttributeValues" : {
":attrValue" : {"SS":["000989"]}
} };
This works when I do updateItem() using the aws cli. But when using aws-sdk in nodejs, I am getting the error:
Invalid UpdateExpression: Incorrect operand type for operator or function; operator or function: list_append, operand type: M\n
Any help?
Thanks

list_append can be read as a "concatenate" operation. You just give it two lists.
"UpdateExpression" : "SET #attrName = list_append(#attrName, :attrValue)",
"ExpressionAttributeNames" : {
"#attrName" : "entries"
},
"ExpressionAttributeValues" : {
":attrValue" : ["000989"]
}
It's worth remembering that lists (and maps) in DynamoDB are not typed and can store arbitrary data.
Side note: Armed with this knowledge, the documentation on appending to the beginning of the list now makes sense:
list_append (operand, operand)
This function evaluates to a list
with a new element added to it. You can append the new element to the
start or the end of the list by reversing the order of the operands.

There's an accepted answer on this question which helped me with part of this issue. However, we'll typically want to update lists with additional objects, not strings. For this, I found it useful to avoid using ExpressionAttributeNames if possible.
1) Make sure the value in your item in your DynamoDB table is a list.
2) Make sure you pass in a list of objects (even if you only have one), not a simple object
UpdateExpression: "set pObj.cObj= list_append(pObj.cObj, :obj)",
ExpressionAttributeValues: {
":obj": [
{myObject: {
property1: '',
property2: '',
property3: '',
}}
]
},

I thought I'd just throw this out there as another option for adding or appending an "object" to a list. It's a map being added an item to the list, and worked well for me:
var upsertExpr = (obj.comments == undefined) ? " :attrValue" : "list_append(#attrName, :attrValue)";
var params = {
TableName: 'tableName',
Key: {
'id': {'S': id},
},
UpdateExpression : "SET #attrName = " + upsertExpr,
ExpressionAttributeNames : {
"#attrName" : "comments"
},
ExpressionAttributeValues : {
":attrValue" : {
"L": [
{ "M" :
{
"comment": {"S": comment},
"vote": {"N": vote.toString()}
}
}
]
}
}
};

maybe this will help someone. i was struggling with updating a list and was getting the same error message as the original poster. i managed to solve my problem when i finally understood the documentation (see the Adding Elements To a List example here http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.ADD)
points to note are: 1) that the "list_append takes two lists as input, and appends the second list to the first." and 2) that ExpressionAttributeValues is a list! like this:
{
":vals": {
"L": [
{ "S": "Screwdriver" },
{"S": "Hacksaw" }
]
}
}
good luck!

Related

Fetched sorted API data(NodeJs &Mongoose) not getting displayed in sorted order when try display in Angular UI

I have tried to get sorted in backend & tested via postman and I am getting sorted order.
const locationInfo = await locationDetails.find(query).sort({sectionName:1});
res.json(locationInfo);
[
{ //some other keys &values
"sectionName": "Closet",
},
{
"sectionName": "Dining",
},
{
"sectionName": "Kitchen",
},
{
"sectionName": "Other",
},
{
"sectionName": "Refrigerator",
}
]
After REST call storing result to,
this.result=data;
but when I try to display the same resultant data on UI, Its not getting displayed in sorted order as well as checked in console also resultant data order got changed.
Console Data
[{
sectionName: "Refrigerator",
},
{
sectionName: "Kitchen",
},
{
sectionName: "Dining",
},
{
sectionName: "Closet",
},
{
sectionName: "Other",
}]
Note: Tried to sort from .ts file also but it is not working.
this.result.sort(function(a,b){a.sectionName-b.sectionName});
If any help would be appreciated. Thanks!
SectioName is not a valid criterion for MongoDB to sort the return result. In this case, MongoDB does not know how to sort it.
Here is an example directly from the MongoDB documentation about cursor.sort():
db.restaurants.insertMany( [
{ "_id" : 1, "name" : "Central Park Cafe", "borough" : "Manhattan"},
{ "_id" : 2, "name" : "Rock A Feller Bar and Grill", "borough" : "Queens"},
{ "_id" : 3, "name" : "Empire State Pub", "borough" : "Brooklyn"},
{ "_id" : 4, "name" : "Stan's Pizzaria", "borough" : "Manhattan"},
{ "_id" : 5, "name" : "Jane's Deli", "borough" : "Brooklyn"},
] );
# The following command uses the sort() method to sort on the borough field:
db.restaurants.find().sort( { "borough": 1 } )
Documents are returned in alphabetical order by borough, but the order of those documents with duplicate values for borough might not be the same across multiple executions.
.sort works best with numerical values. If you are in control of the backend and are able to change how data is stored in the database. I suggest you create a field for the creation date or just an index to indicate the order of the items.
Let's say your document looks something like this:
# Doc 1
{
sectionName: "Refrigerator",
order:1
}
# Doc 2
{
sectionName: "Refrigerator",
order:2
}
Then you can do
const locationInfo = await locationDetails.find(query).sort({order:1});
which will return you the documents sorted using the order field, and the order will be consistent.

Node.js querying MongoDB to return all matches that begin with any value from array

I'm trying to query MongoDB via a Node.js application to return all matches that begin with any of the entries in the following array: ['p','q','r','s','t']
I know how to do a "begins with" for single values:
var value = 't';
genericCollection.find({ 'key' : new RegExp('^' + value, 'i')}).toArray(function (err, items) {
res.send(items);
});
Returns:
[
{
"_id": "59788e1a1b4a3901c7fd5501",
"key": "t-key",
"value": "t-value",
"lastModified": "2018-11-27T12:18:30.029Z"
}
]
And I know how to do a "matches any of these values" query:
var values = ['p-key', 't-key'];
genericCollection.find({ 'key' : { $in : values}}).toArray(function (err, items) {
res.send(items);
});
Returns:
[
{
"_id": "59788e1a1b4a3901c7fd5501",
"key": "t-key",
"value": "t-value",
"lastModified": "2018-11-27T12:18:30.029Z"
},
{
"_id": "5978ba139553e32697564d7e",
"key": "p-key",
"value": "p-value",
"lastModified": "2018-11-27T12:18:55.966Z"
}
]
But how can I combine the two so that I get all results from the DB that begin with any of those array values?
You may build an alternation based pattern from the multicharacter strings bearing in mind the necessary grouping construct (so that ^ could be applied to all of the alternatives):
new RegExp('^(?:' + values.join('|') + ')', 'i')
Note that in case the values contain special regex metacharacters you need to escape them properly:
new RegExp('^(?:' + values.map(function(x) {return x.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');}).join('|') + ')', 'i')
The regex will look like
^(?:t1|p2|mN)
Details
^ - start of string
(?:t1|p2|mN) - a non-capturing group matching any of the alternatives: t1, p2, mN.
You can probably use a chatacter class of [pqrst]
genericCollection.find({ 'key' : new RegExp('^[pqrst]', 'i')}).toArray(function (err, items) {
res.send(items);
});

Mongo text search with AND operation for multiple words partially enered

{
TypeList" : [
{
"TypeName" : "Carrier"
},
{
"TypeName" : "Not a Channel Member"
},
{
"TypeName" : "Service Provider"
}
]
}
Question :
db.supplies.find("text", {search:"\"chann\" \"mem\""})
For above query I want display :
{
TypeName" : "Not a Channel Member"
}
But I am unable to get my result.
What are changes I have to do in query .
Please help me.
The below query will return your desired result.
db.supplies.aggregate([
{$unwind:"$TypeList"},
{$match:{"TypeList.TypeName":{$regex:/.*chann.*mem.*/,$options:"i"}}},
{$project:{_id:0, TypeName:"$TypeList.TypeName"}}
])
If you can accept to get an output like this:
{
"TypeList" : [
{
"TypeName" : "Not a Channel Member"
}
]
}
then you can get around using the aggregation framework which generally helps performance by running the following query:
db.supplies.find(
{
"TypeList.TypeName": /chann.*mem/i
},
{ // project the list in the following way
"_id": 0, // do not include the "_id" field in the output
"TypeList": { // only include the items from the TypeList array...
$elemMatch: { //... where
"TypeName": /chann.*mem/i // the "TypeName" field matches the regular expression
}
}
})
Also see this link: Retrieve only the queried element in an object array in MongoDB collection

update array in mongoose which matches the condition

my schema looks like
{
qty:{
property1:{
//something
}
property2:[{
size:40,
color:"black",
enabled:"true"
}]
}
}
property 2 is array what i want to do is update those array object whose enabled is true in single query
I tried writing the following query
db.col.update({
"qty.property2.enabled" = "true"
}, {
"qty.property2.color" = "green"
}, callback)
but it is not working
error:
[main] Error: can't have . in field names [qty.pro.size]
db.col.update({"qty.property2.enabled":"true"},{$set: {'qty.property2.$.color': 'green'}}, {multi: true})
this is the way to update element inside array.
equal sign '=' cannot be used inside object
updating array is done using $
Alternative solution for multiple conditions:
db.foo.update({
_id:"i1",
replies: { $elemMatch:{
_id: "s2",
update_password: "abc"
}}
},
{
"$set" : {"replies.$.text" : "blah"}
}
);
Why
So I was looking for similar solution as this question, but in my case I needed array element to match multiple conditions and using currently provided answers resulted in changes to wrong fields.
If you need to match multiple fields, for example let say we have element like this:
{
"_id" : ObjectId("i1"),
"replies": [
{
"_id" : ObjectId("s1"),
"update_password": "abc",
"text": "some stuff"
},
{
"_id" : ObjectId("s2"),
"update_password": "abc",
"text": "some stuff"
}
]
}
Trying to do update by
db.foo.update({
_id:"i1",
"replies._id":"s2",
"replies.update_password": "abc"
},
{
"$set" : {"replies.$.text" : "blah"}
}
);
Would result in updating to field that only matches one condition, for example it would update s1 because it matches update_password condition, which is clearly wrong. I might have did something wrong, but $elemMatch solution solved any problems like that.
Suppose your documet looks like this.
{
"_id" : ObjectId("4f9808648859c65d"),
"array" : [
{"text" : "foo", "value" : 11},
{"text" : "foo", "value" : 22},
{"text" : "foobar", "value" : 33}
]
}
then your query will be
db.foo.update({"array.value" : 22}, {"$set" : {"array.$.text" : "blah"}})
where first curly brackets represents query criteria and second one sets the new value.

Updating a Set in Dynamo db using Node Js

I am trying to do one of the most simple operations "Update" in a dynamo db list.
Table Schema -
businessId : String, customers: StringSet, itemCode : NumberSet
I have an entry inserted via put -
bussinessId = "sampleBusiness", cuatomers 0: "cust1", itemCode 0: 4554
I want to add more items using update and here is what I have tried -
var updateRequest = {
'TableName' : tableName,
'Key' : {
'businessId' : {
"S" : businessId
}
},
'UpdateExpression' : "SET itemCode[2] =:attrValue",
'ExpressionAttributeValues' : {
':attrValue' : {
"N" : "564564"
}
}
};
This gives me error -
Document Path provided in document is invalid
I wanted to append new entries so tried this as well -
var sm = [];
sm[0] = "56465";
//Add business to
var updateRequest = {
'TableName' : tableName,
'Key' : {
'businessId' : {
"S" : businessId
}
},
'UpdateExpression' : 'SET #attrName = list_append(#attrName, :attrValue)',
'ExpressionAttributeNames' : {
'#attrName' : 'itemCode'
},
'ExpressionAttributeValues' : {
':attrValue' : {
"NS" : sm
}
}
};
This gives:
ValidationException: Invalid UpdateExpression: Incorrect operand type for operator or function; operator or function: list_append, operand type: NS
Also attempted this -
':attrValue' : {
"N" : "4564"
}
But same error.
As per the example provided in http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.Modifying.html , it adds a new element to the FiveStar review list. The expression attribute name #pr is ProductReviews; the attribute value :r is a one-element list. If the list previously had two elements, [0] and [1], then the new element will be [2].
SET #pr.FiveStar = list_append(#pr.FiveStar, :r)
which Says :r is one element list
I am missing some thing here. Request if any one can help. Struck on this for long time. I just want to append elements in set in dynamo db using nodeJS.
It looks like this:
'ExpressionAttributeValues' : {
':attrValue' : {
"NS" : sm
}
}
Should be this:
'ExpressionAttributeValues' : {
':attrValue' : {
"S" : sm
}
}
Or you need to cast this value sm[0] = "56465"; to a Number Number("56465") and change the :attrValue data type "S" to "N". Depends on how you have your table configured.
It's possible too that you should assign :attrValue to be "S" : sm[0] because right now you are passing an "S" a whole array.
I got a proper solution
var item = {"endTime": "7pm", "imageName": "7abcd", "startTime": "7pm"};
dynamo.updateItem({
TableName:'TableName',
Key:{"BucketName":"abcdefg" },
UpdateExpression : "SET #attrName = list_append(#attrName, :attrValue)",
ExpressionAttributeNames : {
"#attrName" : "ImageLists"
},
ExpressionAttributeValues : {
':attrValue' : [item]
}
},function(err, data) {
if (err)
console.log(err);
else
console.log(data)
});

Resources