I am trying to use async library but I don't know how to rewrite callback hell in a real world example. I am especially interested in series method combined with communication to some existing driver. Could somebody rewrite the following source code using async.js series method? It is taken from this link.
I am not fixed to MongoDb. If anybody has rewritten some other callback hell example to async series it would be nice to show.
I would also be interested in any alternative solution besides async library. But again - rewriting this example in that solution (or showing other full example), so we could see real code and compare.
var MongoClient = require('../lib/mongodb').MongoClient
, format = require('util').format;
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017;
console.log("Connecting to " + host + ":" + port);
MongoClient.connect(format("mongodb://%s:%s/node-mongo-examples?w=1", host, port), function(err, db) {
db.dropDatabase(function(err, result) {
var collection = db.collection('test');
// Erase all records from the collection, if any
collection.remove({}, function(err, result) {
// Insert 3 records
for(var i = 0; i < 3; i++) {
collection.insert({'a':i}, {w:0});
}
collection.count(function(err, count) {
console.log("There are " + count + " records in the test collection. Here they are:");
collection.find().each(function(err, item) {
if(item != null) {
console.dir(item);
console.log("created at " + new Date(item._id.generationTime) + "\n")
}
// Null signifies end of iterator
if(item == null) {
// Destory the collection
collection.drop(function(err, collection) {
db.close();
});
}
});
});
});
});
});
Something like this with async.series (code not tested, provided just as reference):
var async = require('async')
, MongoClient = require('../lib/mongodb').MongoClient
, format = require('util').format;
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017;
var collection, db
console.log("Connecting to " + host + ":" + port);
async.series([
// Connect to DB
function(callback) {
var connectionString = format("mongodb://%s:%s/node-mongo-examples?w=1", host, port)
MongoClient.connect(connectionString, function(err, ref) {
if (ref) db = ref
callback(err, ref)
})
},
// Drop DB
function(callback) {
db.dropDatabase(callback)
},
// Erase all records from the collection, if any
function(callback) {
collection = db.collection('test');
collection.remove({}, callback)
},
// Insert 3 records
function(callback) {
async.each(new Array(3), function(cb) {
collection.insert({'a':i}, {w:0}, cb);
}, callback)
},
//Check records count
function(callback) {
collection.count(function(err, count) {
if (err) return callback(err)
console.log("There are " + count + " records in the test collection. Here they are:");
callback(err, count)
})
},
//Indicate items
function(callback) {
collection.find({}, function(err, items) {
items.forEach(function(item) {
if(item == null) return
console.dir(item);
console.log("created at " + new Date(item._id.generationTime) + "\n")
})
callback(err, items)
})
},
function(callback) {
collection.drop(callback)
}
], function(error, results) {
// disconnect from DB
error && console.error(error)
db && db.close();
})
Or like this (mixed waterfall and series approach, no global vars):
var async = require('async')
, MongoClient = require('../lib/mongodb').MongoClient
, format = require('util').format;
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017;
console.log("Connecting to " + host + ":" + port);
async.waterfall({
// Connect to DB
db: function(callback) {
var connectionString = format("mongodb://%s:%s/node-mongo-examples?w=1", host, port)
MongoClient.connect(connectionString, callback)
},
// Drop DB
collection: function(db, callback) {
db.dropDatabase(function(err, result) {
var collection = db.collection('test');
callback(err, collection)
})
},
collModifications: function(collection, callback) {
async.series([
// Erase all records from the collection, if any
function(callback) {
collection.remove({}, callback)
},
// Insert 3 records
function(callback) {
async.each(new Array(3), function(cb) {
collection.insert({'a':i}, {w:0}, cb);
}, callback)
},
//Check records count
function(callback) {
collection.count(function(err, count) {
if (err) return callback(err)
console.log("There are " + count + " records in the test collection. Here they are:");
callback(err, count)
})
},
//Indicate items
function(callback) {
collection.find({}, function(err, items) {
items.forEach(function(item) {
if(item == null) return
console.dir(item);
console.log("created at " + new Date(item._id.generationTime) + "\n")
})
callback(err, items)
})
},
function(callback) {
collection.drop(callback)
}
], callback)
}
}, function(error, results) {
// disconnect from DB
error && console.error(error)
results && results.db && results.db.close();
})
You can start by defining each of the callbacks in this nested system as functions instead. So the basic idea is instead of doing
action(data, function (err, value) { if (err) { ... } ... })
you do
action(data, namedCallback)
function namedCallback(err, value) {
if (err) { ... }
....
nextAction(value, nextNamedCallback)
}
Things you should note about this implementation
The variables that you want to share are defined in scope of all of the functions (see var db and var collection
In this implementation, all of the functions are callbacks, To use the series implementation, you need to split up the functions into functions which perform the action, not functions which handle the action when it is finished
You should handle the err case in your callbacks properly
var MongoClient = require('../lib/mongodb').MongoClient
, format = require('util').format;
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ?
process.env['MONGO_NODE_DRIVER_HOST'] :
'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ?
process.env['MONGO_NODE_DRIVER_PORT'] :
27017;
console.log("Connecting to " + host + ":" + port);
MongoClient.connect(
format("mongodb://%s:%s/node-mongo-examples?w=1", host, port)
onConnect)
var db
var collection
function onConnect(err, _db) {
db = _db
db.dropDatabase(onDrop);
}
function onDrop(err, result) {
collection = db.collection('test');
// Erase all records from the collection, if any
collection.remove({}, onRemove);
}
function onRemove(err, result) {
// Insert 3 records
for(var i = 0; i < 3; i++) {
collection.insert({ 'a': i }, { w: 0 });
}
collection.count(onCount)
}
function onCount(err, count) {
console.log("There are",
count,
"records in the test collection. Here they are:");
collection.find().each(onFind);
}
function onFind(err, item) {
if (item != null) {
console.dir(item);
console.log("created at " + new Date(item._id.generationTime) + "\n")
}
// Null signifies end of iterator
if (item == null) {
// Destory the collection
collection.drop(onEnd)
}
}
function onEnd(err, item) {
db.close();
}
Related
I used MongoDB and wrote a connection So I want to connect the MongoDB database to my project but I still can not understand why it is not connected to the database?And how can I test connection?
I wrote my db.js file like below:
const mongodb = require("mongodb");
const connectionString =
'mongodb+srv://database_user:database_password#server";';
mongodb.connect(
connectionString,
{ useNewUrlParser: true, useUnifiedTopology: true },
function (err, client) {
module.exports = client.db();
const app = require("./app");
app.listen(3000);
}
);
And I used this db.js in Model/Users.js like below:
const usersCollection = require("../db").collection("users");
const validator = require("validator");
let User = function (data) {
this.data = data;
this.errors = [];
};
User.prototype.cleanUp = function () {
if (typeof this.data.username != "string") {
this.data.username == "";
}
if (typeof this.data.email != "string") {
this.data.email == "";
}
if (typeof this.data.password != "string") {
this.data.password == "";
}
// this.data = {
// username: this.data.username.trim().toLowerCase(),
// email: this.data.email.trim().toLowerCase(),
// password: this.data.password,
// };
};
//For bogus Properties
User.prototype.validate = function () {
if (this.data.username == "") {
this.errors.push("You Should insert username");
}
if (
this.data.username != "" &&
!validator.isAlphanumeric(this.data.username)
) {
this.errors.push("You can use Number and characters");
}
if (!validator.isEmail(this.data.email)) {
this.errors.push("You Should insert email");
}
if (this.data.password == "") {
this.errors.push("You Should insert password");
}
if (this.data.password.lenght > 0 && this.data.password.lenght < 12) {
this.errors.push(
"Password must be at least 3 Characters and maximum 12 characters."
);
if (this.data.password.lenght > 100) {
this.data.errors.push("Password In over qualified 100 Characters!!!");
}
if (this.data.username.lenght > 0 && this.data.username.lenght < 3) {
this.data.errors.push(
"username must be at least 3 Characters and maximum 3 characters."
);
}
if (this.data.username.lenght > 30) {
this.data.username.errors,
push("username In over qualified 30 Characters!!!");
}
}
};
User.prototype.register = function () {
//Step #1: Validate User data
this.cleanUp();
this.validate();
if (!this.errors.lenght) {
usersCollection.insertOne(this.data);
}
};
module.exports = User;
When I want to run the code I got an error in collection:
/Users/shamimi/Desktop/js/complex-app/models/User.js:1
const usersCollection = require("../db").collection("users");
the problem is you're not returning anything from db.js, you're connecting to mongo and starting express.
in my opinion you should separate db connection from express start, cause you're planning to use db from all your models and you wouldn't start the express server everytime. You should also consider creating one connection to the database only.
db.js could look like this:
const client = require("mongodb").MongoClient;
const config = require("../config");
let _db;
function initDb(callback) {
if (_db) {
console.warn("Trying to init DB again!");
return callback(null, _db);
}
client.connect(config.db.connectionString, config.db.connectionOptions, connected);
function connected(err, db) {
if (err) {
return callback(err);
}
console.log("DB initialized - connected to: " + config.db.connectionString.split("#")[1]);
_db = db;
return callback(null, _db);
}
}
function getDb() {
return _db;
}
module.exports = {
getDb,
initDb
};
Then you can use it like:
your main file would look like this:
const initDb = require("./db").initDb;
const getDb = require("./db").getDb;
const app = require("express")();
const port = 3001;
app.use("/", exampleRoute);
initDb(function (err) {
app.listen(port, function (err) {
if (err) {
throw err; //
}
console.log("API Up and running on port " + port);
});
);
function exampleRoute(req, res){
const db = getDb();
//Do things with your database connection
res.json(results);
}
PS
If this is a new app using a recent version of NodeJS you should look into ES6 and more modern ways to create classes, use async/await instead of callbacks
Add the .then and .catch like I did below and you can see if your connection was successful
mongodb.connect(
connectionString,
{ useNewUrlParser: true, useUnifiedTopology: true },
function (err, client) {
module.exports = client.db();
const app = require("./app");
app.listen(3000);
}
).then(() => console.log("DB Connected!"))
.catch(err => {
console.log(
"Error in DB connection : " + JSON.stringify(err, undefined, 2)
);
});
So I have files inside the following folder:
app/controller/token.js
app/controller/news.js
token.js:
"use strict";
var connection = require("../con");
exports.isTokenExists = function(token) {
var checkToken = "SELECT COUNT(`id`) AS 'total' FROM `user` WHERE `token` = '" + token + "'";
var isExists = false;
var count;
var checkResult;
connection.query(checkToken, function(error, rows) {
if (!error) {
checkResult = JSON.parse(JSON.stringify(rows));
for (var i = 0; i < checkResult.length; i++) {
var row = rows[i];
count = row.total;
}
if (count > 0) {
isExists = true;
}
}
});
return isExists;
};
news.js:
"use strict";
var response = require("../response/responses");
var connection = require("../con");
var getToken = require("./token");
exports.news = function(req, res) {
response.send(false, "News API", null, res);
};
exports.allNews = function(req, res) {
var checkTokenExists = getToken.isTokenExists("75d12cc4dc07608d5b87a6cba33cac056df1239c");
if (checkTokenExists) {
var allNewsQuery = "SELECT a.`id`, b.`title` AS `category`, a.`title`, a.`description`, a.`content`, a.`image`, a.`created_date` FROM `news` AS a LEFT JOIN `news_category` AS b ON a.`id_news_category` = b.`id` ORDER BY `created_date` DESC LIMIT 20";
connection.query(allNewsQuery, function(error, rows) {
if (error) {
response.send(true, "" + error, null, res);
} else {
var data = [];
var newsData = JSON.parse(JSON.stringify(rows));
for (var i = 0; i < newsData.length; i++) {
var row = rows[i];
data[i] = {
id: row.id,
idCategory: row.idCategory,
category: row.category,
title: row.title,
description: row.description,
image: row.image,
createdDate: row.created_date
};
}
response.send(false, "News is not empty", data, res);
}
});
} else {
response.send(true, "Error: Token not found", checkTokenExists, res);
}
};
I always getting false value from isTokenExists meanwhile the token is exists in the table.
How do I get true response if the token is exist and how do I get false response if the token is not exists in table?
Any help will be much appreciated.
Regards.
The issue here is that connection.query accepts a callback, but the rest of your code will move passed that without awaiting the result, which is why your isExists always returns false. You can fix this by encapsulating the query with a Promise like this:
"use strict";
const connection = require("../con");
exports.isTokenExists = async function(token) {
const checkToken = "SELECT COUNT(`id`) AS 'total' FROM `user` WHERE `token` = ?";
return new Promise((resolve, reject) => {
connection.query(checkToken, token, function (error, results) {
if (error) return reject(error);
return resolve(results.length > 0);
});
});
};
I also simplified the logic in the callback a bit.
Then, in news.js await the result like this:
exports.allNews = async function(req, res) {
getToken.isTokenExists("75d12cc4dc07608d5b87a6cba33cac056df1239c")
.then(result => {
if (result === true) {
//place your code for handling if the token exists here
}
else {
//place your code for handling if the token does not exist
}
})
.catch(err => {
//handle error
});
}
You are missing async / await concept. You need to wait until your query executes.
1) Write a promise function
export.getCount = function(query) {
return new Promise((res, rej) => {
let count = 0;
connection.query(checkToken, function(error, rows) {
if (!error) {
checkResult = JSON.parse(JSON.stringify(rows));
for (var i = 0; i < checkResult.length; i++) {
var row = rows[i];
count = row.total;
}
}
return res(count);
})
}
2) Write async function which supports await operations
exports.isTokenExists = async function(token) {
var query = "SELECT COUNT(`id`) AS 'total' FROM `user` WHERE `token` = '" + token + "'";
let count = await getCount(query)
return count > 0; // Returns true if count is > 0
};
I'm trying to understand why the debugger does not go into the callback function, it does not give any errors or any results... It just jump over connect.query!
When I run in the mySQL workbench the code runs and I get the data, so the query is not incorrect either.. (You can see the example from the creator of the mysql node package, I can't understand why I can't get into the callback)
SELECT * FROM eclasstree WHERE numberofdigits = '8';
Here is the part of the code that does not enter the callback, all the other SQL queries works above works just fine...
connection.query("SELECT * FROM eclasstree WHERE numberofdigits = '8'", function(error, results, fields) {
if (error) {
throw error;
}
console.log(results, fields);
});
index.js // route /generatewordsconnectedtoeclass
require("console-stamp")(console, "[HH:MM:ss.l]");
const express = require("express");
const router = express.Router();
const { Database, mySQLConnectionDetails, connection } = require("../helpers/mySQL");
const { cleanseString, cleanseStringToArray, generateCountDuplication, sortValuesBykey, generateScoreArray, calculateAverage, replaceAbbreviationWithExpansion } = require("../helpers/stringFixers");
const database = new Database(mySQLConnectionDetails);
router.get("/generatewordsconnectedtoeclass", (req, res) => {
const eClassArray = [];
let eClassCode = "";
connection.query("SELECT * FROM eclasstree WHERE numberofdigits = '8'", function(error, results, fields) {
results.forEach(eclassobj => {
eClassCode = eclassobj.code;
connection.query(`SELECT itemnumber, cleanname, eclass FROM partnumbersclassified WHERE eclass = '${eclassobj.code}'`, function(error, results, fields) {
let stringToArray = [];
let countObject = [];
if(results.length > 0) {
stringToArray = cleanseStringToArray(results, "cleanname");
}
if(stringToArray.length > 0) {
countObject = generateCountDuplication(stringToArray);
// console.log(countObject);
for (const property in countObject) {
if (countObject.hasOwnProperty(property)) {
// console.log(property + " : " + countObject[property] + " : " + eClassCode);
// console.log(property);
// const obj = {
// eclasscode: `${eClassCode}`,
// wordcount: `${countObject[property]}`
// };
// obj[property] = `${countObject[property]}`;
// const obj2 = obj;
// connection.query(`INSERT INTO wordsconnectedtoeclasscode (eclasscode, word, wordcount) VALUES (${eClassCode}, ${property}, ${countObject[property]})`, function(error, results, fields) {
// const post = {word: "Hello", wordcount: "1"};
// connection.connect();
connection.query("SELECT * FROM eclasstree WHERE numberofdigits = '8'", function(error, results, fields) {
if (error) {
throw error;
}
console.log(results, fields);
});
// connection.query("INSERT INTO posts SET ?", post, function(error, results, fields) {
// //connection.query("INSERT INTO wordsconnectedtoeclasscode (eclasscode, word, wordcount) VALUES ('12345123', 'balle', '2'})", function(error, results, fields) {
// if (error) throw error;
// console.log(fields);
// console.log(results);
// connection.end();
// });
}
}
}
});
});
});
res.json({});
});
module.exports = router;
If I do "step in-to" I can see the "sql" is defined with the query, but the "cb" is undefined!
With help of #Patrick Evans I solved it.
I read up on the docs for the mysql node package and they do not follow the standard mysql queries. So after I changed it, it worked!
const obj = {
eclasscode: `${eclassobj.code}`,
wordcount: `${countObject[property]}`,
word: property
};
connection.query("INSERT INTO wordsconnectedtoeclasscode SET ?", obj, function(error, results3) {
if (error) {
throw error;
}
console.log(results3.insertId);
});
require("console-stamp")(console, "[HH:MM:ss.l]");
const express = require("express");
const router = express.Router();
const { Database, mySQLConnectionDetails, connection } = require("../helpers/mySQL");
const { cleanseString, cleanseStringToArray, generateCountDuplication, sortValuesBykey, generateScoreArray, calculateAverage, replaceAbbreviationWithExpansion } = require("../helpers/stringFixers");
const database = new Database(mySQLConnectionDetails);
router.get("/generatewordsconnectedtoeclass", (req, res) => {
connection.query("SELECT * FROM eclasstree WHERE numberofdigits = '8'", function(error, results1) {
results1.forEach(eclassobj => {
connection.query(`SELECT itemnumber, cleanname, eclass FROM partnumbersclassified WHERE eclass = '${eclassobj.code}'`, function(error, results2) {
let stringToArray = [];
let countObject = [];
if(results2.length > 0) {
stringToArray = cleanseStringToArray(results2, "cleanname");
}
if(stringToArray.length > 0) {
countObject = generateCountDuplication(stringToArray);
for (const property in countObject) {
if (countObject.hasOwnProperty(property)) {
const obj = {
eclasscode: `${eclassobj.code}`,
wordcount: `${countObject[property]}`,
word: property
};
connection.query("INSERT INTO wordsconnectedtoeclasscode SET ?", obj, function(error, results3) {
if (error) {
throw error;
}
console.log(results3.insertId);
});
}
}
}
});
});
// res.json({});
});
});
module.exports = router;
I can't seem to make multiple requests from one connection to the database. It always tells me that requests can only be made from logged in state.
For example as seen in the code below: the getCarIdandOwner part of the function will fire fine. However the getChargeRate will not.
I tried combining them like so:
connection.execSqlBatch(getcarIdandOwner, getChargeRate);
However that did not work either as it told me that getChargeRate was not defined.
Using Visual Studio Community, have NPM: Underscore and Tedious (for sql) installed. Just running it as a console app for now to test.
var Connection = require('tedious').Connection;
var config = {
userName: 'user',
password: 'passs',
server: 'somewhere.database.windows.net',
options: {
encrypt: true,
database: 'db-Test',
useColumnNames: true
var connection = new Connection(config);
connection.on('connect', function (err) {
// If no error, then good to proceed.
console.log("Connected".green);
toll("******-b77c-40e0-8f26-d44e98bc7264", "be45c903-****-****-b6ba-4b2fefa3d6b0");
});
function toll(chipId, locId) {
var carId = '';
var userOwner = '';
var charge = '';
var userBalance = '';
getcarIdandOwner = new Request(`SELECT car_id, userOwner FROM Cars WHERE carChipId = '${chipId}'`, function (err) {
if (err) {
console.log(err);
}
});
getcarIdandOwner.on('row', function (columns) {
carId = columns.car_id.value;
userOwner = columns.userOwner.value;
console.log('carId: ', carId, ' userOwner: ', userOwner);
});
getcarIdandOwner.on('done', function (rowCount, more) {
console.log(rowCount + ' rows returned');
if (rowCount = 1) {
console.log('Car Rows Returned Ok'.green);
} else {
console.log('Fatal Error: More than 1 Car Row Returned'.red);
};
});
connection.execSqlBatch(getcarIdandOwner);
getChargeRate = new Request(`SELECT Charge FROM locations WHERE location_id = '${locId}'`, function (err) {
if (err) {
console.log(err);
}
});
getChargeRate.on('row', function (columns) {
charge = columns.charge.value;
console.log('Charging account: ', userOwner, '$', charge);
});
connection.execSqlBatch(getChargeRate);
}
There is some documentation at http://tediousjs.github.io/tedious/api-connection.html which states:
Only one request at a time may be executed on a connection. Once a
Request has been initiated (with callProcedure, execSql, or
execSqlBatch), another should not be initiated until the Request's
completion callback is called.
So your code should be someting like this:
function toll(chipId, locId) {
var carId = '';
var userOwner = '';
var charge = '';
var userBalance = '';
getcarIdandOwner = new Request(`SELECT car_id, userOwner FROM Cars WHERE carChipId = '${chipId}'`, function (err) {
if (err) {
console.log(err);
} else {
getChargeRate = new Request(`SELECT Charge FROM locations WHERE location_id = '${locId}'`, function (err) {
if (err) {
console.log(err);
}
});
getChargeRate.on('row', function (columns) {
charge = columns.charge.value;
console.log('Charging account: ', userOwner, '$', charge);
});
connection.execSql(getChargeRate);
}
});
getcarIdandOwner.on('row', function (columns) {
carId = columns.car_id.value;
userOwner = columns.userOwner.value;
console.log('carId: ', carId, ' userOwner: ', userOwner);
});
getcarIdandOwner.on('done', function (rowCount, more) {
console.log(rowCount + ' rows returned');
if (rowCount = 1) {
console.log('Car Rows Returned Ok'.green);
} else {
console.log('Fatal Error: More than 1 Car Row Returned'.red);
};
});
connection.execSqlBatch(getcarIdandOwner);
}
I m creating a job search portal using nodejs,mongodb,and ejs view engine :
collection "filters" = 'username' 'tags'
collection "alljobs" = 'category' 'jobsdata'
In the following code I'm fetching 'tags' from "filters" collection and comparing with all 'category' in "alljobs" collection. And then all 'tags' array matched with 'category' view their respective 'jobsdata' using ejs view engine.
Problem: Code is working but in browser I can't see all matched category respective jobs only one category jobs data viewed. I am not able to understand where is the problem?
Code:
function processRequest(req,res){
var tags,data,jobsdata = [];
var len;
if(condition)
{....}
else{
var db = new Db('askhere', new Server('localhost', '27017'));
db.open(function (err, db) {
db.authenticate('', '', function (err, result) {
var url = 'mongodb://localhost:27017/askhere';
client.connect(url, function (err, db) {
var col = db.collection('filters');
col.find({username:req.session.userName}).toArray(function (err, items) { // find tags of session user from collection filters
console.log('items: ' + JSON.stringify(items));
items.forEach(function (doc) {
tags = doc.tags; //save tags
});
var col = db.collection('alljobs'); //find all categories jobs matched with tags data in collection alljobs
for(var i=0; i<tags.length;i++){
col.find({category:tags[i]}).toArray(function (err, items1) {
if (items1 == false) {
res.render('mainqa',{uname:req.session.userName,tags:'No Tags Selected',jobsdata:'No Tags Matched !!!',len:0});
}
items1.forEach(function (doc1) {
jobsdata = doc1.jobsdata;
var html = ["url : ", "Posted Date : ", "Job Title : ", "Company : ", "Location : "]
for (var i = 0; i < 25; i++) {
for (var j = 0; j < 5; j++) {
data.push(html[j] + jobsdata[i][j]);
} //Nested for loop
} //for loop covert 2D array in 1D
res.render('mainqa',{uname:req.session.userName,tags:tags,jobsdata:data,len:len});
}); //forEach
}); //col.find collection=alljobs
} //for loop
}); //col.find collection=filters
}); //client connect
}); //db.authenticate
}); //db.open
} //else end
} //function processRequest end
You should stop using callbacks for nesting code of multiple methods unless you want to end up with the Callback Hell. Use the async module to simplify this task, in particular you'd need to use async.waterfall() since you want to run multiple tasks that depend on each other.
The async.waterfall() method allows you to pass values between the functions in a trickle-down manner. It is quite handy when you need the results of a previous function to perform an operation with the next function in the series.
Consider restructuring following this example:
var async = require("async"),
userName = req.session.userName,
locals = {},
url = 'mongodb://localhost:27017/askhere',
db = new Db('askhere', new Server('localhost', '27017'));
async.waterfall(
[
// Open db
function(callback) {
db.open(function (err, db) {
if (err) return callback(err);
callback(null, db);
});
},
// Authenticate and connect
function(db, callback) {
db.authenticate('', '', function (err, result) {
if (err) return callback(err);
client.connect(url, function (err, res) {
if (err) return callback(err);
locals.db = db;
callback(null, locals);
});
});
},
// Query filters collection for tags of session user
function(arg, callback) {
var collection = arg.db.collection("filters");
collection.find({"username": userName}).toArray(function (err, result) {
if (err) return callback(err);
locals.tags = result.map(function(item){ return item.tags; });
callback(null, locals);
});
},
// Query alljobs collection for categories jobs matched with tags data
function(arg, callback) {
var collection = arg.db.collection("alljobs");
collection.find({"category": {"$in": arg.tags}}).toArray(function (err, result) {
if (err) return callback(err);
locals.jobsdata = result.map(function (doc){ return doc.jobsdata; });
callback(null, locals);
});
}
], function(err, result) {
if (err) { /* handle err */ };
res.render("mainqa", {
"uname": userName,
"tags": result.tags,
"jobsdata": result.jobsdata
});
});
);