update(add) dynamodb with lambda function - javascript

I have done a lot of research and the topic does not have enough source for juniors like me. Everything I could find was case specific that was making it impossible to understand. Therefore for myself and for the people who will read this in the future I will not make my question too case specific.
I have created a table record on DynamoDB with the following lambda function:
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({region: 'us-east-2', apiVersion: '2012-08-10'})
exports.handler = (event, context, callback) => {
console.log(event)
const params = {
Item: {
"UserId": {
S: "global"
},
"search": {
SS: [
"" + event.hashtag
]
}
},
TableName: "crypto-app"
};
dynamodb.putItem(params, function(err, data) {
if (err) {
callback(err)
} else {
callback(null, data)
}
});
};
this is creating a simple string set
{
"search": {
"SS": [
"london"
]
},
"UserId": {
"S": "global"
}
}
how can I add more strings to my string set with a lambda function to make it like this?
{
"search": {
"SS": [
"london", "tokyo", "moskow"
]
},
"UserId": {
"S": "global"
}
}

You can update the item and add additional string set values.
Here's how you would do it if you had named the attribute xxx rather than search, which is a reserved word.
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({region: 'us-east-2'});
const params = {
Key: {
UserId: {
S: 'global',
},
},
UpdateExpression: 'ADD xxx :avals',
ExpressionAttributeValues: {
':avals': {
SS: ['tokyo', 'moskow'],
},
},
TableName: 'crypto-app',
};
dynamodb.updateItem(params, (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
However, because you named the attribute search, which is reserved, you need to essentially escape that reserved name using an expression attribute name, which is a placeholder that you use in an expression, as an alternative to an actual attribute name.
Here's an example of how you do that:
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({region: 'us-east-2'});
const params = {
Key: {
UserId: {
S: 'global',
},
},
UpdateExpression: 'ADD #a :avals',
ExpressionAttributeValues: {
':avals': {
SS: ['tokyo', 'moskow'],
},
},
ExpressionAttributeNames: {
'#a': 'search',
},
TableName: 'crypto-app',
};
dynamodb.updateItem(paramse, (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
Another, probably better, way to do this is to use the DynamoDB DocumentClient. It's a higher level client interface and it simplifies working with items by abstracting away the notion of attribute values, and instead using native JavaScript types.
With the DocumentClient, rather than explicitly writing UserId: { 'S': 'global' }, you can simply use UserId: 'global' and the string type ('S') will be inferred.
Here's an example of the item update using DocumentClient:
const AWS = require('aws-sdk');
const dc = new AWS.DynamoDB.DocumentClient({region: 'us-east-2'});
const params = {
Key: {
UserId: 'global',
},
UpdateExpression: 'ADD #a :avals',
ExpressionAttributeValues: {
':avals': dc.createSet(['tokyo', 'moskow']),
},
ExpressionAttributeNames: {
'#a': 'search',
},
TableName: 'crypto-app',
};
dc.update(params, (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});

Related

AWS Lambda NodeJS access result of Dynamo DB query from outer function

I am trying to query Dynomo DB table and I want to go through over the resulting items in a function in my AWS Lambda. I am not able to extract result from Dynamo DB query. It is inside the closure, I am able to console log it, but I am not able to assign it for any variable in the scope of outer function.
What should I do to get it outside?
function check(id) {
//build params
let params = {
TableName: 'demo_table',
KeyConditionExpression: #key =: id,
Limit: 5,
ScanIndexForward: false,
ExpressionAttributeNames: {
#key: process.env.PRIMARYKEY
},
ExpressionAttributeValues: {
: id: id
}
};
//query ddb
let result = {};
ddb.query(params, function(err, data) {
if (err) {
console.log("AN ERROR OCCURED\n");
console.log(err);
} else {
//How to copy the data from here to outside??
//I can console log and see the data
result = data;
}
});
console.log(result); //returns {}
}
const check = async (id) => {
//build params
let params = {
TableName: 'demo_table',
KeyConditionExpression: #key =: id,
Limit: 5,
ScanIndexForward: false,
ExpressionAttributeNames: {
#
key: process.env.PRIMARYKEY
},
ExpressionAttributeValues: {
: id: id
}
};
let result = await new Promise((resolve, rejects) => {
ddb.query(params, function (err, data) {
if (err) rejects(err)
resolve(data)
});
})
console.log(result); //returns {}
}
By using promises you can get the data. database read is an asyncronous operation.

DynamoDB: ValidationException: The provided key element does not match the schema

I'm getting the error ValidationException: The provided key element does not match the schema trying to find the field username in my database, when querying the id manually I get a response but with the username I get the error.
const readKey = async (table, queryData, column) => {
const params = {
TableName: table,
Key: {},
}
// { TableName: 'users', Key: { username: 'Maarten' } }
params.Key[column] = queryData
return await new Promise((res, rej) => {
dynamodb.get(params, (err, data) => {
if (err) {
rej(err)
console.log(err)
}
console.log(data)
res(data)
})
})
}
The response:
message: 'The provided key element does not match the schema',
code: 'ValidationException',
time: 2019-11-24T23:50:37.472Z,
requestId: 'RHQF02LFVE7V3NNSGDBF8JR97RVV4KQNSO5AEMVJF66Q9ASUAAJG',
statusCode: 400,
retryable: false,
retryDelay: 14.742327788838905
when I change the params to this:
const params = {
TableName: table,
Key: {
id: '22601770-37ea-47ce-9814-bd979ca4c841',
},
}
I get the response:
{
Item: {
id: '22601770-37ea-47ce-9814-bd979ca4c841',
password: '$2b$10$f/gXRrSCCALPRedCIxOwVuhzi2EX07DeEDVF4h10UuUN.DgYY2Hnq',
username: 'Maarten'
}
}
What am I doing wrong?

Conditional update to dynamoDB with nested object using node.js

I'm trying to update 3 nested items in a db table,my lambda runs fine and doesnt give any erros,but when a query the table it doesnt show the new values,i´m not sure if im invoking the table or passing the arguments correctly
my partion key/primary key is badgeNumber
my dynamoDB table looks like this:
(the items i'm trying to update are date,hour,register to yyy-mm-dd,hh-mm-ss and true
{
"assistance": [
{
"date": "null",
"hour": "null",
"register": false
}
],
"badgeNumber": "0000",
"data": {
"cardType": "elem",
"firstName": "Moriuks",
"imageURL": "url",
"lastName": "Mora",
"position": "Student"
}
}
the condition to update the items is if register is = false then write the new values to the table.
my code looks like this
pppp
var updateAsisstance = function(day,hour,id){
var docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName:"someTable",
Key: { badgeNumber : 0000 },
UpdateExpression: "SET #asi[0].#reg = :locVal",
ExpressionAttributeNames: {
'#asi': 'asisstance',
'#reg': 'register',
},
ConditionExpression: "NE(#asi[0].#reg:true)",
ExpressionAttributeValues:{
":date":day,
":hour":hour,
":locVal":true
},
ReturnValues:"UPDATED_NEW"
};
docClient.update(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
};
after defining the funcion,im calling it using
updateAssistance(day,hour,id)
the expected output should look something like this:
"assistance": [
{
"date": "yyyy-MM-DD",
"hour": "HH-MM-SS",
"register": true
}
],
i solved it changing the code,also,my condition expression was wrong...here is what it looks like.
'use strict';
const AWS = require('aws-sdk');
var today = new Date();
var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
var hour = (today.getHours()-5) + ":" + today.getMinutes() + ":" + today.getSeconds();
exports.handler = async (event,context) => {
const documentClient = new AWS.DynamoDB.DocumentClient();
let responseBody = "";
let statusCode = 0;
var params = {
TableName:"SomeTable",
Key: { badgeNumber : '0000' },
UpdateExpression: "set assistance[0].register = :n,assistencia[0].date = :date,assistencia[0].hour = :hour",
ExpressionAttributeNames: {
'#asi': 'assistance',
'#reg': 'register'
},
ConditionExpression: "(#asi[0].#reg = :p)",
ExpressionAttributeValues:{
":n":true,
":p":false,
":date":date,
":hour":hour
},
ReturnValues:"UPDATED_NEW"
}
try {
const data = await documentClient.update(params).promise();
responseBody = JSON.stringify(data);
statusCode = 204;
} catch (err) {
responseBody = `Unable to update product: ${err}`;
statusCode = 403;
}
const response = {
statusCode: statusCode,
headers: {
"Content-Type": "application/json"
},
body:responseBody
}
return response
}
this changes the nested values inside my dynamoDB table,if you dont have any its pretty straight forward.

remove a list element in dynamodb with the index being a value stored in a variable

I'm trying to remove an item from the list. The update expression works when I hard code the index of the item (i.e. REMOVE relatedUsers[0]) but I have the index found in a variable. So I tried using ExpressionAttributeValues to replace the variable in the update expression but I'm getting the error 'Invalid UpdateExpression: Syntax error; token: \":userIndex\", near: \"[:userIndex]\"'
Here is my code
function updateUser(data) {
console.log('---------updateUser---------');
console.log(data);
const params = {
TableName: process.env.USER_TABLE,
Key: {
id: data.id,
},
ExpressionAttributeValues: {
':updatedAt': timestamp,
':notificationCount':1,
':userIndex':data.index,
},
UpdateExpression: 'ADD notificationCount :notificationCount REMOVE relatedUsers[:userIndex] SET updatedAt= :updatedAt ',
ReturnValues: 'ALL_NEW',
};
return new Promise((resolve, reject)=>{
dynamodb.update(params, (error,data) => {
// handle potential errors
if (error) {
reject(error);
}
else {
console.log("update consultant response");
console.log(data);
resolve(data);
}
});
});
}
I also tried with ExpressionAttributeNames
function updateUser(data) {
console.log('---------updateUser---------');
console.log(data);
let relatedUser = 'relatedUsers[' + data.index + ']'
const params = {
TableName: process.env.USER_TABLE,
Key: {
id: data.id,
},
ExpressionAttributeNames: {
'#user':relatedUser
},
ExpressionAttributeValues: {
':updatedAt': timestamp,
':notificationCount':1,
},
UpdateExpression: 'ADD notificationCount :notificationCount REMOVE #user SET updatedAt= :updatedAt ',
ReturnValues: 'ALL_NEW',
};
But it didn't update anything in db.
Can you help me resolve this situation?
Effectively you are just building a query string, so try using a literal instead:
function updateUser(data) {
console.log('---------updateUser---------');
console.log(data);
const params = {
TableName: process.env.USER_TABLE,
Key: {
id: data.id,
},
ExpressionAttributeValues: {
':updatedAt': timestamp,
':notificationCount':1
},
UpdateExpression: "ADD notificationCount :notificationCount REMOVE relatedUsers[" + data.index + "] SET updatedAt= :updatedAt",
ReturnValues: 'ALL_NEW',
};
return new Promise((resolve, reject)=>{
dynamodb.update(params, (error,data) => {
// handle potential errors
if (error) {
reject(error);
}
else {
console.log("update consultant response");
console.log(data);
resolve(data);
}
});
});
}

Using async.js for deep populating sails.js

I have a big issue with my function in sails.js (v12). I'm trying to get all userDetail using async (v2.3) for deep populating my user info:
UserController.js:
userDetail: function (req, res) {
var currentUserID = authToken.getUserIDFromToken(req);
async.auto({
//Find the User
user: function (cb) {
User
.findOne({ id: req.params.id })
.populate('userFollowing')
.populate('userFollower')
.populate('trips', { sort: 'createdAt DESC' })
.exec(function (err, foundedUser) {
if (err) {
return res.negotiate(err);
}
if (!foundedUser) {
return res.badRequest();
}
// console.log('foundedUser :', foundedUser);
cb(null, foundedUser);
});
},
//Find me
me: function (cb) {
User
.findOne({ id: currentUserID })
.populate('myLikedTrips')
.populate('userFollowing')
.exec(function (err, user) {
var likedTripIDs = _.pluck(user.myLikedTrips, 'id');
var followingUserIDs = _.pluck(user.userFollowing, 'id');
cb(null, { likedTripIDs, followingUserIDs });
});
},
populatedTrip: ['user', function (results, cb) {
Trip.find({ id: _.pluck(results.user.trips, 'id') })
.populate('comments')
.populate('likes')
.exec(function (err, tripsResults) {
if (err) {
return res.negotiate(err);
}
if (!tripsResults) {
return res.badRequest();
}
cb(null, _.indexBy(tripsResults, 'id'));
});
}],
isLiked: ['populatedTrip', 'me', 'user', function (results, cb) {
var me = results.me;
async.map(results.user.trips, function (trip, callback) {
trip = results.populatedTrip[trip.id];
if (_.contains(me.likedTripIDs, trip.id)) {
trip.hasLiked = true;
} else {
trip.hasLiked = false;
}
callback(null, trip);
}, function (err, isLikedTrip) {
if (err) {
return res.negotiate(err);
}
cb(null, isLikedTrip);
});
}]
},
function finish(err, data) {
if (err) {
console.log('err = ', err);
return res.serverError(err);
}
var userFinal = data.user;
//userFinal.trips = data.isLiked;
userFinal.trips = "test";
return res.json(userFinal);
}
);
},
I tried almost everthing to get this fix but nothing is working...
I am able to get my array of trips(data.isLiked) but I couldn't get my userFInal trips.
I try to set string value on the userFinal.trips:
JSON response
{
"trips": [], // <-- my pb is here !!
"userFollower": [
{
"user": "5777fce1eeef472a1d69bafb",
"follower": "57e44a8997974abc646b29ca",
"id": "57efa5cf605b94666aca0f11"
}
],
"userFollowing": [
{
"user": "57e44a8997974abc646b29ca",
"follower": "5777fce1eeef472a1d69bafb",
"id": "5882099b9c0c9543706d74f6"
}
],
"email": "test2#test.com",
"userName": "dany",
"isPrivate": false,
"bio": "Hello",
"id": "5777fce1eeef472a1d69bafb"
}
Question
How should I do to get my array of trips (isLiked) paste to my user trips array?
Why my results is not what I'm expecting to have?
Thank you for your answers.
Use .toJSON() before overwriting any association in model.
Otherwise default toJSON implementation overrides any changes made to model associated data.
var userFinal = data.user.toJSON(); // Use of toJSON
userFinal.trips = data.isLiked;
return res.json(userFinal);
On another note, use JS .map or _.map in place of async.map as there is not asynchronous operation in inside function. Otherwise you may face RangeError: Maximum call stack size exceeded issue.
Also, it might be better to return any response from final callback only. (Remove res.negotiate, res.badRequest from async.auto's first argument). It allows to make response method terminal

Categories

Resources