Passing extra parameters for mysql query callback function in node js - node.js

I am trying to run a mysql query using node js and in the callback function I want to pass some additional parameters.
for (var i = 0; i < categories.length; i++)
{
connection.query ('select * from products where category=' + categories[i] , function (err, products) {
if (products != undefined && products.length > 0)
{
// want to do some processing based on product data and category name
}
});
}
In the above code, how can I pass category name to the callback function? I know I can do a join in the sql query and that will give me the data in the products but just want to know if there is anyway of passing the data in the callback function.

Just use const. Also, never construct queries by simply concatenating strings with variables.
Example:
for (var i = 0; i < categories.length; i++)
{
const categoryName = categories[i];
connection.query('select * from products where category = ?',
[categoryName],
function (err, products) {
if (err)
throw err;
if (products.length > 0)
{
// use `categoryName`
}
});
}
You may also want to consider using a database connection pool instead of just a single connection (since all queries are queued and only one query can be executing at any given time per connection).

Related

Node Express js Query inside a loop

I am new in node js / express I am trying to query, foreach id in an array, some data from the sqlite3 database. My for loop looks like as follows
response.patients = results;
for (var i = 0; i < results.length; i++) {
response.patients[i].check = "false";
var patient = response.patients[i];
db.each("SELECT visit_id FROM patient_visits where patient_id='"+patient.id+"' AND visitdate >='"+moment().format('YYYY-MM-DD')+"'", function(err, row) {
if (row) {
response.patients[i].check = "true";
}
});
}
res.send(response);
the problem is that the for loop continues before the query is finished. Is there a way to check if the query has finished?
I only need to set a flag true / false.
The problem is that db.each is a callback function. This function executes and the response takes some time to be retrieved, however the system doesn't stop and the for loop continues. When it ends, res.send is called while the responses from the db.each are still being processed.
You can try
db.all(
SELECT visit_id FROM patient_visits where patient_id IN ?
AND visitdate >='"+moment().format('YYYY-MM-DD')+"'", response.patients,
function(err, rows) {
if (err) {
throw err;
}
rows.forEach((row) => {
response.patients[row.visit_id].check = "true";
});
res.send(response);
});
I'm not sure about the content of row variable to get the visit_id but before print the variable and check if it works.
If you have any doubt let me know. And I strongly advise you to read some content about callbacks and how they work and then promises and async/await because callbacks aren't used now, with async await, your problem would be solved easily.

callback in callback with waterfall in nodejs

I am using MEAN (Mongo Express Angulars NodeJs) for a project. The problem is I have to add one extra attribute to data received from query object. And make new data array with exactly old data array but have one extra attribute. I know how to add attribute and pass them into callback using waterfall model, as I am using multiple callback functions and for loops I am not able to get expected result.
code:
var fetchRevenue = function(restaurantArray, type, startDate, endDate, fn) {
_.forEach(restaurantArray, function(rest) {
fetchDateWiseReport(new Date('07/10/2015'), new Date('07/16/2015'), rest._id, type, function(orders) {
var newOrders = [];
async.waterfall([
function(callback) {
if(orders && orders.length > 0){
async.forEach(orders, function(order) {
getSellingPriceOfItems(order.orders, function(sp) {
order.sp = sp;
newOrders.push(order);
if (newOrders.length === orders.length)
callback(null, newOrders);
});
});
} else {
newOrders.push([]);
}
},
function(newOrders, callback) {
var restArr = []
//get sum of all orders of each restaurant and add into restArr
callback(null, restArr);
},
function(restArr, callback) {
callback(null, newOrders);
}
], function(err, result) {
fn(result);
});
});
});
};
where my functions:
fetchDateWiseReport = fetches restaurant record for given date and send result in callback
getSellingPriceOfItems = query to item model find price for each item and return selling price of given array and send result in callback.
my complete code including all functions is here.
now I want orders should be equal to newOrders with additional attibute 'sp'. But I am unable to get this. Will you suggest me something to proceed?
Use Express way to handle callback problem
in you route
app.get('you/route',fetchDateWiseReport(), second(),finalReturningREsult())
your first function will be doing first async loop function assiggn reult in req.body.firstResult and pass to second function. and so on

Multiple queries inside mongodb query

I'm having an issue when trying to query based on the result of another query on mongodb.
I'm trying to make an initial query and then do another query for each one of the result of the first query. The reason I'm doing it like this is because I have two different collections and I need to join some data from one collection with the data of the other collection. In a SQL world I could easily do this with a JOIN, but as I'm using mongodb in this one I can't really use JOINs, so I guessed doing a for loop inside the first query's callback function would be the way to go.
Here's the code I'm using...
var resultSet = [];
db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){
if(e || docs.length === 0) {
console.log("Sorry, wrong id.");
return e;
}
for(var m=0; m<docs.length; m++){
var auxRes = {};
auxRes.id_bus = docs[m].id_bus;
auxRes.id_bus_stop = docs[m].id_bus_stop;
auxRes.coord_x = docs[m].coord_x;
auxRes.coord_y = docs[m].coord_y;
auxRes.id_bus_variation = docs[m].id_bus_variation;
db.get('buscollection').find({id_bus: parseInt(docs[m].id_bus)}, function(e, busDocs){
auxRes.s_origin_description = busDocs[0].s_origin_description;
auxRes.s_destination_description = busDocs[0].id_destination_description;
resultSet.push(auxRes);
});
res.send(JSON.stringify(resultSet));
}
});
I need to res.send the resultSet array after all the values have been added.
I've tried some other ways of doing this, but the thing is that when the res.send line is reached the second query hasn't finished at all. I also tried doing that inside the inner query's callback, but I need to check if it's the last in the for loop, and checking the value o m won't do it as it always is equivalent to docs.length.
As far as I know there's no such thing as a synchronous query in mongodb, but maybe I'm wrong.
What's the right way of doing this?
EDIT
I found a way around it, but I'm sure there's got to be a better way. Here's how I'm doing it...
db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){
if(e || docs.length === 0) {
console.log("Ha ocurrido un error, no existe esa parada");
return e;
}
var busIDs = [];
for(var m=0; m<docs.length; m++){
busIDs.push(parseInt(docs[m].id_bus));
var auxRes = {};
auxRes.id_bus = docs[m].id_bus;
auxRes.id_bus_stop = docs[m].id_bus_stop;
auxRes.coord_x = docs[m].coord_x;
auxRes.coord_y = docs[m].coord_y;
auxRes.id_bus_variation = docs[m].id_bus_variation;
resultSet.push(auxRes);
}
db.get('buscollection').find({id_bus: {$in: busIDs}}, function(e, busDocs){
for(var n = 0; n<busDocs.length; n++){
for(var k=0; k<resultSet.length; k++){
if(resultSet[k].id_bus == busDocs[n].id_bus){
resultSet[k].s_origin_description = busDocs[n].s_origin_description;
resultSet[k].s_destination_description = busDocs[n].id_destination_description;
}
}
}
res.send(JSON.stringify(resultSet));
});
});
Node.js behavior is asynchronous , programmer has to code taking consideration of this behavior. Use callbacks or promises or a flow control library . In your your program , you
have put mongo query inside loop , which is a bad approach of querying . Instead if querying multiple times , use $in operator . It will optimize your code performance and
solves your response sending problem also.
var resultSet = [];
db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){
if(e || docs.length === 0) {
console.log("Sorry, wrong id.");
return e;
}
var bus_ids = [];
for(var m=0; m<docs.length; m++){
var auxRes = {};
auxRes.id_bus = docs[m].id_bus;
bus_ids.push(parseInt(docs[m].id_bus)); // collect all ids
auxRes.id_bus_stop = docs[m].id_bus_stop;
auxRes.coord_x = docs[m].coord_x;
auxRes.coord_y = docs[m].coord_y;
auxRes.id_bus_variation = docs[m].id_bus_variation;
resultSet.push(auxRes);
}
// Query at one time for all document
db.get('buscollection').find({id_bus: {$in : bus_ids}}).toArray( function(e, busDocs){
// Now find and merge in one go
busDocs.forEach(function(eachBusDoc){
for(var i=0,len = resultSet.length;i< len;i++){
if(resultSet[i].id_bus == busDocs.id_bus ){
resultSet[i].s_origin_description = eachBusDoc.s_origin_description;
resultSet[i].s_destination_description = eachBusDoc.id_destination_description;
}
}
});
res.send(JSON.stringify(resultSet));
});
});
Your updated solution in your question is generally fine, as using $in is an excellent way of fetching a set of results (you'll want to make sure that you've indexed the id_bus property).
Here are a few tweaks (with a bit of cleanup and optimization):
db.get('busstopcollection')
.find({id_bus_stop: parseInt(req.body.busstopid)}).toArray(function(e, docs){
var auxById = {}; // store a dictionary of all the results for later
if(e || docs === null || docs.length === 0) {
console.log("Ha ocurrido un error, no existe esa parada");
return e;
}
var busIDs = [];
docs.forEach(function(doc) {
busIDs.push(parseInt(doc.id_bus));
// consider just using the doc directly rather than copying each property
// especially if you're not manipulating any of the data as it passes
var auxRes = {
id_bus : doc.id_bus,
id_bus_stop : doc.id_bus_stop,
coord_x : doc.coord_x,
coord_y : doc.coord_y,
id_bus_variation : doc.id_bus_variation
};
// you could just use each doc directly ...
// var auxRes = doc; ??
// ** importantly, store off the id_bus for each one so you can
// ** avoid a costly loop trying to match an id below.
auxById[doc.id_bus] = auxRes;
resultSet.push(auxRes);
});
// might want to consider using a cursor ... here's an example
db.get('buscollection')
.find({id_bus: {$in: busIDs}}).each(function(e, busDoc){
// the last item in the cursor will be null
if (busDoc === null) {
res.send(JSON.stringify(resultSet));
return;
}
var res = auxById[busDoc.id_bus];
if (res) { // did we find it in our dictionary of results?
// yes, we did == copy the necessary field data
res.s_origin_description = busDoc.s_origin_description;
res.s_destination_description = busDoc.id_destination_description;
}
});
});

How to break out of the for loop in nodejs

Well the problem is simple, I am using a query within the for loop and and I want to get out of the for loop if I get the count less than 15, otherwise increase the assigned. But I can't be able to use the break statement and the loop will continue to execute even after the first callback.
for (var i = 0; i < test; i++) {
var sql = "SELECT count(*) as count FROM `tb_members` WHERE `assigned`=?";
connection.query(sql, [assigned], function (err, response) {
if (response[0].count < 15) {
callback(assigned);
}
else {
++assigned;
if (i == test - 1) {
callback(0);
}
}
});
}
The way your code is written, all your SQL queries are going to get started at once. Then, sometime later, the queries will start returning with results. So, you can't break out of the for loop because it's already done and all SQL queries have already been sent.
If you want to decide whether to send the next query based on the previous one's results, then you have to only send one at a time and because of the async nature of the results, you can't use a for loop for that.
One way of sending the queries one at a time and then deciding whether to send the next one is this:
function sendQueries() {
var i = 0;
function next() {
if (i < test) {
var sql = "SELECT count(*) as count FROM `tb_members` WHERE `assigned`=?";
connection.query(sql, [assigned], function (err, response) {
i++;
if (response[0].count < 15) {
callback(assigned);
} else {
++assigned;
if (i == test - 1) {
callback(0);
}
}
// here you can decide whether you want to do the next iteration
// or not by either calling next() or not.
next();
});
}
}
next();
}
You should be using async to write such a loop. If you want a serial behavior - you might consider using async.eachSeries, and then you can stop the loop by calling the callback function with error. At that point the loop will not execute anymore. But in the error handler - ignore the err.
Check it out here: https://github.com/caolan/async#collections

Check if a document exists in mongoose (Node.js)

I have seen a number of ways of finding documents in mongoDB such that there is no performance hit, i.e. you don't really retrieve the document; instead you just retrieve a count of 1 or 0 if the document exists or not.
In mongoDB, one can probably do:
db.<collection>.find(...).limit(1).size()
In mongoose, you either have callbacks or not. But in both cases, you are retrieving the entries rather than checking the count. I simply want a way to check if a document exists in mongoose — I don't want the document per se.
EDIT: Now fiddling with the async API, I have the following code:
for (var i = 0; i < deamons.length; i++){
var deamon = deamons[i]; // get the deamon from the parsed XML source
deamon = createDeamonDocument(deamon); // create a PSDeamon type document
PSDeamon.count({deamonId: deamon.deamonId}, function(error, count){ // check if the document exists
if (!error){
if (count == 0){
console.log('saving ' + deamon.deamonName);
deamon.save() // save
}else{
console.log('found ' + deamon.leagueName);
}
}
})
}
You have to read about javascript scope. Anyway try the following code,
for (var i = 0; i < deamons.length; i++) {
(function(d) {
var deamon = d
// create a PSDeamon type document
PSDeamon.count({
deamonId : deamon.deamonId
}, function(error, count) {// check if the document exists
if (!error) {
if (count == 0) {
console.log('saving ' + deamon.deamonName);
// get the deamon from the parsed XML source
deamon = createDeamonDocument(deamon);
deamon.save() // save
} else {
console.log('found ' + deamon.leagueName);
}
}
})
})(deamons[i]);
}
Note: Since It includes some db operation, I am not tested.
I found it simpler this way.
let docExists = await Model.exists({key: value});
console.log(docExists);
Otherwise, if you use it inside a function, make sure the function is async.
let docHandler = async () => {
let docExists = await Model.exists({key: value});
console.log(docExists);
};
You can use count, it doesn't retrieve entries. It relies on mongoDB's count operation which:
Counts the number of documents in a collection.
Returns a document that contains this count and as well as the command status.

Resources