synchronous loop in nodejs - node.js

//Importing: postges DB connection
var pg = require('pg');
var conString = "postgres://readxxx:p#ssword#vmwoxxx-tst:8888/worxxx";
var prvsiteid = '';
var cursiteid = '';
var qurystring = '';
pg.connect(conString, function(err, client, done) {
if (err) {
return console.error('error fetching client from pool', err);
return;
}
client.query("select site_id,created_at,started_at,completed_at,notes,finish_code from _background_tasks where finish_code > 0 and site_id > 0 and abs(extract(Epoch from (now()::timestamp without time zone - completed_at)))/60 <= 4600 order by site_id asc", function(err, result1) {
done();
if (err) {
return console.error('error running query', err);
return;
}
for (var i = 0; i < result1.rowCount; i++) {
cursiteid = result1.rows[i].site_id;
if (prvsiteid != cursiteid) {
prvsiteid = cursiteid;
qurystring = "select trim(su.name) as name, case When trim(su.email) is null then su.name || '#netapp.com' when trim(su.email) > '' then su.name || '#netapp.com' else su.name end uemail,su.friendly_name as frdname from system_users su where su.state = 'active' and su.id in (select distinct(system_user_id) from users u where u.site_id = " + cursiteid + "and u.admin_level >= 5)"
client.query(qurystring, function(err, result2) {
done();
if (err) {
return console.error('error running query', err);
return;
}
for (var j = 0; j < result2.rowCount; j++) {
console.log(cursiteid, result2.rows[j].name, result2.rows[j].uemail, result2.rows[j].frdname);
}
});
}
console.log(result1.rows[i].site_id, result1.rows[i].created_at, result1.rows[i].started_at, result1.rows[i].completed_at);
}
});
});
I know NodeJS programs are asynchronous but this scenario I intend it to be synchronous.
for loop(outer) --> for loop(inner) when outer forloop changes with new site id i want to send email to all the emailids from inner loop and also the resultant rows of the each site of outer loop has to printed.

If the library you are using doesn't support synchronous operations, you can't use it synchronously without abominations (You do not want abominations in your code. No matter how much the boss is pressing).
You can fairly simply perform loops and other actions on asynchronous operations by using Promises, specifically, bluebird which supports the ability to take callback-based asynchronous functions and transform them into Promise-ready functions.

Related

Can't set headers after they are sent to the client [ERR_HTTP_HEADERS_SENT]

I understood why it was an error, I tried the suggestions like return or do function aysnc, but I could not solve it.
I have documents as long as find.lenght and 100 sub-documents. That's why I am use two loop. When first iteration is complete and second matching value is found, server is crashed. Without server it works.
I have documents called 0,1,2 and there are 100 crypto coins records from 0 to 100 in them. I added an image. For example, USDT is at [0,3], [1,43], [2,13] and I want to send all of three.
app.post('/', (req, res) => {
print(req, res)
})
function print(req, res) {
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("cryptoDb");
dbo.collection("coinTable").find({}).toArray(function (err, find) {
if (err) throw err;
for (i = 0; i < find.length; i++) {
let found = false;
for (j = 0; j < 100; j++) {
//console.log(i + " " + j)
let id = Capitalize(req.body.coinName);
if (find[i].result[j].name == id || find[i].result[j].symbol == id.toUpperCase()) {
// console.log(find[i].result[j]);
res.send(find[i].result[j]);
found = true;
}
}
if (!found) {
console.log("Not found")
}
}
db.close();
});
});
function Capitalize(s) {
return s[0].toUpperCase() + s.slice(1).toLowerCase();
}
}
Thank you so much !
This error comes from attempting to send more than one response to the same http request.
You have res.send(find[i].result[j]); inside a for loop and you do not stop the inner loop after sending a response.
Thus, this code is capable of attempting to send multiple responses to the same request which you cannot do.
It's unclear from the code exactly what the desired solution is. If you only want to send the first response, then you can close the db and return after you send a response which will terminate both for loops.
If you intend to send multiple pieces of data, then accumulate the data you want to send in an array and send all the data once after all the loops are done.
If you're trying to send an array of all matching results, you can do this:
app.post('/', (req, res) => {
print(req, res)
})
function print(req, res) {
MongoClient.connect(url, function(err, db) {
if (err) {
console.log(err);
res.sendStatus(500);
return;
}
var dbo = db.db("cryptoDb");
dbo.collection("coinTable").find({}).toArray(function(err, find) {
if (err) {
console.log(err);
res.sendStatus(500);
db.close();
return;
}
const id = Capitalize(req.body.coinName);
const idUpper = id.toUpperCase();
const results = [];
for (let findItem of find) {
for (let j = 0; j < 100; j++) {
if (findItem.result[j].name == id || findItem.result[j].symbol == idUpper) {
results.push(findItem.result[j]);
}
}
}
res.send(results);
if (!results.length) {
console.log("No results");
}
db.close();
});
});
function Capitalize(s) {
return s[0].toUpperCase() + s.slice(1).toLowerCase();
}
}
Other Notes:
I changed the if (err) { ... } handling on your database call to actually send an error response. All paths through your request handler need to send a response or cause a response to be sent.
The hard coded loop from 0 to 99 is a bit odd as you don't check if the .result array actually has that many entries in it. That could result in a run-time error if the data isn't exactly how you are expecting it.
You don't have any validation of the req.body data you are expecting. All data arriving from the user should be validated before assuming it is what you are expecting.
You have res.send inside of your for loop, im assuming you want it to quit once its done, so add break to your loop.
var senddata = [];
for (j = 0; j < 100; j++) { // <-- For loop, it will send multiple times
// console.log(i + " " + j)
let id = Capitalize(req.body.coinName);
if (find[i].result[j].name == id || find[i].result[j].symbol == id.toUpperCase()) {
// console.log(find[i].result[j]);
senddata[senddata.length] = find[i].result[j]); // Add to array
found = true;
}
if (!found) {
console.log("Not found")
}
}
res.send(JSON.stringify({senddata})); // Send unparsed data

How to call subsequent callbacks in loop?

In my Nodejs application I need to select a set of data from the database by some queries. An amount of queries not defined at the start and I need to check after each DB query whether I need to make another query or not. So the process like this
var total_result = [];
var n = 0;
db.query('.....query...', function(result, error){
// Callback with db data response
total_results.push(...result...);
n += result.length;
if (n < req_n) {
// ... Here is the code to repeat the same query ...
// ?????
}
});
Thank you.
var total_result = [],
n = 0
while(await query())
async function query() {
return new Promise((resolve, reject) => {
db.query('...query...', (result, error) => {
if(error) reject(error)
// Callback with db data response
total_results.push(...result...)
n += result.length
if (n < req_n) resolve(true)
else resolve(false)
})
})
}

nodejs get sqlite3 query result using promise or wait

This is my first personal project in Nodejs. I'm trying to get in live soon.
I have a Nodejs server that uses sqlite3. There are only 3000 rows with word, transform and a precalculated value each in a column of the table, which is already populated.
I need to just lookup the word in the DB to be sure it is valid.
var sqlite3 = require("sqlite3").verbose();
var db = new sqlite3.Database("validate.db");
db.get("SELECT * FROM tab WHERE w = ?", word, function(err, row) {
if(err) { console.log("Lookup:",word,", Error => ",err); return false; }
return true;
});
The problem is that the caller of this code has a lot of context and need the operation to wait. So, I tried this
function dbLookup(db, w) {
return function(cb) {
var rows = [];
db.exec('SELECT w FROM tab WHERE w = "'+w+'"')
.on('row', function(r) {
rows.push(r)
})
.on('result', function() {
cb(rows);
});
}
async.each([word], function(w) {
dbLookup(this.db, w);
}, function(err) {
if(err) {console.log("...ERROR..."); return false; }
else {console.log("...SUCCESS..."); return true; }
});
This doesn't solve the wait issue as the callback can fire at its own pace.
I read that promise using something like bluebird can solve my problem
but now I'm not able to get the value/result of the query out:
I've been pulling my hair for so long. Please help me either get the async working or get the result back from the promise approach.
var async = require('async');
var sqlite3 = require("sqlite3").verbose();
var db = new sqlite3.Database("validate.db");
function check(word, callback) {
db.get("SELECT count(1) cnt FROM tab WHERE w = ?", word, callback)
}
async.map(words, check, function(err, results) {
if (err)
return console.log('Query error')
var all_checked = results.filter(function(r) {
return r.cnt > 0
});
...
});
Or
var sqlite3 = require("sqlite3").verbose();
var db = new sqlite3.Database("validate.db");
db.all("SELECT distinct w FROM tab", function(err, rows) {
var all_checked = words.filter(function (w) {
return rows.indexOf(w) != -1;
})
...
})

callback is not a function node js

I am new to javascript and i am having trouble solving this error. I get the message: "callback is not a function" at:"return callback(rolesArray)".
Rol.getAllRoles = function(callback){
sql = "select role from Role;";
var rolesArray = [];
var role;
mysql.connection(function(err,conn){
if (err){
return callback(err);
}
conn.query(sql,function(err,rows){
if (err){
return callback(err);
}
for(var i=0; i < rows.length; i++){
role = rows[i].role;
rolesArray.push(rol);
}
console.log("roles: " + rolesArray);
return callback(rolesArray);
});
});
}
The console.log outputs:"roles: admin,customer" so the connection with the database works.
That error means that you are not passing a function to Rol.getAllRoles(fn) when you call it.
In addition, so that you can have proper error handling in your callback and so you can more easily distinguish between an error and the actual data, you should always pass a first argument to the callback that indicates whether there was an error or not and then the second argument (if not an error) can be your results array like this:
Rol.getAllRoles = function(callback){
sql = "select role from Role;";
var rolesArray = [];
var role;
mysql.connection(function(err,conn){
if (err){
return callback(err);
}
conn.query(sql,function(err,rows){
if (err){
return callback(err);
}
for(var i=0; i < rows.length; i++){
role = rows[i].role;
rolesArray.push(rol);
}
console.log("roles: " + rolesArray);
// make sure the first argument to the callback
// is an error value, null if no error
return callback(null, rolesArray);
});
});
}
And, then you should be calling it like this:
Rol.getAllRoles(function(err, rolesArray) {
if (err) {
// handle error here
} else {
// process rolesArray here
}
});
This style of calling an async callback as in callback(err, data) is a very common async callback design pattern. It allows all callers to see if there was an error or not and if there was no error to get access to the final result.
I'd suggest the following:
Rol.getAllRoles = function(callback){
var sql = "select role from Role;";
var rolesArray = [];
var role;
callback = callback || function(){};
mysql.connection(function(err,conn){
if (err){
return callback(err);
}
conn.query(sql,function(err,rows){
if (err){
return callback(err);
}
for(var i=0; i < rows.length; i++){
role = rows[i].role;
rolesArray.push(rol);
}
console.log("roles: " + rolesArray);
return callback(rolesArray);
});
});
}
This way you enforce that callback is always a function. If you run it like Rol.getAllRoles() then you would get an error previously. Now you wont. You wont get any data back though.
Make sure you are calling Rol.getAllRoles with the proper parameter (ie: a function).

How to efficiently close the db connection (mongodb and node)

I have a small program that reads each of the record and update each of the record. Given the async nature of node and callback. what is the efficient and the correct way to close the db connection?
Sample Program:
var MongoClient = require('mongodb').MongoClient;
var updateCount = 0;
MongoClient.connect('mongodb://localhost:27017/school', function(err, db) {
if(err) throw err;
var query = { };
// get all the students in the database
var cursor = db.collection('students').find(query);
cursor.each(function(err, doc) {
if(err) throw err;
if(doc == null) {
return;
}
// filter out only the homework scores
var homeworksOnly = doc.scores.filter(function(scores){
if (scores.type === "homework") return true;
return false;
})
// filter out the non homework scores
var notHomeWorks = doc.scores.filter(function(scores){
if (scores.type !== "homework") return true;
return false;
})
// sort the homework score to remove the min score from the list.
homeworksOnly.sort(function(a,b){
if (a.score > b.score) return 1;
if (b.score > a.score) return -1;
return 0;
});
console.log("Before removing the min score"+doc._id);
console.dir(homeworksOnly);
console.log("After removing the min score"+doc._id);
homeworksOnly.splice(0,1);
console.dir(homeworksOnly);
console.log("Merge the homework with other scores"+doc._id);
var newScores = homeworksOnly.concat(notHomeWorks);
console.dir(newScores);
console.log("*****");
// Now update the database for this student with the new scores
var search = {"_id":doc._id};
var operator = { '$set' : { 'scores' : newScores } };
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
});
});
});
Now the program works but I need to hit the Ctrl+C to terminate the program. Is there a way to know that all the callbacks have completed so that the program can be terminated?
There are better libaries you can integrate with nodejs to handle the callback flow better, but simply working with the basic driver as a dependency, all you need is the basic node stream interface which is already built in to the cursor.
This allows .pause() and .resume()for flow control when processing, and an "end" event when the cursor stream is complete:
var MongoClient = require('mongodb').MongoClient;
var updateCount = 0;
MongoClient.connect('mongodb://localhost:27017/school', function(err, db) {
if(err) throw err;
var query = { };
// get all the students in the database
var cursor = db.collection('students').find(query);
// called on errors
cursor.on("error",function(err) {
throw err;
});
// called on stream complete
cursor.on("end",function() {
db.close();
});
// process each document in the stream
cursor.on("data",function(data) {
cursor.pause(); // stops the cursor stream while processing
// filter out only the homework scores
var homeworksOnly = doc.scores.filter(function(scores){
if (scores.type === "homework") return true;
return false;
})
// filter out the non homework scores
var notHomeWorks = doc.scores.filter(function(scores){
if (scores.type !== "homework") return true;
return false;
})
// sort the homework score to remove the min score from the list.
homeworksOnly.sort(function(a,b){
if (a.score > b.score) return 1;
if (b.score > a.score) return -1;
return 0;
});
console.log("Before removing the min score"+doc._id);
console.dir(homeworksOnly);
console.log("After removing the min score"+doc._id);
homeworksOnly.splice(0,1);
console.dir(homeworksOnly);
console.log("Merge the homework with other scores"+doc._id);
var newScores = homeworksOnly.concat(notHomeWorks);
console.dir(newScores);
console.log("*****");
// Now update the database for this student with the new scores
var search = {"_id":doc._id};
var operator = { '$set' : { 'scores' : newScores } };
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
cursor.resume(); // restarts the stream processing now we are done
});
});
});
After the update statement is done use
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
});
db.close();

Resources