Sails JS - Nested .query method doesn't run sequence - javascript

I beginner in Sails JS. I try to make multiple .query("SELECT ... "). I have a problem when looks like it not run sequence. I will explain my problem, look to My snippet code :
var isSuccess = true;
for(var loc = 0 ; loc < decode.length ; loc++){
Location.query('SELECT * FROM abc', function (err, res){
if (err) {
isSuccess = false;
return res.json(200, {
data: {
},
status: {
id: 0,
message: err.message
}
});
} else {
var validate = res.rows;
sails.log(validate);
secondQuery.query('SELECT * FROM def', function (err, seconResult) {
if (err) {
isSuccess = false;
sails.log("Update is Success : "+isSuccess);
return res.json(200, {
data: {
},
status: {
id: 0,
message: err.message
}
});
} else {
}
});
}
});
if(isSuccess){
return res.json(200, {
data: {
},
status: {
id: 1,
message: "Successful",
isSuccess: isSuccess
}
});
}
}
When i request the API via POST method and the result is :
On Console Node JS Server :
Update is Success : false
So it means the request is failed and must be return status = 0 in postman
But in the Postman, it show :
data: {
},
status: {
id: 1,
message: "Successful",
isSuccess: true
}
My Question :
Can anyone help me to explain why and how to make it a sequence process, not like "multi thread" ? (In my case, i need to use RAW query cause i will face with really complex query)
Thank you.

A for loop is synchronous while Location.query() method is not.
So if you want to do it in sequence, you'll have to use Bluebird Promise.
Example (not tested) :
const Bluebird = require('bluebird');
const promises = [];
for(let loc = 0 ; loc < decode.length ; loc++) {
promises.push(Location.query('SELECT ...'));
}
Bluebird.each(promises, (result) => res.json(200, {/* your payload data */}))
.catch((err) => res.json(200, {/* your error data */}));
NB : Be careful when you use res variable in your callbacks when you use it in Sails controllers.

Related

nodejs mongoDB findOneAndUpdate(); returns true even after database is updated

i am working on an Ionic-1 + nodejs + angular application. My mongoDb findOneAndUpdate() function returns true on each call even the first call updates database.
nodejs:
app.post('/booking', function (req, res) {
var collection = req.db.get('restaurant');
var id = req.body.id;
var status = req.body.status;
collection.findOneAndUpdate({status: status, id: id},{$set:{status:"booked"}}, function (e, doc) {
console.log(id, status);
if (e) {
console.log(e);
}
else if(!doc) {
res.send(false);
}
else {
res.send(true);
}
});
});
controller.js
$scope.bookMe = function(id){
var Obj = {status: "yes", id: id};
myService.booking(Obj).success(function(res){
console.log(Obj, "Checking status")
console.log(res);
if (res == true) {
var alertPopup = $ionicPopup.alert({
title: 'Booking Confirm',
template: 'Thanks For Booking'
});
}
else{
var alertPopup = $ionicPopup.alert({
title: 'Error',
template: ' Not available'
});
}
})
};
where i am doing wrong. my DB gets updated but it returns true always on next call.
The documentation about findOneAndUpdate says :
Finds a matching document, updates it according to the update arg, passing any options, and returns the found document (if any) to the callback. The query executes immediately if callback is passed.
So it's regular behavior you got a doc.
Note:
Since you are checking availability status="yes", Better hard code, instead of getting it from request query/data.
Change the response according to your requirement res.send(true)/ res.send(false).
Following code will work
app.post('/booking', function (req, res) {
var collection = req.db.get('restaurant');
collection.findOneAndUpdate({
status: "yes",
_id: req.body.id
}, {
$set: {
status: "booked"
}
}, function (err, result) {
//Error handling
if (err) {
return res.status(500).send('Something broke!');
}
//Send response based on the required
if (result.hasOwnProperty("value") &&
result.value !== null) {
res.send(true);
} else {
res.send(false);
}
});
});

Getting records from DynamoDB recursively using Q.Promises

I am having trouble implementing Q promises with recursive dynamodb call, new to nodejs and q, considering the limitations of the dynamodb to retrieve results, we need to run recursive query to get the required results.
normally we use the query with Q implementation something like this as
function getDBResults(){
var q = Q.defer();
var params = {
TableName: mytable,
IndexName: 'mytable-index',
KeyConditionExpression: 'id = :id',
FilterExpression: 'deliveryTime between :startTime and :endTime',
ExpressionAttributeValues: {
':startTime': {
N: startTime.toString()
},
":endTime": {
N: endTime.toString()
},
":id": {
S: id.toString()
}
},
Select: 'ALL_ATTRIBUTES',
ScanIndexForward: false,
};
dynamodb.query(params, function(err, data) {
if (err) {
console.log('Dynamo fail ' + err);
q.reject(err);
} else {
console.log('DATA'+ data);
var results = data.Items;
q.resolve(results);
}
});
return q.promise;
}
getDBResults.then(
function(data) {
// handle data
},
function(err) {
//handle error
}
);
Using recursive query I can get the results but I need those results to be used in another function, but because of nodejs async nature,the next function calls happens already before the recursive query function finishes its job, now I want that I get all the results from the recursive query function and then get as a promise to a new function and finally handle all the data.
recursive query for dynamodb looks like this.
function getDBResults(){
//var q = Q.defer();
params = {
TableName: mytable,
IndexName: 'mytable-index',
KeyConditionExpression: 'id = :id',
FilterExpression: 'deliveryTime between :startTime and :endTime',
ExpressionAttributeValues: {
':startTime': {
N: startTime.toString()
},
":endTime": {
N: endTime.toString()
},
":id": {
S: id.toString()
}
},
Select: 'ALL_ATTRIBUTES',
ScanIndexForward: false,
};
dynamodb.query(params, onQueryCallBack);
}
function onQueryCallBack(err, data) {
if (err) {
console.log('Dynamo fail ' + err);
console.error("Could not query db" + err);
} else {
if (typeof data.LastEvaluatedKey != "undefined") {
console.log("query for more...");
params.ExclusiveStartKey = data.LastEvaluatedKey;
dynamodb.query(params, onQueryCallBack);
}
data.Items.forEach(function(item) {
allResults.push(item);
});
//console.log('NO:OF Results:' + allResults.length);
//q.resolve(tickets);
//});
}
Now I want that I can get the results as promise finally so I can handle them in the next function like this.
getDBResults.then(
function(data) {
// handle data
},
function(err) {
//handle error
}
);
Please help me on this, sorry if its a stupid question but recursive calls with promises have made a hurdle for me.
Thanks
First of all, keep the promisified function you already have. Use it as a building block for the recursive solution, instead of trying to alter it!
It might need two small adjustments though:
function getDBResults(startKey){
// ^^^^^^^^
var q = Q.defer();
var params = {
ExclusiveStartKey: startKey,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
… // rest as before
};
dynamodb.query(params, function(err, data) {
if (err) {
q.reject(err);
} else {
q.resolve(data);
// ^^^^ Not `data.Items`
}
});
return q.promise;
}
Now we can use that to trivially implement the recursive solution:
function getRecursiveDBResults(key) {
return getDBResults(key).then(function(data) {
if (typeof data.LastEvaluatedKey != "undefined") {
return getRecursiveDBResults(data.LastEvaluatedKey).then(items) {
return data.Items.concat(items);
});
} else {
return data.Items
}
});
}
Here is how i solve the problem, Thanks Bergi for your solution as well
function getDBResults() {
var q = Q.defer();
var dynamodb = core.getDynamoDB();
params = {
TableName: mytable,
IndexName: 'mytable-index',
KeyConditionExpression: 'id = :id',
FilterExpression: 'deliveryTime between :startTime and :endTime',
ExpressionAttributeValues: {
':startTime': {
N: startTime.toString()
},
":endTime": {
N: endTime.toString()
},
":id": {
S: id.toString()
}
},
Select: 'ALL_ATTRIBUTES',
ScanIndexForward: false,
};
var results = [];
var callback = function(err, data) {
if (err) {
console.log('Dynamo fail ' + err);
q.reject(err);
} else if (data.LastEvaluatedKey) {
params.ExclusiveStartKey = data.LastEvaluatedKey;
dynamodb.query(params, callback);
} else {
q.resolve(results);
}
data.Items.forEach(function(item) {
results.push(item);
});
}
dynamodb.query(params, callback);
return q.promise;
}

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

How to confirm if update succeeds using mongoose and bluebird promise

I'm using bluebird and mongoose for a node page.
I want to check if the update is successful before sending data back to clients via socket.js.Here's the part of the code that I can't figure out:
.then(function(a) {
var g = collection3.update({
_id: a.one[0]._id
}, {
$set: {
avg: a.one[0].avg
}
}).function(err, d) {
if (!err) {
return 1; // Here's the problem
}
}) return {
updated: g,
info: a
};
}).then(function(c) {
console.log(c.updated); // I can't get the `1` value
if (c == 1) {
io.sockets.in('index|1').emit("estimate", c.three);
}
})
Does mongoose return a success message after update? I can't return 1 from the update query and pass it to the next then function, instead, I'm getting this object:
{ _mongooseOptions: {},
mongooseCollection:
{ collection:
{ db: [Object],
collectionName: 'table',
internalHint: null,
opts: {},
slaveOk: false,
serializeFunctions: false,
raw: false,
pkFactory: [Object],
serverCapabilities: undefined },
opts: { bufferCommands: true, capped: false },
name: 'table',
conn:....
Here's the full code:
socket.on("input",function(d){
Promise.props({
one: collection2.aggregate([
{
$match:{post_id:mongoose.Types.ObjectId(d.id)}
},
{
$group:{
_id:"$post_id",
avg:{$avg:"$rating"}
}
}
]).exec();
}).then(function(a){
var g = collection3.update({_id:a.one[0]._id},{$set:{avg:a.one[0].avg}}).function(err,d){
if(!err){
return 1; // Here's the problem
}
})
return {updated:g,info:a};
}).then(function(c){
console.log(c.updated); // I can't get the `1` value
if(c.updated == 1){
io.sockets.in('index|1').emit("estimate",c.three);
}
}).catch(function (error) {
console.log(error);
})
I'm assuming you're using Mongoose here, update() is an asynchronous function, your code is written in a synchronous style.
Try:
socket.on("input",function(d){
Promise.props({
one: collection2.aggregate([
{
$match:{post_id:mongoose.Types.ObjectId(d.id)}
},
{
$group:{
_id:"$post_id",
avg:{$avg:"$rating"}
}
}
]).exec()
}).then(function(a){
return collection3.update({_id:a.one[0]._id},{$set:{avg:a.one[0].avg}})
.then(function(updatedDoc){
// if update is successful, this function will execute
}, function(err){
// if an error occured, this function will execute
})
}).catch(function (error) {
console.log(error);
})
Mongoose docs says
Mongoose async operations, like .save() and queries, return
Promises/A+ conformant promises. This means that you can do things
like MyModel.findOne({}).then() and yield MyModel.findOne({}).exec()
(if you're using co).
Also
Mongoose Update returns the updated document.
So this should look something like this.
function runBarryRun(d) {
Promise.props({
one: aggregateCollection2(d)
})
.then(updateCollection3)
.then(updatedDoc => {
// if update is successful, do some magic here
io.sockets.in('index|1').emit("estimate", updatedDoc.something);
}, err => {
// if update is unsuccessful, find out why, throw an error maybe
}).catch(function(error) {
// do something here
console.log(error);
});
}
function aggregateCollection2(d) {
return collection2.aggregate([{
$match: { post_id: mongoose.Types.ObjectId(d.id) }
}, {
$group: {
_id: "$post_id",
avg: { $avg: "$rating" }
}
}]).exec();
}
function updateCollection3(a) {
return collection3.update({ _id: a.one[0]._id }, { $set: { avg: a.one[0].avg } }).exec();
}
socket.on("input", runBarryRun);

jQuery ignoring status code from Express

I'm making an application where a form has to be validated with AJAX. Nothing too fancy. When a form submit is triggered I'm posting to a URL on a Node.js server and routing with Express. If the data does not pass all of the validation requirements, I'm sending a status code of 400, like so:
app.post('/create', checkAuth, function (req,res)
{
var errors = new Errors();
if (req.body['game-name'].length < 3 || req.body['game-name'].length > 15)
{
res.send({
msg: 'Game name must be between 3 and 15 characters.'
}).status(500).end();
}
else
{
GameModel.find({id: req.body.roomname}, function (err,result)
{
if (result.length !== 0)
{
errors.add('A game with that name already exists.');
}
//Some more validation
if (errors.get().length > 0)
{
res.status(400).send({
msg: errors.get()[0]
}).end();
return;
}
else
{
var data = new GameModel({
roomname: req.body['roomname'],
owner: req.session.user,
id: req.body['roomname'].toLowerCase(),
config: {
rounds: req.body['rounds'],
timeLimit: req.body['time-limit'],
password: req.body['password'],
maxplayers: req.body['players'],
words: words
},
finished: false,
members:
[
req.session.user
]
});
data.save(function (err, game)
{
if (err) {
console.log(err);
res.send({
msg: 'Something funky happened with our servers.'
}).status(500).end();
}
else
{
res.send({
msg: 'All good!'
}).status(200).end();
}
});
}
});
}
});
On the client side, I have the following code:
$.ajax({
type: "POST",
url: "/someURL",
data: $("form").serialize(),
statusCode:
{
200: function (data)
{
//All good.
},
400: function (data)
{
//Uh oh, an error.
}
}
});
Strangely, jQuery is calling the 200 function whenever I send a 400 error. I believe this is because I'm sending an object along with the status code. How can I resolve this?
First guess is you need a return; statement in your express code inside that if block. I bet the error code is running then the success code and the last values for the statusCode/body are being sent to the browser.

Categories

Resources