Updating a Set in Dynamo db using Node Js - 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)
});

Related

json file in terraform

I have a JSON file with the following and trying to list the shapes ex: "t3-nano, t3-micro, t3-small, t2.medium, t3-2xlarge, r6g-medium".
json file = info.json
{
"t3-nano" : {
"service_name" : "t3",
"existing" : 100
},
"t3-micro" : {
"service_name" : "t3",
"existing" : 1
},
"t3-small" : {
"service_name" : "t3",
"existing" : 2
},
"t2.medium" : {
"service_name" : "t2",
"existing" : 0
},
"t3-2xlarge" : {
"service_name" : "t3-2",
"existing" : 5
},
"r6g-medium" : {
"service_name" : "r6g.medium",
"existing" : 10
}
}
I tried the following
locals {
service_name = flatten([for i in local.info : i[*].service_name])
shapes = flatten([for i in local.info : i[*].index])
}
and it got failed
Error: Unsupported attribute
This object does not have an attribute named "index".
I was expecting to print shapes = [t3-nano, t3-micro, t3-small, t2.medium, t3-2xlarge, r6g-medium]. Can someone help if there is a way to just list the shapes?
The flatten function and for expression are both unnecessary here. The function keys already has the functionality and return value that you want to achieve:
shapes = keys(local.info)
and that will assign the requested value.

DynamoDB : SET list_append not working using aws sdk

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!

Formatting the returned object from MongoDB/Mongoose group by

I have a MongoDB with documents of the form:
{
...
"template" : "templates/Template1.html",
...
}
where template is either "templates/Template1.html", "templates/Template2.html" or "templates/Template3.html".
I'm using this query to group by template and count how many times each template is used:
var group = {
key:{'template':1},
reduce: function(curr, result){ result.count++ },
initial: { count: 0 }
};
messageModel.collection.group(group.key, null, group.initial, group.reduce, null, true, cb);
I'm getting back the correct result, but it's formatted like this:
{
"0" : {
"template" : "templates/Template1.html",
"count" : 2 },
"1" : {
"template" : "templates/Template2.html",
"count" : 2 },
"2" : {
"template" : "templates/Template3.html",
"count" : 1 }
}
I was wondering if it's possible to change the query so that it returns something like:
{
"templates/Template1.html" : { "count" : 2 },
"templates/Template2.html" : { "count" : 2 },
"templates/Template3.html" : { "count" : 1 }
}
or even:
{
"templates/Template1.html" : 2 ,
"templates/Template2.html" : 2 ,
"templates/Template3.html" : 1
}
I would rather change the query and not parse the returned object from the original query.
As mentioned by Blakes Seven in the comments you could use aggregate() instead of group() to achieve nearly your desired result.
messageModel.collection.aggregate([
{ // Group the collection by `template` and count the occurrences
$group: {
_id: "$template",
count: { $sum: 1 }
}
},
{ // Format the output
$project: {
_id: 0,
template: "$_id",
count: 1
}
},
{ // Sort the formatted output
$sort: { template: 1 }
}
]);
The output would look like this:
[
{
"template" : "templates/Template1.html",
"count" : 2 },
{
"template" : "templates/Template2.html",
"count" : 2 },
{
"template" : "templates/Template3.html",
"count" : 1 }
}
]
Again, as stated by Blakes in the comments the database can only output an array of objects rather than a solitary object. That would be a transformation that you would need to do outside of the database.
I think it deserves to be restated that this transformation produces an anti-pattern and should be avoided. An object key name provides the context or description for the value. Using a file location as a key name would be a fairly vague description whereas 'template' provides a bit more information about what that value represents.

MongoDB geospatial index, how to use it with array elements?

I would like to get Kevin pub spots near a given position. Here is the userSpots collection :
{ user:'Kevin',
spots:[
{ name:'a',
type:'pub',
location:[x,y]
},
{ name:'b',
type:'gym',
location:[v,w]
}
]
},
{ user:'Layla',
spots:[
...
]
}
Here is what I tried :
db.userSpots.findOne(
{ user: 'Kevin',
spots: {
$elemMatch: {
location:{ $nearSphere: [lng,lat], $maxDistance: d},
type: 'pub'
}
}
},
},
function(err){...}
)
I get a strange error. Mongo tells me there is no index :2d in the location field. But when I check with db.userSpots.getIndexes(), the 2d index is there. Why doesn't mongodb see the index ? Is there something I am doing wrong ?
MongoError: can't find special index: 2d for : { spots: { $elemMatch: { type:'pub',location:{ $nearSphere: [lng,lat], $maxDistance: d}}}, user:'Kevin'}
db.userSpots.getIndexes() output :
{
"0" : {
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "mydb.userSpots",
"name" : "_id_"
},
"1" : {
"v" : 1,
"key" : {
"spots.location" : "2d"
},
"ns" : "mydb.usersBoxes",
"name" : "spots.location_2d",
"background" : true,
"safe" : null
}
}
For a similar geospatial app, I transformed the location into GeoJSON:
{
"_id" : ObjectId("5252cbdd9520b8b18ee4b1c3"),
"name" : "Seattle - Downtown",
"location" : {
"type" : "Point",
"coordinates" : [
-122.33145,
47.60789
]
}
}
(the coordinates are in longitude / latitude format. Mongo's use of GeoJSON is described here.).
The index is created using:
db.userSpots.ensureIndex({"location": "2dsphere"})
In my aggregation pipeline, I find matches using:
{"$match":{"location":{"$geoWithin": {"$centerSphere":[[location.coordinates[0], location.coordinates[1]], radius/3959]}}}}
(where radius is measured in miles - the magic number is used to convert to radians).
To index documents containing array of geo data MongoDB uses multi-key index. Multi-key index unwinds document to some documents with single value instead of array before indexing. So the index consider that key field as single value field not array.
Try query it without $elemMatch operator.

Mongoose embedded document query returning null

I have the following schema :
_schema : {
Prize : new Schema({
prizeName : { type : String },
thumbnailImage : [ String ],
detailImage : [ String ],
prizeCategory : [ {type : String, index : true } ],
prizeDescription : { type : String },
prizePrice : { type : Number, required : true }
}),
Game : new Schema ({
roomName : { type : String, required : true },
openTime : { type : Date },
closeTime : { type : Date },
minPlayers : { type : Number },
maxPlayers : { type : Number, required : true },
numberOfPlayers : { type : Number },
winner : { userId : { type : ObjectId, index : true, ref : 'User'} },
prize : [ this.Prize ],
tag : [ { type : String, index : true } ],
status : { type : Number, index : true },
businessType : { type : Number, required : true, index : true },
mallId : { type : ObjectId, ref : 'Mall' },
registeredPlayers : { type : ObjectId, ref : 'User' }
}),
Schedule : new Schema ({
_id : ObjectId,
time : { type : Date, index : true },
game : [ this.Game ]
}),
}
However when I try to query the game embedded document the object is always null. I'm querying like so:
var Schedule = mongoose.model('Schedule', this._schema.Schedule);
Schedule.findById({'game._id' : req.params._id}).exec(function(err,gameDetail){...});
Am I doing anything wrong when I declare the schema and models? I have seen numerous examples where people appear to be doing exactly what I'm trying. Any help would be greatly appreciated! Thanks in advance.
A mongoose Model's findById method is used to find the instance of that Model with the _id that's supplied as the first parameter to the method. So Schedule.findById returns Schedule instances, not individual Game instances. Schedule.findOne({'game._id' : req.params._id}, ... will get you the Schedule instance containing the Game with that id, but if you need to query for Game instances by id, you should be keeping them in a separate collection instead of embedding them in Schedule.
Looking at your code, my first guess is actually that your findById isn't structured quite right.
First, you're using the value req.params._id to get the id. Most of the code examples I have seen, and my code, uses :id in the router (app.get('/schedules/:id')), which would actually mean the ID you're looking for is stored in req.params.id. You'll have to check that in your code.
Secondly, to my understanding, findById is only useful for finding, in this case, a Schedule by that ID. Try using just a normal find.
Last thought: you're missing a closing curly bracket at the end of {'game._id' : req.params._id}.
Hopefully something in that helps. :)

Resources