Fetched data from mongodb viewing using ejs trouble - javascript

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

Related

How to update a global variable with a sqlite query in javascript?

I want to update a globally declared variable after sqlite query,
but I cant get it to work,
I have read that it might be related to asynchronous functions but I have no idea how to implement callbacks and stuff in this example,
can you guys help please. Here is the code:
const sqlite3 = require('sqlite3').verbose();
const dbPath = './src/db/db.sqlite3';
let db = new sqlite3.Database(dbPath, (err) => {
if (err) {
console.error(err.message);
}
console.log('Connected to database.');
});
let number = null;
let rowsExist = null;
db.get("select count(*) from PRICE", [], (err, row) => {
if (err) {
console.error(err.message)
}
else {
rowsExist = Object.values(row)[0];
console.log(rowExist) //this works but outside the function it doesnt get updated
}
});
// here rowExist remains the same after query
if (rowsExist === null) {
number = 1
}
else {
db.get("SELECT number FROM PRICE ORDER BY number DESC LIMIT 1", [], (err, row) => {
if (err) {
console.error(err.message)
}
else {
number = Object.values(row)[0] + 1
}
})
};

How to perserve array value after multiple async callback in node js?

I am trying to execute multiple callback and at the same time storing value in array , but at the end array return empty.
Here is my code :
var sheetData = [];
async.forEachSeries(req.body.data, function (data, cb) {
sheet.find({accountid: req.body.id}, function (err, doc) {
if (doc == '') {// get the next worksheet and save it
var sheet = new sheet({
accountid: req.body.id,
sheetid: data.sheetid
});
var jsonData = {};
jsonData.sheetid = data.sheetid;
sheet.save(function (err, doc) {
if (!err) {
sheetData.push(jsonData); // trying to push in array , success
console.log("-----sheet data---- : ", sheetData);// data available here
}
});
}
});
cb();
}, function () {
console.log("-----sheet data---- : ", sheetData);// empty array
});
Where I am doing wrong? Can anyone suggest me ?
Or, If any other alternative in nodejs.
Thanks
The callback is being called early. Try following:
var sheetData = [];
async.forEachSeries(req.body.data, function (data, cb) {
sheet.find({accountid: req.body.id}, function (err, doc) {
if (!doc) {
return cb (); //sheet exists, call back early
}
// get the next worksheet and save it
var sheet = new sheet({
accountid: req.body.id,
sheetid: data.sheetid
});
var jsonData = {};
jsonData.sheetid = data.sheetid;
sheet.save(function (err, doc) {
if (!err) {
sheetData.push(jsonData); // trying to push in array , success
console.log("-----sheet data---- : ", sheetData);// data available here
cb (); // all done, now we can call back
}
});
});
}, function () {
console.log("-----sheet data---- : ", sheetData);// lots of sheets
});

Node JS & SQL on request per connection?

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

Real world example of caolan's async library series method

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

JavaScript / mongoose - Loop through an array to create array of Objects

At the moment I have a list of Team ID's which I have stored in the array teamIds. I also have a function findByKey in my Team mongoose schema which finds a Team based on its key. As you can see from the code I have to put each new Team.findByKey inside the previous one, if I put it outside of the previous findByKey it doesn't function correctly.
I'm having problems putting the Team.findByKey methods into some sort of for loop which can loop through the array teamIds and for each Team in the array. I am developing the function to create a new tournament, the idea is that you pass an array of teamIds to here, and it loops through each ID, searching for that Team and adding it to a teamsList array in order to create the tournament.
I need to put the last bit of code to create the array of Team objects into the last Team.findByKey method which is my problem. How would I fix this so that I can pass it n amount of teams and find each team and store it as an array of Teams?
This is my code:
app.get('/tournament', function(req, res){
function genMatches(nTeams) {
var matchArray = [];
while (nTeams > 1) {
nTeams = nTeams >> 1;
var matches = [];
for (var i = 0; i < nTeams; ++i) {
matches.push([]);
}
matchArray.push(matches);
}
return matchArray;
}
var teamIds = [
1364472344972,
1363173222886,
1363007586845,
1363007557484
]
var tournamentName = 'My Tournament';
Team.findByKey(teamIds[0], function(err, team1) {
if(err) {
util.log("Error occured");
}
if(!team1) {
util.log("The team does not exist");
}
Team.findByKey(teamIds[1], function(err, team2) {
if(err) {
return util.log("Error occured");
}
if(!team2) {
return util.log("The team does not exist");
}
Team.findByKey(teamIds[2], function(err, team3) {
if(err) {
return util.log("Error occured");
}
if(!team3) {
return util.log("The team does not exist");
}
Team.findByKey(teamIds[3], function(err, team4) {
if(err) {
return util.log("Error occured");
}
if(!team4) {
return util.log("The team does not exist");
}
var teamList = [
team1._id,
team2._id,
team3._id,
team4._id
];
var numRounds = Math.log(teamList.length)/Math.log(2);
var matches = genMatches(teamList.length);
for(var i = 0; i < matches[0].length; i++){
matches[0][i] = [ teamList[i+i], teamList[i+(i+1)] ]
}
var tournament = new Tournament({
name: tournamentName,
teams: teamList,
rounds: numRounds,
matches: matches
});
res.send(tournament);
});
});
});
});
});
Team schema:
'use strict';
var util = require('util');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var validatePresenceOf = function(value){
return value && value.length;
};
var toLower = function(string){
return string.toLowerCase();
};
var getId = function(){
return new Date().getTime();
};
/**
* The Team schema. we will use timestamp as the unique key for each team
*/
var Team = new Schema({
'key' : {
unique : true,
type : Number,
default: getId
},
'name' : { type : String,
validate : [validatePresenceOf, 'Team name is required'],
index : { unique : true }
}
});
/**
* Get complete team details for all the teams
*/
Team.statics.getAll = function(cb){
var query = this.find({});
query.sort({key : -1});
return query.exec(cb);
};
Team.statics.findByKey = function(key, cb){
return this.find({'key' : key}, cb);
};
Team.statics.findByName = function(name, cb) {
return this.find({'name' : name}, cb);
};
module.exports = mongoose.model('Team', Team);
You can use the $in operator to look up your four teams in one query:
Team.find({key: {$in: teamIds}}, function (err, teams) {
...
});
If for some reason you need to call findByKey multiple times instead of collapsing it all into a single $in, look at using the async library's each method to do that more cleanly.

Categories

Resources