ExpressJS mongodb multiple queries - node.js

I want to take data with two collections.
How to send this data in one response?
This is my code:
//RETURN SINGLE QUERYE
exports.findById = function(req, res) {
//Get Params
var querye_id =new BSON.ObjectID(req.params.id);
var querye;
//Get Single Querye
db.collection('queryes', function(err, collection) {
collection.findOne({'_id':querye_id}, function(err, item) {
querye=item;
});
});
//Get Questions and Answers
db.collection('questions', function(err, collection) {
collection.find().toArray(function(err, items) {
querye.questions=items;
});
});
//Send Response
res.send(querye);
};
I have varible querye as undefined. How to solve this?

var async = require('async');
function getQueries(id, callback) {
db.collection('queryes', function(err, collection) {
collection.findOne({'_id':id}, callback);
});
}
function getQuestions(callback) {
db.collection('questions', function(err, collection) {
collection.find().toArray(callback);
});
}
exports.findById = function(req, res) {
var querye_id =new BSON.ObjectID(req.params.id);
async.parallel({
querye: async.apply(getQueries, query_id),
questions: getQuestions
}, function (error, results) {
if (error) {
res.status(500).send(error);
return;
}
results.querye.questions = results.questions;
res.send(results.querye);
});
};

What you should probably do is use deferreds/promises to get the results in a waterfall manner then return the results when everything is completed.
I like to use the async library for node.
https://npmjs.org/package/async
What this lets you do is queue up all of your async functions then report back success/failure. At the very end you will get the results.
Assuming you already required the async module your code would look something like this.
exports.findById = function(req, res) {
//Get Params
var querye_id =new BSON.ObjectID(req.params.id);
async.waterfall([
function(callback){
//Get Single Querye
db.collection('queryes', function(err, collection) {
collection.findOne({'_id':querye_id}, function(err, item) {
callback(null, item);
});
});
},
function(arg, callback){
//Get Questions and Answers
db.collection('questions', function(err, collection) {
collection.find().toArray(function(err, items) {
arg.questions=items;
callback(null, arg);
});
});
}],function(err, results){
//Send Response
res.send(results);
});
};

Related

Mongodb.connect does not execute callback function

Im trying to connect to my Mongodb and insert some documents if they are not already in the db. It works fine with the first inserts but in the function existInDatabase it sometimes does not execute the callback function.
var MongoClient = require('mongodb').MongoClient;
var mongoData = require('./mongoData');
var exports = module.exports = {};
var dbName = 'checklist';
MongoClient.connect(mongoData.ConString, {
useNewUrlParser: true
}, function(err, db) {
if (err) throw err;
for (var key in mongoData.Customers) {
if (!existsInDatabase(mongoData.Customers[key], 'Customers')) {
db.db(dbName).collection('Customers').insertOne(mongoData.Customers[key], function(err, res) {
if (err) throw err;
console.log('1 document inserted');
db.close();
});
}
}
for (var key in mongoData.Categorys) {
if (!existsInDatabase(mongoData.Customers[key], 'Customers')) {
db.db(dbName).collection('Categorys').insertOne(mongoData.Categorys[key], function(err, res) {
if (err) throw err;
console.log('1 document inserted');
db.close();
});
}
}
});
function existsInDatabase(obj, collection) {
var result = false;
MongoClient.connect(mongoData.ConString, {
useNewUrlParser: true
}, function(err, db) {
db.db(dbName).collection(collection).find({}).forEach(function(doc) {
if (doc.id == obj.id) {
result = true;
}
}, function(err) {
console.log(err);
});
});
return result;
}
I have made a few changes to your code. It seems you are new to async programming, spend some time to understand the flow. Feel free for any further query. Here is your code.
// Welcome to aync programming
// Here no one waits for the slow processes
var MongoClient = require('mongodb').MongoClient;
var mongoData = require('./mongoData');
var exports = module.exports = {};
var dbName = 'checklist';
// Make the connection for once only
MongoClient.connect(mongoData.ConString, { useNewUrlParser: true },
function(err, db) {
if (err) throw err;
var myDB = db.db(dbName); // create DB for once
for (var key in mongoData.Customers) {
//make call to the function and wait for the response
existsInDatabase(mongoData.Customers[key], 'Customers', function(err, result) {
//once the response came excute the next step
if (result) {
myDB.collection('Customers').insertOne(mongoData.Customers[key], function(err, res) {
if (err) throw err;
console.log('1 document inserted');
});
}
});
}
for (var key in mongoData.Categorys) {
//make call to the function and wait for the response
existsInDatabase(mongoData.Customers[key], 'Customers', function(err, result) {
//once the response came excute the next step
if (result) {
myDB.collection('Categorys').insertOne(mongoData.Categorys[key], function(err, res) {
if (err) throw err;
console.log('1 document inserted');
});
}
});
}
// Both the for loop will work randomly without any order
function existsInDatabase(obj, collection, cb) {
var result = false;
myDB.collection(collection).findOne({ id: obj.id }, function(err, result)
{
if (err) {
//this cb will work only when db operation is complited
cb(err);
} else if (result) {
cb(null, true);
} else {
cb(null, false);
}
});
}
});
This code may result in some error. Feel free to ask more questions over it
db.db(dbName).collection(collection).find({}) returns a cursor per the docs. You are missing .toArray():
db.db(dbName).collection(collection).find({}).toArray()...

Async parallel to pass data in object

So I'm trying to pass data from two databases to my dashboard view after user gets logged in, however...
Don't know if that's the right way of doing.
Don't know how to access those two objects that are passed in results.
router.get("/dashboard", middleware.isLoggedIn, function (req, res) {
async.parallel([
function (callback) {
callback(null, User.findById(req.user));
},
function (callback) {
callback(null, Instrument.find({}));
}
],
function (err, results) {
res.send("all good" + results);
console.log(results)
});
});
Try it like this:
router.get("/dashboard", middleware.isLoggedIn, function (req, res) {
async.parallel([User.findById(req.user), Instrument.find({})
],
function (err, results) {
if (err) {
// handle error here
} else {
// results[0] will have user object
// results[1] will have instrument object
res.send(results); // sending json to client
}
});
});

Express js use values from mongodb to outside of the query

I am new with express js and node js. I am wonder if its possible to do this or not.
1st thing I'm trying to get the count on 1 query.
2nd I query collection login. loop it and format and get the name values using ids.
I get undefined on count1 and I see it ommitted on the json response.
how do I pass the query returns outside of the query?
router.post('/logmein', function(req, res, next) {
var email = req.param('email');
var password = req.param('password');
var count1;
MongoClient.connect(MongoUrl, function(err, db) {
if (err) return;
db.collection('users').count({email: email,password: md5(password)}, function(err, count) {
count1 = count;
});
db.collection('login').find({email: email,password: md5(password)}).toArray(function(err, results){
console.log(results); // output all records
});
//use results
/*
foreach each results format with other info
var new_array;
*/
res.json({"success": true,"count":count1,new_array: new_array});
});
});
Async issues, you have to look more at it. Use the async module and then you will be able to fire the queries in parallel. Finally, you will have the results in the final callback. This snippet will fix your issue:
var async = require('async');
router.post('/logmein', function (req, res, next) {
var email = req.param('email');
var password = req.param('password');
var retResults;
var count1;
MongoClient.connect(MongoUrl, function (err, db) {
if (err) return;
async.parallel([
function(callback){
db.collection('users').count({ email: email, password: md5(password) }, function (err, count) {
return callback(err, count);
});
},
function(callback){
db.collection('login').find({ email: email, password: md5(password) }).toArray(function (err, results) {
return callback(err, results);
});
}
], function(err, results) {
if (err) {
// #todo: handle the error
}
count1 = results[0];
retResults = results[1];
//use results
/*
foreach each results format with other info
var new_array;
*/
res.json({ "success": true, "count": count1, new_array: new_array });
};
});
});
You need something like async.parallel to control the async flow of your nodejs application
async.parallel([
function(callback){ ... },
function(callback){ ... }
], function(err, results) {
// optional callback
};

NodeJS / Mongoose Filter JSON

I am building a JSON API with ExpressJS, NodeJS and Mongoose:
Input -> id:
app.get('/folder/:id', function (req, res){
return Cars.find({reference: req.params.id}, function (err, product) {
if (!err) {
console.log(product);
return res.send(product);
} else {
return console.log(err);
}
});
});
It shows well the JSON:
[{"_id":"B443U433","date":"2014-08-12","reference":"azerty","file":"087601.png","
....:.
{"_id":"HGF6789","date":"2013-09-11","reference":"azerty","file":"5678.pnf","
...
I just want to display the _id in the JSON, so it is good when I have lots of data.
How I can do that? Something like a filter?
You can chain calls to select and lean to retrieve just the fields you want from the docs you're querying:
app.get('/folder/:id', function (req, res){
return Cars.find({reference: req.params.id}).select('_id').lean().exec(
function (err, product) {
if (!err) {
console.log(product);
return res.send(product);
} else {
return console.log(err);
}
});
});
You would have to iterate over your "products" object to obtain the ids
Something like this:
(Disclaimer: I haven't tested this)
app.get('/folder/:id', function (req, res){
return Cars.find({reference: req.params.id}, function (err, product) {
if (!err) {
console.log(product);
var ids = new Array();
for(var i = 0; i < product.length; i++){
ids.push(product[i]._id);
}
return res.send(JSON.stringify(ids));
} else {
return console.log(err);
}
});
});
--Edit
Also, "products" may already be a JSON string. You may want to parse it before looping.
product = JSON.parse(product);
Other answers are true but I think it's better to limit data in mongoose like this :(it's same as mongo shell commands)
app.get('/folder/:id', function (req, res){
Cars.find({reference: req.params.id} ,{ _id : true } ,function (err, product) {
if (!err) {
console.log(product);
} else {
console.log(err);
}
});
});

How to assign query results to an object

I am trying to transfer results data from query function to an object.
console.log(results) line returns 'undefined' result. What should I do?
module.exports = {
show: function(req, res) {
var results;
User.native(function(err, User) {
if(err) {
console.log("There is no exist a User by _id");
}
User.findOne({'_id' : req.param('id')},
function(err, user) {
results = user;
});
});
console.log(results);
return res.view({ stuff : results });
}
};
You have an async issue, the callback from findOne isn't necessarily executed in line with the rest of the code, so you get to the console.log(results) before results = user gets called. You'd want to change it to something like this:
show: function(req, res) {
var results;
User.native(function(err, User) {
if(err) {
console.log("There is no exist a User by _id");
}
User.findOne({'_id' : req.param('id')},
function(err, user) {
results = user;
console.log(results);
// Send response or make a callback here
});
});
}

Resources