Conditional update to dynamoDB with nested object using node.js - javascript

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.

Related

update(add) dynamodb with lambda function

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);
}
});

how to get key value pair from body object in javascript while using request module

I am creating an application in which I want to use some data from a JSON, which is generated by another js file. Here is the code which is generating JSON
var request = require('request');
module.exports = {
foo:
request('https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/c1d1e5d6-fe5c-42de-8713-60f272a3b63e?subscription-key=d3d3e4dfa8744be9b4ae47558df8fc5a&timezoneOffset=0&verbose=true&q=hey',function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log(body);
})
};
I am interested in body object, which is giving following contents -
{
"query": "hey",
"topScoringIntent": {
"intent": "Help",
"score": 0.500165462
},
"intents": [
{
"intent": "Help",
"score": 0.500165462
},
{
"intent": "None",
"score": 0.10364107
},
{
"intent": "SearchHotels",
"score": 0.00249445555
},
{
"intent": "ShowHotelsReviews",
"score": 9.451727E-06
}
],
"entities": []
}
Now I want to access value of intent from topScoringIntent element. That to in another JS file. I tried using body[1].intend but it gives undefined.
I am very new to javascript and need very basic code to do this. Please give some suggestions on this. Also plz tell me if this can be solved by body-parser and if yes then how?
Update - Here is the code where I want to use body['topScoringIntent'].intent as global.
require('dotenv-extended').load();
var builder = require('botbuilder');
var restify = require('restify');
var Store = require('./store');
var spellService = require('./spell-service');
var request = require('request');
var myJSON = require("JSON");
var fs = require('fs');
//var window = window;
var request = require("request");
var myJSON = require("JSON");
var globalVar = [];
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
// Create connector and listen for messages
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
server.post('/api/messages', connector.listen());
function getMyBody(url, callback) {
request({
url: 'https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/c1d1e5d6-fe5c-42de-8713-60f272a3b63e?subscription-key=d3d3e4dfa8744be9b4ae47558df8fc5a&timezoneOffset=0&verbose=true&q=hey',
json: true
}, function (error, response, body) {
if (error || response.statusCode !== 200) {
return callback(error || {statusCode: response.statusCode});
}
global.topScoringIntent = body['topScoringIntent'].intent;
//if(body['topScoringIntent'].intent == 'Help');
//console.log('yay');
callback(null, body);
});
}
getMyBody('https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/c1d1e5d6-fe5c-42de-8713-60f272a3b63e?subscription-key=d3d3e4dfa8744be9b4ae47558df8fc5a&timezoneOffset=0&verbose=true&q=hey', function(err, body) {
if (err) {
console.log(err);
}
})
if(body['topScoringIntent'].intent == 'Help');
console.log('success');
This should work for you
console.log(response.topScoringIntent.intent);
body.topScoringIntent.intent will return 'Help'.
To use it globally you can set a var :
var body = {
"query": "hey",
"topScoringIntent": {
"intent": "Help",
"score": 0.500165462
},
"intents": [
{
"intent": "Help",
"score": 0.500165462
},
{
"intent": "None",
"score": 0.10364107
},
{
"intent": "SearchHotels",
"score": 0.00249445555
},
{
"intent": "ShowHotelsReviews",
"score": 9.451727E-06
}
],
"entities": []
}
var result = body.topScoringIntent.intent;
And then use result somewhere else :
console.log(result);

AWS Node.js JSON put error

I have the following code I am trying to upload to DynamoDB local host using Node.js.
Is there a possible work around. For the following error?
Unable to add event undefined . Error JSON: {
"message": "One of the required keys was not given a value",
"code": "ValidationException",
"time": "2016-06-28T04:02:26.250Z",
"requestId": "970984e4-3546-41f0-95f9-6f1b7167c510",
"statusCode": 400,
"retryable": false,
"retryDelay": 0
}
Here is the code. I would like the Item: {} to accept whatever values may be present, and add them to the table.
var AWS = require("aws-sdk");
var fs = require('fs');
AWS.config.update({
region: "us-west-2",
endpoint: "http://localhost:8000"
});
var docClient = new AWS.DynamoDB.DocumentClient();
console.log("Importing movies into DynamoDB. Please wait.");
var allMovies = JSON.parse(fs.readFileSync('moviedata.json', 'utf8'));
allMovies.forEach(function(movie) {
var params = {
TableName: "Movies",
Item: {
"year": movie.year,
"title": movie.title,
"info": movie.info,
"twitter": movie.twitter
}
};
docClient.put(params, function(err, data) {
if (err) {
console.error("Unable to add movie", movie.title, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("PutItem succeeded:", movie.title);
}
});
});
As you are looping over a promise call, you need a safeguard that the current promise resolves before you begin the next.
var AWS = require("aws-sdk");
var fs = require('fs');
const tableName = 'Movies';
AWS.config.update({
region: "local",
endpoint: "http://localhost:8000"
});
var docClient = new AWS.DynamoDB.DocumentClient();
console.log("Importing movies into DynamoDB. Please wait.");
var allMovies = JSON.parse(fs.readFileSync('moviedata.json', 'utf8'));
for (let i = 0, p = Promise.resolve(); i < allMovies.length; i++) {
p = p.then(_ => new Promise(resolve =>
setTimeout(function () {
var params = {
TableName: tableName,
Item: {
"year": allMovies[i].year,
"title": allMovies[i].title,
"info": allMovies[i].info
}
};
docClient.put(params, function(err, data) {
if (err) {
console.error("Unable to add movie", allMovies[i].title, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("PutItem succeeded:", allMovies[i].title);
}
});
resolve();
}, 10)
));
}

node js + express + jade iterate array

What I'm trying to do is iterate over an array in a jade layout. The layout file is lessons.jade:
each lesson in myLessons
ul.nav.pull-center: li.dropdown.nav.text-center
.btn.btn-default.dropdown-toggle.btn-lg.btn-block(data-toggle="dropdown" aria-expanded="false")= lesson.day
ul.dropdown-menu.col-xs-12
each lessonName in myLessons
li: a(href='/lessons/details')= lessonName.name
li.divider
My view controller file is lessons.js:
var renderLessonPage = function (req, res, responseBody) {
var message;
if (!(responseBody)) {
message = "Lessons API Error!";
} else {
if (responseBody.length < 0) {
message = "No lessons found!";
}
}
res.render('lessons', {
title: 'Lesson page',
pageHeader: {
title: 'Just a page'
},
myLessons: responseBody,
message: message
});
};
module.exports.lessons = function(req, res) {
var requestOptions, path;
path = '/api/locations/' + req.params.locationid + '/lessons/';
requestOptions = {
url: apiOptions.server + path,
method: "GET",
json: {}
};
request(
requestOptions,
function (err, response, body) {
renderLessonPage(req, res , body );
}
);
};
My API controller file contains:
var sendJsonResponse = function (res, status, content) {
res.status(status);
res.json(content);
};
module.exports.lessons = function (req, res) {
loc
.findById(req.params.locationid)
.populate('lessons')
.exec(function (err, location) {
if (!location) {
sendJsonResponse(res, 404, {
"message": "No lessons found!"
});
} else {
response = {
location: {
lessons: location.lessons
// id: req.params.locationid
}
};
sendJsonResponse(res, 200, response);
}
})
};
If I fire up Chrome and browse to the location accessed by the API, I see the following json result:
{
"location": {
"lessons": [
{
"_id": "56d5d947bdb5c3d92ace848c",
"name": "Henk",
"startTime": "13:00",
"endTime": "14:00",
"day": "Tuesday",
"__v": 0
},
{
"_id": "56d5d9dfea5cbcf42a20f87e",
"name": "skaaak",
"startTime": "12:00",
"endTime": "18:00",
"day": "Monday",
"__v": 0
}
]
}
}
If I enable console.log in my jade layout template via - console.log and catch the lesson in myLessons, the exact same thing is outputted in the console. But I just can't use values from the array in my jade layout.
I get one dropdown menu only instead of two, the text isn't populated on the dropdown button, and there are two empty items in the pull down menu.
I tried many things, but most resulted in undefined or properties which couldn't be read.
When you sent result for your request
response = {
location: {
lessons: location.lessons
// id: req.params.locationid
}
};
sendJsonResponse(res, 200, response);
response is object, not array. So when renderLessonPage function render (option myLessons: responseBody) is object.
You can replace your code as below:
response = {
location: {
lessons: location.lessons
// id: req.params.locationid
}
};
sendJsonResponse(res, 200, response.location.lessons);

Elastic search Parser Error

I'm trying to construct the following search call in Javascript with no luck, the below works fine so I know my index is setup correctly in ES.
GET /teachersx9/teacher/_search
{
"query": {
"bool": {
"must": [
{ "match_all": {}},
{
"nested": {
"path": "langs",
"score_mode": "max",
"query": {
"bool": {
"must": [
{ "match": { "languagename": "Afrikaans"}}
]
}}}}
]
}}}
However, when trying to create the query on my server using:
app.get('/search', function(req, res) {
var term = req.query.term;//req.param('term');
var urlPath = 'https:.../teachersx9/teacher/_search';
//var obj = {};
var query = {};
query.bool = {};
var match_all = {};
var nested = {path:"langs", score_mode:"max"};
nested.query = {};
nested.query.bool = {};
nested.query.bool.must = [{match: {languagename:term}}];
query.bool.must = [{match_all:match_all}, {nested:nested}];
console.log(query);
request.post({
url: urlPath,
json: true,
body: query
}, function(error, response, body){
if (!error && response.statusCode == 200) {
console.log(body);
res.send(body);
} else {
console.log(body);
res.send(response);
}
});
});
Error I get back from ES as part of the response:
"statusCode": 400,
"body": {
"error": "SearchPhaseExecutionException[Failed to execute phase [query_fetch], all shards failed; shardFailures {[FMwFQZmkIAfOE7X48Q][teachersx9][0]: RemoteTransportException[[Quentin Quire][inet[/172.31.7.165:9300]][indices:data/read/search[phase/query+fetch]]]; nested: SearchParseException[[teachersx9][0]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\"bool\":{\"must\":[{\"match_all\":{}},{\"nested\":{\"path\":\"langs\",\"score_mode\":\"max\",\"query\":{\"bool\":{\"must\":[{\"match\":{\"languagename\":\"Italian\"}}]}}}}]}}]]]; nested: SearchParseException[[teachersx9][0]: from[-1],size[-1]: Parse Failure [No parser for element [bool]]]; }]",
"status": 400
},
bool isn't a valid top-level construct in the Elastic query DSL. You need to wrap it in query.
var body = { query: query }
request.post({
url: urlPath,
json: true,
body: body
})

Categories

Resources