expressjs mongodb find query loop with queries inside - node.js

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

Related

nodejs mongodb driver native query issue

It turns a funny error in Node.js mongodb, if I put the form below:
locals.collection.find(filter, req.query);
returns me the following error:
MongoError: query selector must be an object
Now if I manually put the query works.
I'm having this problem a long time, and I've tried several ways.
locals.collection.find({slug:'somak'}, req.query);
I am using the following function:
exports.findAll = function(req, res, next) {
var locals = {},
section = req.params.section,
query = req.query,
filter = {};
if(query.filter) {
filter = query.filter.replace(/"(\w+)"\s*:/g, '$1:');
filter = filter.replace(/["]/g, "'");
}
console.log(filter);
delete query.filter;
async.series([
function(callback) {
MongoClient.connect(url, function(err, db) {
if (err) return callback(err);
locals.collection = db.collection(section);
callback();
});
},
function(callback) {
locals.collection.count(filter, function (err, result){
if (err) return callback(err);
locals.count = result;
callback();
});
},
function(callback) {
var cursor = locals.collection.find({slug:'somak'}, req.query);
if(req.query.page) {
cursor = cursor.skip(Math.abs(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
});
});
Just use JSON.parse:
exports.findAll = function(req, res, next) {
var locals = {},
section = req.params.section,
query = req.query,
filter = {};
if(query.filter) {
filter = JSON.parse(query.filter);
}
delete query.filter;
...

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

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

Applying async nodejs

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

Fetch data from multiple collections in mongo

I have some collections shown like below which is holding relationships, relation between testMaster and testDoc is holding inside the testDocMaster
For eg:
testMaster {
_id: "Schema.Objectid",
name: "String" //master name
}
testDoc {
_id : Schema.ObjectId,
name: "String", //doc name
other datas
}
testDocMaster {
masterId: "_id of the testMaster table",
docId : "_id of the above testDoc"
}
For each master entry, we are expecting many relations,
what would be the best way to fetch the data from the testDoc table, if I have the masterId.
I got it working using this:
// GLOBAL ARRAYS FOR STORING COLLECTION DATA
var collectionOne = [];
var collectionTwo = [];
app.get('/', function(req, res){
MongoClient.connect("mongodb://localhost:27017/michael", function(err, db) {
if(!err) {
console.log("We are connected");
}
db.collection("collectionOne", function(err, collection) {
collection.find().sort({order_num: 1}).toArray(function(err, result) {
if (err) {
throw err;
} else {
for (i=0; i<result.length; i++) {
collectionOne[i] = result[i];
}
}
});
db.collection("collectionTwo", function(err, collection) {
collection.find().sort({order_num: 1}).toArray(function(err, result) {
if (err) {
throw err;
} else {
for (i=0; i<result.length; i++) {
collectionTwo[i] = result[i];
}
}
});
});
// Thank you aesede!
res.render('index.html', {
collectionOne: collectionOne,
collectionTwo: collectionTwo
});
});
});
});
Now, for some reason when Node restarts, and I hit refresh, it doesn't render HTML into the front-end. However, any subsequent refresh renders the page correctly.
Assuming your testDocMaster schema uses ObjectId types that ref the other two collections you can use Mongoose's query population support to help with this:
TestDocMaster.findOne({ masterId: masterId})
.populate('docId')
.exec(function(err, testDocMaster) {
// testDocMaster.docId is populated with the full testDoc for the
// matching _id
});

Resources