Sails js -model resultset variable scope - node.js

Can someone explain to me why i can't save the booksCount variable into the users json object? Here's my code
for(var user in users){
Books.count({author: users[user]['id']}).exec(function(err, count){
users[user]['booksCount']=count;
});
}
return res.view('sellers', {data: users});
Where Users is the list of users from the table which is the direct result of a User.find() method. User is the model.
Now if i try to print the users[user]['booksCount'] inside the for loop, it works fine. But when it goes outside the for loop the variable vanishes into thin air. Console prints 'undefined' outside for loop.

Because Books.count is an API call and all the API call are async so In
for(var user in users){
// It Will call the Books.count and leave the callback Function without waiting for callback response.
Books.count({author: users[user]['id']}).exec(function(err, count){
users[user]['booksCount']=count;
});
}
//As callback result didn't came here but the controll came here
// So, users[user] will be undefined here
return res.view('sellers', {data: users});
Use promises:
async.forEachOf(users, function (value, user, callback) {
Books.count({author: users[user]['id']}).exec(function(err, count){
users[user]['booksCount']=count;
callback(err);
// callback function execute after getting the API result only
});
}, function (err) {
if (err) return res.serverError(err.message); // Or Error view
// You will find the data into the users[user]
return res.view('sellers', {data: users});
});

Related

What is the best way to query mongodb with mongoose with an array of IDs?

I query one collection (messages) with mongoose. The result is an array of documents. Each document contains an ID for a different collection (users). Now I want to query the users collection for each ID from the messages collection.
The idea is to update each message object with the information from the user collection before returning it to the front end.
I tried using async.each. For some reason the final function is never called even though I am making sure the callback() function is called after each iteration.
app.get('/getmsg', function(req, res){
messages.find({query})
.exec(function(err, ms){
if(err) throw err;
async.each(ms, function(m, callback){
users.findOne({_id : m.userId})
.lean()
.exec(function(err, user){
if(err) {
console.log('error' , err);
callback();
} else {
m.userName = user.name;
// everything is working up to here
callback();
}
}), function(err){
res.send(ms); // this is never returned!
}
});
});
});
Is there a better way of achieving this? I assume this must be a common issue.
Thanks!
You can't use res.send. Instead create a function to get notified about it. Something like this.
// 1st para in async.each() is the array of items
async.each(items,
// 2nd param is the function that each item is passed to
function(item, callback){
// Call an asynchronous function, often a save() to DB
item.someAsyncCall(function (){
// Async call is done, alert via callback
callback();
});
},
// 3rd param is the function to call when everything's done
function(err){
// All tasks are done now
doSomethingOnceAllAreDone();
}
);

res.send() is running before nested function completes with value

The 'GetUsers' function is running and it returns the correct values as expected. However, res.send(users) is running before the nested function has completed with the value so it is undefined. I have tried doing the res.send(users) from inside the nested function but it doesn't recognize the function. I have also tried using return users but can't get it to work. Any help would be appreciated!
app.get('/GetUsers', function (req, res){
var url = 'mongodb://localhost:27017/users';
var users = "";
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
console.log('Connection established to', url);
// Get the documents collection
var collection = db.collection('users');
// Get some users
collection.find().toArray(function (err, res) {
if (err) {
console.log(err);
} else {
console.log('Got Information from db.');
}
//Close connection
db.close();
console.log('db closed.');
users = res;
console.log(users);
});
}
});
res.send(users);
});
Just move res.send(users) inside your callback before/after you call console.log(users).
You reused the variable res in the toArray call, so please rename it there: ...toArray(function (err, res)... to ...toArray(function (err, result)... and also in users = res; to users = result;.
this is async call it will send the response and will not wait for the database connection function.
write res.send in call back function
write your res.send function where you are closing your data base connection.
here is detail How do I return the response from an asynchronous call?
Found the answer!!!
I needed to use res.send(users) in my callback after I had closed the database as pointed out by the users suggestions above. HOWEVER, that alone wasn't working as res.send(users) was erroring as it wasn't recognized as a function. I had to use res.send({'matched':users}) and then access my response objects on the client side like this: response.matched.objects etc.
Thanks for your help everyone!

NodeJS Cannot read property 'length' of undefined. [duplicate]

When I run collection.find() in MongoDB/Node/Express, I'd like to get a callback when it's finished. What's the correct syntax for this?
function (id,callback) {
var o_id = new BSON.ObjectID(id);
db.open(function(err,db){
db.collection('users',function(err,collection){
collection.find({'_id':o_id},function(err,results){ //What's the correct callback synatax here?
db.close();
callback(results);
}) //find
}) //collection
}); //open
}
That's the correct callback syntax, but what find provides to the callback is a Cursor, not an array of documents. So if you want your callback to provide results as an array of documents, call toArray on the cursor to return them:
collection.find({'_id':o_id}, function(err, cursor){
cursor.toArray(callback);
db.close();
});
Note that your function's callback still needs to provide an err parameter so that the caller knows whether the query worked or not.
2.x Driver Update
find now returns the cursor rather than providing it via a callback, so the typical usage can be simplified to:
collection.find({'_id': o_id}).toArray(function(err, results) {...});
Or in this case where a single document is expected, it's simpler to use findOne:
collection.findOne({'_id': o_id}, function(err, result) {...});
Based on JohnnyHK answer I simply wrapped my calls inside db.open() method and it worked. Thanks #JohnnyHK.
app.get('/answers', function (req, res){
db.open(function(err,db){ // <------everything wrapped inside this function
db.collection('answer', function(err, collection) {
collection.find().toArray(function(err, items) {
console.log(items);
res.send(items);
});
});
});
});
Hope it is helpful as an example.

Node JS + Express + Mongoose not filling array from findById

I need to edit a Game object by adding a User object to it. This is done by the mongoose query findById. This query works. But however, when I want to add the modified game to the resulting array, something goes wrong. The 'console.log(game);' returns the right output, but the 'console.log(result);' is always empty. What is going wrong? Is it something with inner functions?
var result = [];
games.forEach(function(game){
User.findById(game.user1_id, function(err, user){
if(err)
console.log(err);
game.user1 = user;
result.push(game);
console.log(game);
});
});
console.log(result);
You have run into a class callback problem. When you call forEach on games the code will actually continue outside the callback and the result will therefore be the value you first assigned to it, which is []. This is due to the code being evaluated asynchronous.
Solve this by moving your code into a function with a callback that is called when the loop is done, like this:
var utils = require('restberry-utils');
var getResult = function(next) {
var result = [];
utils.forEachAndDone(games, function(game, iter) {
User.findById(game.user1_id, function(err, user) {
if (err) console.log(err);
game.user1 = user;
result.push(game);
iter();
});
}, function() {
next(result);
});
};
getResult(function(result) {
console.log(result);
});
Notice I've imported the restberry-utils package and used the forEachAndDone method. This method will loop through the objects but won't continue unless you call the iter method. When it has looped through all the objects, the last callback is called which is where I'm returning the result.
Hope this makes sense.

How to get a callback on MongoDB collection.find()

When I run collection.find() in MongoDB/Node/Express, I'd like to get a callback when it's finished. What's the correct syntax for this?
function (id,callback) {
var o_id = new BSON.ObjectID(id);
db.open(function(err,db){
db.collection('users',function(err,collection){
collection.find({'_id':o_id},function(err,results){ //What's the correct callback synatax here?
db.close();
callback(results);
}) //find
}) //collection
}); //open
}
That's the correct callback syntax, but what find provides to the callback is a Cursor, not an array of documents. So if you want your callback to provide results as an array of documents, call toArray on the cursor to return them:
collection.find({'_id':o_id}, function(err, cursor){
cursor.toArray(callback);
db.close();
});
Note that your function's callback still needs to provide an err parameter so that the caller knows whether the query worked or not.
2.x Driver Update
find now returns the cursor rather than providing it via a callback, so the typical usage can be simplified to:
collection.find({'_id': o_id}).toArray(function(err, results) {...});
Or in this case where a single document is expected, it's simpler to use findOne:
collection.findOne({'_id': o_id}, function(err, result) {...});
Based on JohnnyHK answer I simply wrapped my calls inside db.open() method and it worked. Thanks #JohnnyHK.
app.get('/answers', function (req, res){
db.open(function(err,db){ // <------everything wrapped inside this function
db.collection('answer', function(err, collection) {
collection.find().toArray(function(err, items) {
console.log(items);
res.send(items);
});
});
});
});
Hope it is helpful as an example.

Resources