Applying async nodejs - node.js

How to apply async series to this code... I am having trouble with the querying and using the results correctly... I need to take the user.friendStatus which is a list of all the friends a user have in the Friend collection and then, take each id of the friend and find the user info from the User collection...
UPDATE: Changed async.series to async.waterfall so I can pass one result to the next function. However, I am unsure how you call and pass the results from one to the next...
Here is my code:
exports.contactList = function(req, res) {
async.waterfall([
function(callback) {
var friends = [];
Friend.findOne({userId: req.signedCookies.userid}, function(err, users) {
if(err) {throw err};
for(var i = 0; i < users.friendStatus; i++) {
if(users.friendStatus[i].status === 3) { friends.push(users.friendStatus[i])};
}
});
callback(null, friends);
},
function(callback) {
var friendsinfo = [];
for(var i = 0; i < friends.length; i++) {
User.findbyID({_id: friends[i].fuID}, function(err, users) {
if (err) { throw err};
friendsInfo.push(users[i], friends[i].favorites);
});
}
callback(null, friendsinfo);
}
],
function(err, results) {
res.render('contactList', {title: 'Weblio', Friends: friendsinfo});
});
};

Here is an example of async.waterfall from https://github.com/caolan/async#waterfall I added more comments to me make it clear
async.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
// arg1 is equal 'one'
// arg2 is equal 'two
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
[edit] applied to your code...(notice is just add friends param to the second function)
[edit] better indentation, and change results var to friendsinfo in the last callback
exports.contactList = function(req, res) {
async.waterfall([
function(callback) {
var friends = [];
Friend.findOne({userId: req.signedCookies.userid}, function(err, users) {
if(err) {throw err};
for(var i = 0; i < users.friendStatus; i++) {
if(users.friendStatus[i].status === 3) { friends.push(users.friendStatus[i])};
}
});
callback(null, friends);
},
function(friends, callback) {
var friendsinfo = [];
for(var i = 0; i < friends.length; i++) {
User.findbyID({_id: friends[i].fuID}, function(err, users) {
if (err) { throw err};
friendsInfo.push(users[i], friends[i].favorites);
});
}
callback(null, friendsinfo);
}
],
function(err, friendsinfo) {
res.render('contactList', {title: 'Weblio', Friends: friendsinfo});
});
};

Related

Callback function is not working as expected in watefall on async.foreachlimit

The callback function is not working as expected in a waterfall on async.for each limit.
When I remove that rest api call and if I call callback(null,arg2) it is working as expected, I think the problem with callback function or some api call in the below code.
var async = require("async");
var users = [1, 2, 3, 45]; // Initialize user array or get it from DB
var e = [];
var unirest = require("unirest");
var data = function() {
return new Promise((resolve, reject) => {
async.forEachLimit(
users,
1,
function(user, userCallback) {
async.waterfall(
[
function(callback) {
console.log(user);
callback(null, user);
},
function(arg1, callback) {
console.log(arg1);
callback(null, arg1);
},
function(arg2, callback) {
unirest
.get("http://dummy.restapiexample.com/api/v1/employee/1")
.end(function(response) {
if (response.error) callback("null", "data");
else callback(null, arg2);
});
}
],
function(err, result) {
if (err) {
console.log("err");
reject(e);
} else {
console.log("done", result);
e.push(result);
userCallback();
resolve(e);
}
}
);
},
function(err, result) {
console.log("User For Loop Completed", err, result);
}
);
});
};
I'm getting output as
expected outputs as

expressjs mongodb find query loop with queries inside

So I am using this query to create an array to pass on to ejs to generate table rows. but before I pass it, I have other queries like referencing ids or count. but I get undefined on count. I may have other queries inside.
router.get('/', function(req, res, next) {
MongoClient.connect(MongoUrl, function(err, db) {
var table_data = [];
db.collection('categories').find().toArray(function(err, result) {
for (var i = 0, len = result.length; i < len; i++) {
var count;
db.collection('articles').count({category_id: result[i].category_id}, function(err, count) {
count = count;
});
table_data[i] = {
"category_id" : result[i].category_id,
"category" : result[i].category,
"count" : count,
"date_last_imported" : result[i].date_last_imported,
"feed" : result[i].feed,
"url" : result[i].url,
"xml" : result[i].xml
};
}
console.log(table_data);
res.render('categories', {title:"Categories",table_data: table_data});
});
});
});
It's because count is not yet fetched and you have pushed it in array that is undefined. If you have lot of queries inside queries then in that case you'll be trapped in callback hell. To get out of this scenario either use promise or use "async"module.
For reference: http://caolan.github.io/async/
For current scenario and to get idea why "count" is undefined:
router.get('/', function(req, res, next) {
MongoClient.connect(MongoUrl, function(err, db) {
var table_data = [];
db.collection('categories').find().toArray(function(err, result) {
for (var i = 0, len = result.length; i < len; i++) {
var count;
db.collection('articles').count({category_id: result[i].category_id}, function(err, count) {
count = count;
table_data.push( {
"category_id" : result[i].category_id,
"category" : result[i].category,
"count" : count,
"date_last_imported" : result[i].date_last_imported,
"feed" : result[i].feed,
"url" : result[i].url,
"xml" : result[i].xml
});
if(table_data.length === result.length) {
res.render('categories', {title:"Categories",table_data: table_data});
}
});
}
});
});
});
UPDATE:
To get out of inner queries.
router.get('/', function(req, res, next) {
MongoClient.connect(MongoUrl, function(err, db) {
var table_data = [];
db.collection('categories').find().toArray(function(err, result) {
async.each(result, function (user, cb) {
async.waterfall([
function (cb) {
db.collection('articles').count({
category_id: result[i].category_id
}, function(err, count) {
cb(err, count);
});
},
function (count, cb) {
//another query
cb(err, count, secondquerydata);
},
.....
], function (err, count, secondquerydata, .....) {
if(err) return callback(err);
// here use your all query data
table_data.push( {
"category_id": result[i].category_id,
"category": result[i].category,
"count": count,
"date_last_imported": result[i].date_last_imported,
"feed": result[i].feed,
"url": result[i].url,
"xml": result[i].xml
});
res.render('categories', {
title: "Categories",
table_data: table_data
});
})
})
});
});
});

mongodb native driver error on query

I am writing the filter using mongodb native driver, but it's driving me this error when you run the query.
In the case of this driver, it has no exec?
What is another way to perform this query?
exports.findAll = function(req, res) {
MongoClient.connect(url, function(err, db) {
var section = req.params.section;
var collection = db.collection(section);
var filter = req.query.filter ? {nameToLower: new RegExp('^' + req.query.filter.toLowerCase())} : {};
var query = collection.find(filter);
var count = 0;
collection.count(filter, function (error, result) {
count = result;
});
if(req.query.order) {
query.sort(req.query.order);
}
if(req.query.limit) {
query.limit(req.query.limit);
if(req.query.page) {
query.skip(req.query.limit * --req.query.page);
}
}
query.exec(function (error, results) {
res.json({
count: count,
data: results
});
});
});
};
Error:
TypeError: undefined is not a function
Better to use the async library in this case as it simplifies the code. In the case where you need to run multiple tasks that depend on each other and when they all finish do something else, use the
async.series() module. The following demonstrates how you can go about this in your case:
exports.findAll = function(req, res) {
var locals = {},
section = req.params.section,
filter = !!req.query.filter ? {nameToLower: new RegExp('^' + req.query.filter.toLowerCase())} : {};
async.series([
// Connect to DB
function(callback) {
MongoClient.connect(url, function(err, db) {
if (err) return callback(err);
locals.collection = db.collection(section); //Set the collection here, so the next task can access it
callback();
});
},
// Get count
function(callback) {
locals.collection.count(filter, function (err, result){
if (err) return callback(err);
locals.count = result; //Set the count here
callback();
});
},
// Query collection
function(callback) {
var cursor = locals.collection.find(filter);
if(req.query.order) {
cursor = cursor.sort(req.query.order);
}
if(req.query.limit) {
cursor = cursor.limit(req.query.limit);
if(req.query.page) {
cursor = cursor.skip(req.query.limit * --req.query.page);
}
}
cursor.toArray(function(err, docs) {
if (err) return callback(err);
locals.docs = docs;
callback();
});
}
], function(err) { //This function gets called after the three tasks have called their "task callbacks"
if (err) return next(err);
// Here locals will be populated with 'count' and 'docs'
res.json({
count: locals.count,
data: locals.docs
});
res.render('user-profile', locals);
});
};

how to run code sequentially in node js

Hi I have this code but when finish the result is not the espected because didn't run in the sequence that I wish
here is the code:
var user_data = {};
models.games.find({$or: [{w_id: req.user._id}, {b_id: req.user._id}, {owner: req.user._id}]}, function (err, games) {
var req_games = [];
if (!err) {
for (var i in games) {
req_games.push(games[i]);
models.users.findOne({_id: games[i].w_id}, function (err, user) {
req_games[i].w_id = user.user;
console.log(req_games[i].w_id) //< -- 3
});
console.log('a ' + req_games[i].w_id) //<-- 2
}
user_data.games = req_games; // <-- 1
}
});
at the end of the task req_games didnt have any update because it's running in the sequence that I put in the comments in the code
This may help you using Q(promises)
obj.find = function(model, condition) { //make your find to return promise
var deferred = q.defer();
model.find(condition, function(err, results) {
if (err) {
logger.log(err);
deferred.reject(err);
} else {
deferred.resolve(results);
}
});
return deferred.promise;
}
ArraysOfId.forEach(function (id) {
var tempProm = mongoUtilsMethodObj.find(schemaObj.Asset, id).then(function (assetObj) {
---- your code
return q.resolve();
});
promArr.push(tempProm);//push all promise to array
});
q.all(promArr).then(function () {
// this will be called when all promise in array will we resolved
})
Here is a version using the async library to map your game values.
var async = require('async');
var user_data = {};
models.games.find({$or: [{w_id: req.user._id}, {b_id: req.user._id}, {owner: req.user._id}]}, function (err, games) {
if(err) {
// or whatever your error response happens to be
return res.render('user.swig', {error: err});
}
async.map(games, function(game, nextGame) {
models.users.findOne({_id: game.w_id}, function (err, user) {
game.w_id = user.user;
nextGame(err, game);
});
}, function(err, req_games) {
user_data.games = req_games;
res.render('user.swig', {user: user_data});
});
});

Not able to access result returned from mongoDb outside query

I am working with node.js using express and mongoDb.
I am trying to retrieve data from database "local" and collection "borrower" and pushing it into an empty array collectionOne. It is rendering this array on the view page.
I want to use _id pushed in an array b_ids from this output to make a call to company_details collection under same router .
The issue I am facing here is I am not getting any value in array b_ids when trying to access it under call to company_details collection.
The code is as below:
var collectionOne = [];
var collectionTwo = [];
var b_ids = [];
router.get('/fetch',function(req, res, next){
MongoClient.connect('mongodb://localhost:27017/local', function(err, db){
var collection = db.collection('borrower');
db.collection("borrower", function(err, collection) {
collection.find().toArray(function(err, result) {
if (err) {
throw err;
} else {
//console.log(result[0].first_name);
for (i=0; i<result.length; i++) {
collectionOne[i] = result[i];
b_ids[i] = result[i]._id;
}
}
});
db.collection("company_details", function(err, collection){
console.log(b_ids);
collection.find({borrower_id : {$in :b_ids}}).toArray(function(err, result){
if (err) {
throw err;
} else {
for (i=0; i<result.length; i++) {
collectionTwo[i] = result[i];
}
}
});
});
res.render('index',{data : collectionOne , data1 : collectionTwo});
});
});
});
Please suggest me how can I access the b_ids array here. Thanks in advance :)
The callback from toArray is asynchronous, i.e. will happen after your console.log line executes, therefore you need to re-arrange as follows:
var collectionOne = [];
router.get('/fetch',function(req, res, next){
MongoClient.connect('mongodb://localhost:27017/local', function(err, db){
var collection = db.collection('borrower');
db.collection("borrower", function(err, collection) {
collection.find().toArray(function(err, result) {
if (err) {
throw err;
} else {
for (i=0; i<result.length; i++) {
collectionOne[i] = result[i];
b_ids[i] = result[i]._id;
}
db.collection("company_details", function(err, collection){
console.log(b_ids);
collection.find({borrower_id : {$in :b_ids}}).toArray(function(err, result){
if (err) {
throw err;
} else {
for (i=0; i<result.length; i++) {
collectionTwo[i] = result[i];
}
res.render('index',{data : collectionOne , data1 : collectionTwo});
}
});
});
}
});
});
});
This ensures that the callback completes before you try and log / render it.
It is simple create a function with a callback function inside it as an argument
function returnBids(collection, callback){
collection.find().toArray(function(err, result) {
if (err)
throw err;
else
callback(result)
})
}
and you can access your result outside by calling the function returnbids() .i.e
returnBids(collection,function(b_ids){
console.log(b_ids)
})

Resources