Dynamodb - Update value of JSON object in array of objects - node.js

Below is a sample item object/record stored in DynamoDb. I use NodeJS and AWS.DynamoDB.DocumentClient to access the database.
I'm building out a PUT function to update the status for an JSON object in an array. The function will have access to the Item's uuid and room's uuid. How can I simply (creatively) update the value of corresponding status field, given the array of JSON objects?
Params:
let params = {
TableName: room-table,
Key: {
uuid: event.body.uuid
},
UpdateExpression : "??",
ExpressionAttributeNames: {
"??":"??"
},
ExpressionAttributeValues:{
"??":"??"
},
ReturnValues:"ALL_NEW"
};
Item Object:
{
"Item": {
"uuid": "77b1e88e-5e60-44d9-b6ca-aec345c0dc99",
"rooms": [
{
"room": "303",
"status": "pending",
"uuid": "b8f1c1a8-04a9-4c2e-82ad-bc3e81face35"
},
{
"room": "302",
"status": "pending",
"uuid": "42fdc61a-4a25-4316-90c9-60209875d208"
},
{
"room": "678",
"status": "pending",
"uuid": "7bedc115-20ed-4c3e-9cd7-7fed0520f4df"
}
],
"status": "pending"
}
}

It's not possible to do this with ExpressionAttributeValues. I had to build a function to modify the object, similar to below:
function setStatus(jsonObj, uuid, newStatus) {
for (var i=0; i<jsonObj.length; i++) {
if (jsonObj[i].uuid === uuid) {
jsonObj[i].status = newStatus;
return jsonObj;
}
}
}

let params = {
TableName: room-table,
Key: {
uuid: event.body.uuid
},
UpdateExpression : "SET #stat = :stat",
ExpressionAttributeNames: {
"#stat": "status"
},
ExpressionAttributeValues:{
":stat": "updatedStatusValue"
},
ReturnValues:"ALL_NEW"
};
ExpressionAttributeNames is needed because status is a DynamoDB reserved word. More info on Attribute Name and Attribute Value placeholders is available in the DynamoDB docs.

Related

Dynamodb DB scan: Filter on embedded object in array

Trying to be able to filter on embedded object that looks like:
"posts": [
{
"id": "10e85cf7-acd2-417b-a5dc-1dfb6de606bf",
"references": [
{
"type": "URL",
"title": "How to get dynamodb to only return certain columns",
},
{
"type": "HASHTAG",
"title": "#dynamodb",
},
]
},
...
]
I am trying to return all posts that have reference of type "HASHTAG" and VALUE "#dynamodb". I have tried this but it always returns null (running in node.js):
const params = {
TableName: "tableName",
ScanIndexForward: false,
FilterExpression: "#references.#type = :referenceValue",
ExpressionAttributeNames: {
"#references": "references",
"#type": "HASHTAG"
},
ExpressionAttributeValues: {
":referenceValue": "#dynamoDB"
}
}
const response = await docClient.scan(params).promise();
console.log(response);
And only returns (successfully) an empty array.
Use the contains function to filter on items in a list. In your case, return posts that have an item like :map at the path indicated by #references.
FilterExpression: 'contains(#references, :map)',
ExpressionAttributeNames: { '#references': 'references' },
ExpressionAttributeValues: {
':map': {
type: 'HASHTAG',
title: '#dynamodb',
},
},

Query a list of objects in dynamodb

I am trying to get a result from my dynamodb database and ran into a problem, I have this data in my orders table.
{
"orders": [
{
"id": "24950-05675",
"orderNumber": null,
"orderProduct": [
{
"id": "28",
"relatedProductsID": null,
"quantity": 1
},
{
"id": "31",
"relatedProductsID": null,
"quantity": 2
},
{
"id": "99",
"relatedProductsID": null,
"quantity": 3
}
]
}
]
}
and I trying to query for the values inside orderProduct attribute as you can see is a list of objects, so am using FilterExpression with contains in my query like this.
productId = {id: '28'}
let attributes = {
country: { String: ':country', Name: 'country', Value: country },
orderProduct: { String: ':orderProduct', Name: 'orderProduct', Value: productId }
};
const { query } = this.generateQueryFull(attributes);
let params = this._createParamObject({
IndexName: 'countryIndex',
KeyConditionExpression: query.string,
ConditionExpression: 'enabled = true',
ExpressionAttributeNames: query.atributeNames,
ExpressionAttributeValues: query.atributeValues,
FilterExpression: 'contains(#orderProduct, :orderProduct)'
});
and running with query like this:
let response = await this.dbService.client
.query(params)
.promise();
The query is generating without problems and the consult is executing correctly.
So the idea is to filter in the list orderProduct by the propertie { id: <string>}, I have done this before without problem with object lists, but when I try this implementation I don't get results but also don't get any error.
I do not know what I can be doing wrong if someone has an idea it would be very helpful.
the final query
query string
{"string":"#country = :country","atributeNames":"#country":"country","#orderProduct":"orderProduct"},"atributeValues":{":country":"co",":orderProduct":{"id":"31"}}}

Find when the keys are unkown in mongodb

How can I getthe data that has email as abc#gmail.com in mongoDB?I don't know the Key Name and I want to iterate through all the data.
I have data like this:
{
"_id":"5c0a1589a5a41b2ae707317b",
"test1":{
"email":"abc#gmail.com",
"phoneNo":"123456897",
"endpointId":"test1"
}
}
{
"_id":"5c0a1989a5a41b2ae807317b",
"test2":{
"email":"abc#gmail.com",
"phoneNo":"123456897",
"endpointId":"test2"
}
}
{
"_id":"5c0a1989a5a41b2ae807317b",
"test2":{
"email":"pqr#gmail.com",
"phoneNo":"123456897",
"endpointId":"test3"
}
}
But the object key is not known at the time of searching. I want to iterate through all the data and get matched data that has specific email.
If I know the key name like test1,test2 etc then I can use find({test1:{...}}) but Here I don't know the key value.
So, how can I do that?
You can use below aggregation using $objectToArray in mongodb 3.4 and above
db.collection.aggregate([
{ "$addFields": {
"field": { "$objectToArray": "$$ROOT" }
}},
{ "$match": { "field.v.email": "abc#gmail.com" }},
{ "$project": { "field": 0 }}
])
I am assuming you get the objects in array type.
I made a method named findObject. This method will take the object array and the desired email.
Finally, return the first object, that matched with the email.
const data = [{
"_id":"5c0a1589a5a41b2ae707317b",
"test1":{
"email": "abc#gmail.com",
"phoneNo": "123456897",
"endpointId":"test1"
}
},
{
"_id":"5c0a1989a5a41b2ae807317b",
"test2":{
"email": "abc#gmail.com",
"phoneNo": "123456897",
"endpointId":"test2"
}
},
{
"_id":"5c0a1989a5a41b2ae807317b",
"test2":{
"email": "pqr#gmail.com",
"phoneNo": "123456897",
"endpointId": "test3"
}
}];
const findObject = (data, email) => {
for (let index=0; index<data.length; index++) {
const currentData = data[index];
for (let property in currentData) {
if (property != '_id' && currentData[property].email == email) {
return currentData;
}
}
}
return null;
}
let desiredObject;
const desiredEmail = 'abc#gmail.com';
desiredObject = findObject(data, desiredEmail);
console.log(desiredObject);
And the output will be
{ _id: '5c0a1589a5a41b2ae707317b',
test1:
{ email: 'abc#gmail.com',
phoneNo: '123456897',
endpointId: 'test1' } }
I think you can't do query on totally unknown field! if you could change your schema see here for more info, also you could write script to migrate to a new DB with new schema:
// new doc instance
{
"_id":"5c0a1589a5a41b2ae707317b",
"obj": {
"name": "test1"
"email":"abc#gmail.com"
"phoneNo":"123456897",
"endpointId":"test1"
}
},
{
"_id":"5c0a1989a5a41b2ae807317b",
"obj": {
"name": "test2"
"email":"abc#gmail.com"
"phoneNo":"123456897",
"endpointId":"test2"
}
},
{
"_id":"5c0a1989a5a41b2ae807317b",
"obj": {
"name": "test3"
"email":"pqr#gmail.com"
"phoneNo":"123456897",
"endpointId":"test3"
}
}
otherwise, check this may works correctly. if all of them is not effective so make a query to get all of your data as an Array and use filter method on it:
Model.find({}, (err, docs) => {
const result = docs.filter((doc) => {
for (key in doc) {
if (doc[key].email === 'abc#gmail.com')
return doc;
}
});
console.log(result);
});

Dynamodb UpdateExpression adding new map object to a list of existing Item

I currently have a List attribute in my dynamodb table:
name: "Test User"
recommendations: []
I would like to add new item to the recommendations attribute using the UpdateExpression
const params = {
TableName: 'insiders',
Key:{
"uuid": event.uuid, // WHERE uuid is event.uuid
},
UpdateExpression: "SET #recommendations = :recommendation",
ExpressionAttributeNames: {
"#recommendations": "recommendations",
},
ExpressionAttributeValues: {
":recommendation": [{
"uuid": `ir_${uuidv4()}`,
"recommendation": event.recommendation
}]
},
ReturnValues:"UPDATE_NEW"
};
dynamodb.update(params, function(err, data) { }
I managed to add an object map to recommendations list but when I want to add another one it will replace the object in the recommendation list.
I also tried to use the ADD in UpdateExpression
const params = {
TableName: 'insiders',
Key:{
"uuid": event.uuid,
},
UpdateExpression: "ADD #recommendations :recommendation",
ExpressionAttributeNames: {
"#recommendations": "recommendations",
},
ExpressionAttributeValues: {
":recommendation": [{
"uuid": `ir_${uuidv4()}`,
"recommendation": event.recommendation,
}]
},
ReturnValues:"ALL_NEW"
};
dynamodb.update(params, function(err, data) { }
but Im getting an error
"Invalid UpdateExpression: Incorrect operand type for operator or function; operator: ADD, operand type: LIST",
Okay I already figured out how to add an object to existing map attribute of an item. I used list_append
this will add an object to existing map attribute
const params = {
TableName: 'insiders',
Key:{
"uuid": event.uuid,
},
UpdateExpression: "SET #attrName = list_append(#attrName, :attrValue)",
ExpressionAttributeNames: {
"#attrName": "recommendations",
},
ExpressionAttributeValues: {
":attrValue": [{
"uuid": `ir_${uuidv4()}`,
"recommendation": event.recommendation
}]
},
ReturnValues:"ALL_NEW"
};

dynamo db error on update expression using node.js

i need to update a list on my aws-dynamo database. i have created a table with partition key : email. then iam insert some email id to the table successfully. Now my table like this
email
manaf1#gmail.com
manaf2#gmail.com
manaf3#gmail.com
Then, i tried to update the table with new key "details" and its value is a list. this is my code
var AWS = require("aws-sdk");
var params =
{
TableName: "manaftable1",
Key: { email: "manaf1#gmail.com" },
UpdateExpression: "set #details = list_append (#details, :detailsinput)",
ExpressionAttributeNames: {
"#details": "details"
},
ExpressionAttributeValues: {
":detailsinput":{ "id": "1","mob": "978956" }
}
};
var docClient = new AWS.DynamoDB.DocumentClient();
docClient.update(params, function (err, data) {
if (err)
console.log(JSON.stringify(err, null, 2));
else
console.log(JSON.stringify(data, null, 2));
});
But i got error like this
{
"message": "Invalid UpdateExpression: Incorrect operand type for operator or function; operator or function: list_append, operand type: M",
"code": "ValidationException",
"time": "2016-10-26T11:04:60.756",
"requestId": "SN0NPRHDFHUKBBJHOVI0DFHHRQNSO5AEMVJFFGHF9ASFHUAAJG",
"statusCode": 400,
"retryable": false,
"retryDelay": 0
}
i need response like this after updation
column1 : email
column2 : details
manaf1#gmail.com |
[{"id":"1","mob":"978956"},{"id":"2","mob":"767886"}]
manaf2#gmail.com |
what is the issue related with my code?
As AWS documentation for list_append says:
The new element must be contained in a list, for example to add 2 to a
list, the operand would be [2]
So you need to append an array of objects, not just an object:
var params =
{
TableName: "manaftable1",
Key: { email: "manaf1#gmail.com" },
UpdateExpression: "set #details = list_append (#details, :detailsinput)",
ExpressionAttributeNames: {
"#details": "details"
},
ExpressionAttributeValues: {
":detailsinput": [{ "id": "1","mob": "978956" }]
}
};
If the property does not exist in the object, you can use if_not_exists operand:
var params =
{
TableName: "manaftable1",
Key: { email: "manaf1#gmail.com" },
UpdateExpression: "set if_not_exists(#details, []) set #details = list_append (#details, :detailsinput)",
ExpressionAttributeNames: {
"#details": "details"
},
ExpressionAttributeValues: {
":detailsinput": [{ "id": "1","mob": "978956" }]
}
};
Use list_append() and if_not_exists() together to append to a potentially non-existent list column:
var params = {
TableName: "manaftable1",
Key: { email: "manaf1#gmail.com" },
UpdateExpression: "set #details = list_append(if_not_exists(#details, :empty_list), :detailsinput)",
ExpressionAttributeNames: {
"#details": "details"
},
ExpressionAttributeValues: {
":detailsinput": [{ "id": "1","mob": "978956" }],
":empty_list": []
}
};
When the details attribute is NOT present in the item, the update expression should have just SET without the list_append.
UpdateExpression : "SET #details = :details"
When the details attribute is present in the item and the new update needs to add additional elements in the list, then list_append can be used to add the new element to the list.
UpdateExpression : "set #details = list_append (#details, :details)"

Resources