Error: Callback was already called - async node.js oracledb - node.js

Hi I am getting this error when the query returns 0 results after filtering.
How do I go about fixing that?
This is my code.
> router.post('/report/', jsonParser, function (req, res) {
var data = req.body,
filteredID = data.filteredID,
startRow = data.startRow,
numRows = data.numRows,
sortCol = data.sortCol,
sortDir = data.sortDir;
var countQuery = 'SELECT COUNT(*) ' +
'FROM this_view ' ;+
var query = 'SELECT * +
'FROM this_view ' ;
var seg,
orderBy,
offset;
if(filteredID !== null && filteredID !== ""){
seg = ' WHERE "ID" LIKE \'%'+ filteredID + '%\'';
query += seg;
countQuery += seg;
}
orderBy = ' ORDER BY UPPER(' + sortCol + ') ' + sortDir;
offset = ' OFFSET ' + startRow + ' ROWS FETCH NEXT ' + numRows + ' ROWS ONLY';
query += orderBy;
query += offset;
logger.info("Begin: " + (new Date().toString()));
async.parallel({
rows: function (callback) {
pool.getConnection(function (err, connection) {
logger.info("Begin Connection: " + (new Date().toString()));
if (err) {
logger.error(err.message);
return;
}
logger.info("Begin execute: " + (new Date().toString()));
connection.execute(
query,
[],
{
resultSet: true,
prefetchRows: 1000
},
function (err, result) {
logger.info("End execute: " + (new Date().toString()));
if (err) {
logger.error(err.message);
callback("Something broke in the first thing");
doRelease(connection);
return;
}
function fetchRowsFromRS(connection, resultSet, numRows) {
result.resultSet.getRows(
numRows, // get this many rows
function (err, rows) {
if (err) {
logger.error(err);
doClose(connection, resultSet); // always close the result set
} else if (rows.length >= 0) {
var procJson = [];
/**
* For each row in the result, pushes a new object to the rows array
* In each new object, the key is assigned and the result row value set
*/
for (var i = 0; i < rows.length; i++) {
procJson.push({});
for (var j = 0; j < resultSet.metaData.length; j++) {
procJson[i][resultSet.metaData[j].name.toLowerCase()] = rows[i][j];
}
}
//TODO: Add null handling
logger.info("Send JSON: " + (new Date().toString()));
callback(null, procJson);
logger.info("JSON Sent: " + (new Date().toString()));
if (rows.length === numRows) // might be more rows
fetchRowsFromRS(connection, resultSet, numRows);
else
doClose(connection, resultSet); // always close the result set
} else { // no rows
doClose(connection, resultSet); // always close the result set
}
});
}
fetchRowsFromRS(connection, result.resultSet, numRows)
});
});
},
totalRows: function (callback) {
pool.getConnection(function (err, connection) {
logger.info("Begin Connection: " + (new Date().toString()));
if (err) {
logger.error(err.message);
return;
}
logger.info("Begin execute: " + (new Date().toString()));
connection.execute(
countQuery,
function (err, result) {
logger.info("End execute: " + (new Date().toString()));
if (err) {
logger.error(err.message);
callback("Something broke");
doRelease(connection);
return;
}
logger.info("Send JSON: " + (new Date().toString()));
console.log(result.rows);
callback(null, result.rows[0][0]);
logger.info("JSON Sent: " + (new Date().toString()));
doRelease(connection);
});
});
}
}, function(err, result){
if(err){
logger.error(err);
}
res.send(result);
});
});
If rows.length >= 0, I get this error. If I just use "rows.length > 0" and if 0 results come up after query, it fails.
Is there any other alternative to async.parallel that might fix this issue?
Kindly help.

Related

How to make a function synchronous in nodejs

I made this function that retrieve the flight data from Aviationstack API(a flight API ). I'm new to nodejs and I want to make this function synchronous using 'sync-request' so that when I call anywhere I could get something in return. here is the function code :
function get_flights_by_date(date) {
let url = 'http://api.aviationstack.com/v1/flights?access_key=xxxxxxxxxxxxxxxxx&flight_status=scheduled'
request(url, function (err, response, body) {
if (err) {
console.log('error:', error);
} else {
let vol = JSON.parse(body)
num = vol.pagination.limit
for (let i = 0; i < num; i++) {
dep = vol.data[i].departure.airport
depart = vol.data[i].departure.timezone
arrival = vol.data[i].arrival.timezone
arr = vol.data[i].arrival.airport
flight_date = vol.data[i].flight_date
number = vol.data[i].flight.number
console.log('flight number:' + ' ' + number + ' ' + 'from' + ' ' + depart + ' ' + 'at' + ' ' + dep + ' ' + 'airport' + ' ' + 'to' + ' ' + arrival + ' ' + 'at' + ' ' + arr + ' ' + 'airport' + ' ' + 'on' + ' ' + flight_date + '.')
}
}
});
}
This is not a way to make a sync code in node js
There are few approach to make any code sync
callback
What you are doing, this will fail when you will call directly from other function. instead you can do this
function get_flights_by_date(date, callback) {
let url = 'http://api.aviationstack.com/v1/flights?access_key=xxxxxxxxxxxxxxxxx&flight_status=scheduled'
request(url, function (err, response, body) {
if (err) {
console.log('error:', error);
callback(err);
} else {
let vol = JSON.parse(body)
num = vol.pagination.limit
for (let i = 0; i < num; i++) {
dep = vol.data[i].departure.airport
depart = vol.data[i].departure.timezone
arrival = vol.data[i].arrival.timezone
arr = vol.data[i].arrival.airport
flight_date = vol.data[i].flight_date
number = vol.data[i].flight.number
console.log('flight number:' + ' ' + number + ' ' + 'from' + ' ' + depart + ' ' + 'at' + ' ' + dep + ' ' + 'airport' + ' ' + 'to' + ' ' + arrival + ' ' + 'at' + ' ' + arr + ' ' + 'airport' + ' ' + 'on' + ' ' + flight_date + '.')
}
callback(null, number)
}
});}
function mainFunction(){
var date = '';
get_flights_by_date(date, function(err, result){
if(err){
return err
}
return result
})
}
promise
function get_flights_by_date(date) {
return new Promise(function (resolve, reject) {
let url =
"http://api.aviationstack.com/v1/flights?access_key=xxxxxxxxxxxxxxxxx&flight_status=scheduled";
request(url, function (err, response, body) {
if (err) {
console.log("error:", error);
reject(err);
} else {
let vol = JSON.parse(body);
num = vol.pagination.limit;
for (let i = 0; i < num; i++) {
dep = vol.data[i].departure.airport;
depart = vol.data[i].departure.timezone;
arrival = vol.data[i].arrival.timezone;
arr = vol.data[i].arrival.airport;
flight_date = vol.data[i].flight_date;
number = vol.data[i].flight.number;
}
resolve(number);
}
});});
}
async function main() {
try {
await get_flights_by_date("");
} catch (error) {}
}
Async lib
or you can use this lib to do your entire code here https://medium.com/velotio-perspectives/understanding-node-js-async-flows-parallel-serial-waterfall-and-queues-6f9c4badbc17
I would recommend you to go for async lib as it is easy to scale in this fashion
I would totally avoid using synchronous code when ever possible, as this does not scale. This is a very bad practice, and will halt your server as long as the request is still fetching...
You can (And should) use async/await, or Promise to wait for the data while making sure your server is available.
If you want to avoid the then callback on Promise, just use async/await
Converted to use Promise and async/await:
function get_flights_by_date(date) {
const url = 'http://api.aviationstack.com/v1/flights?access_key=xxxxxxxxxxxxxxxxx&flight_status=scheduled'
return new Promise((resolve, reject) => {
request(url, (err, response, body) => {
if (err) {
reject(err);
} else {
const data = JSON.parse(body); // Process your data
resolve(data);
}
});
})
}
function promiseExample() {
get_flights_by_date(new Date()).then(resultData => console.log(resultData))
}
async function example() {
const resultData = await get_flights_by_date(new Date());
console.log(resultData);
}

findOne not executed

I have the code below:
exports.getMyPets = function (req, res){
var email = req.body.email;
var result = [{}];
Owner.findOne({"email" : email }, "_id", function(err, owner){
if(err)
res.send(err);
console.log("Owner: " + owner._id + "\n");
Pet.find({"OwnerId": owner._id}, function(err, pet) {
if(err)
res.send(err)
var index = pet.length;
var empty = false;
if(index > 0)
empty = true;
try{
for (let x = 0; x < index; x += 1){
PetPicture.findOne({"petId" : pet[x]._id}, function (err, petPic){
console.log('pet ID: ' + pet[x]._id);
result[x] = {
name: pet[x].name,
breed: pet[x].breed,
description: pet[x].description,
imageType: petPic.imageType,
imageData: petPic.imageType
};
}); //end of petPicture.findOne
console.log('X : ' + pet[x]._id);
}// end of for loop
} catch (error){
console.log(error);
}
}
while(true){
if (result.length === index)
break;
}
if(!empty){
res.json(pet);
} else {
res.json(result);
}
}); // end of Pet,find
}); // end of petOwner.findOne
};
The block of PetPicture.findOne (the whole try catch block) does not get executed when I try to call it on endpoint. It does not log anything, not even an error on the catch part. What can I do to debug this?

ORDER BY in query gives me ORA-00933: SQL command not properly ended

Hi I have a query and I am using resultSet to process data. The query works fine but when I append 'ORDER BY name des', it gives me ORA-00933: SQL command not properly ended error.
This is my query.
router.post('/report/', jsonParser, function (req, res) {
var data = req.body,
startRow = data.startRow,
numRows = data.numRows,
sortCol = data.sortCol,
sortDir = data.sortDir;
var countQuery = 'SELECT COUNT(*) ' +
'FROM this_view ' ;
var query = 'SELECT t.NAME "NAME", ' +
't.AGE "AGE" ' +
'FROM this_view t ' +
'ORDER BY NAME desc';
var seg,
orderBy,
offset;
orderBy = ' ORDER BY UPPER(' + sortCol + ') ' + sortDir;
offset = ' OFFSET ' + startRow + ' ROWS FETCH NEXT ' + numRows + ' ROWS ONLY';
query += orderBy;
query += offset;
async.parallel({
rows: function (callback) {
pool.getConnection(function (err, connection) {
logger.info("Begin Connection: " + (new Date().toString()));
if (err) {
logger.error(err.message);
return;
}
logger.info("Begin execute: " + (new Date().toString()));
connection.execute(
query,
[],
{
resultSet: true,
prefetchRows: 1000
},
function (err, result) {
logger.info("End execute: " + (new Date().toString()));
// var rowsProcessed = 0;
// var startTime;
if (err) {
logger.error(err.message);
callback("Something broke in the first thing");
doRelease(connection);
return;
}
var procJson = [];
function fetchRowsFromRS(connection, resultSet, numRows) {
resultSet.getRows(
numRows, // get this many rows
function (err, rows) {
if (err) {
console.error(err);
doClose(connection, resultSet); // always close the result set
} else if (rows.length > 0) {
/**
* For each row in the result, pushes a new object to the rows array
* In each new object, the key is assigned and the result row value set
*/
for (var i = 0; i < rows.length; i++) {
procJson.push({});
for (var j = 0; j < resultSet.metaData.length; j++) {
procJson[i][resultSet.metaData[j].name.toLowerCase()] = rows[i][j];
}
}
//TODO: Add null handling
logger.info("Send JSON: " + (new Date().toString()));
logger.info("JSON Sent: " + (new Date().toString()));
if (rows.length === numRows) // might be more rows
fetchRowsFromRS(connection, resultSet, numRows);
else
doClose(connection, resultSet); // always close the result set
} else {
callback(null, procJson);
doClose(connection, resultSet); // always close the result set
}
});
}
fetchRowsFromRS(connection, result.resultSet, numRows)
});
});
},
totalRows: function (callback) {
pool.getConnection(function (err, connection) {
logger.info("Begin Connection: " + (new Date().toString()));
if (err) {
logger.error(err.message);
return;
}
logger.info("Begin execute: " + (new Date().toString()));
connection.execute(
countQuery,
function (err, result) {
logger.info("End execute: " + (new Date().toString()));
if (err) {
logger.error(err.message);
callback("Something broke");
doRelease(connection);
return;
}
logger.info("Send JSON: " + (new Date().toString()));
callback(null, result.rows[0][0]);
logger.info("JSON Sent: " + (new Date().toString()));
doRelease(connection);
});
});
}
}, function(err, result){
if(err){
logger.error(err);
}
res.send(result);
});
});
Since I am paginating, cant I use ORDER BY in the query itself?
If I dont use ORDER BY in the query, everything works fine. Please advice.
You have:
var query = 'SELECT t.NAME "NAME", ' +
't.AGE "AGE" ' +
'FROM this_view t ' +
'ORDER BY NAME desc';
Then you do:
orderBy = ' ORDER BY UPPER(' + sortCol + ') ' + sortDir;
offset = ' OFFSET ' + startRow + ' ROWS FETCH NEXT ' + numRows + ' ROWS ONLY';
query += orderBy;
query += offset;
Which would give you the query:
SELECT t.NAME "NAME",
t.AGE "AGE"
FROM this_view t
ORDER BY NAME desc
ORDER BY UPPER( <sortColumn> ) <sortDir>
OFFSET <startRow> ROWS FETCH NEXT <numRows> ROWS ONLY
You will have two ORDER BY clauses which is invalid syntax.
Instead, change:
orderBy = ' ORDER BY UPPER(' + sortCol + ') ' + sortDir;
To
orderBy = ', UPPER(' + sortCol + ') ' + sortDir;
Don't you mean ORDER BY NAME desC?

TypeError: Cannot read property 'emails' of null

Here is line 54, where I am getting the error:
if (docr.emails) {
And here is the rest of my original code:
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var ObjectId = require('mongodb').ObjectID;
var config = require('./config'),
xlsx = require('./xlsx'),
utils = require('./utils'),
_ = require('lodash'),
url = config.DB_URL;
var meetings = [];
function findNumberOfNotesByMeeting(db, meeting, callback) {
var meetingId = meeting._id.toString(),
meetingName = meeting.name.displayValue,
attendees = meeting.attendees;
host = meeting.host;
var count = 1, pending = 0, accepted = 0;
console.log("==== Meeting: " + meetingName + '====');
_.each(attendees, function(item) {
console.log(count++ + ': ' + item.email + ' (' + item.invitationStatus + ')');
if (item.invitationStatus == 'pending') { pending++; }
else if (item.invitationStatus == 'accepted') { accepted++; }
});
console.log("*** " + attendees.length + ", " + pending + "," + accepted);
db.collection('users').findOne({'_id': new ObjectId(host)}, function(err, doc) {
var emails = [];
if (doc.emails) {
doc.emails.forEach(function(e) {
emails.push(e.email + (e.primary ? '(P)' : ''));
});
}
var email = emails.join(', ');
if (utils.toSkipEmail(email)) {
callback();
} else {
db.collection('notes').find({ 'meetingId': meetingId }).count(function(err, count) {
if (count != 0) {
console.log(meetingName + ': ' + count + ',' + attendees.length + ' (' + email + ')');
meetings.push([ meetingName, count, email, attendees.length, pending, accepted ]);
}
callback();
});
}
});
}
function findMeetings(db, meeting, callback) {
var host = meeting.host;
db.collection('users').findOne({'_id': new ObjectId(host)}, function(err, docr) {
var emails = [];
if (docr.emails) {
docr.emails.forEach(function(e) {
emails.push(e.email + (e.primary ? '(P)' : ''));
});
}
var email = emails.join(', ');
if (utils.toSkipEmail(email)) {
callback();
} else {
var cursor = db.collection('meetings').find({
'email': {'$regex': 'abc', '$options': 'i' }
});
}
cursor.count(function(err, count) {
console.log('count: ' + count);
var cnt = 0;
cursor.each(function(err, doc) {
assert.equal(err, null);
if (doc != null) {
findNumberOfNotesByMeeting(db, doc, function() {
cnt++;
if (cnt >= count) { callback(); }
});
}
});
});
});
};
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
findMeetings(db, function() {
var newMeetings = meetings.sort(function(m1, m2) { return m2[1] - m1[1]; });
newMeetings.splice(0, 0, [ 'Meeting Name', 'Number of Notes', 'Emails' ]);
xlsx.writeXLSX(newMeetings, config.xlsxFileNameMeetings);
db.close();
});
});
Try the following:
function findMeetings(db, meeting, callback) {
var host = meeting.host;
db.collection('users').findOne({'_id': new ObjectId(host)}, function(err, docr) {
var emails = [];
if (!err && docr && docr.emails) {
docr.emails.forEach(function(e) {
emails.push(e.email + (e.primary ? '(P)' : ''));
});
}
...

Iteration of http requests in nodejs

I am trying to make multiple http get requests in nodeJs. I need to continue getting records until there are no more available. I am having problems trying to do the iteration of a chunk of code. I had been reading about the async module but I am not understanding how to apply it. Please help.
Here is the New code 11/25/2014:
The step I want to repeat is the GetUrl(newBlock) is the step I want to repeat until there is no more blocks. I determine that there is no more blocks when the result.lenght of the request response is less than 5,000.
Is there a way of me getting this parameter pack from newBlock function and then stop the loop if I use the whilst async module?
// JS Script: GPSAPIClient
// Code Description: API client code to retrieve GPS data for customer with account number: 47631
// This script requires Node.Js environment installed with Request() and Node-crontab modules (through NPM).
// It will run (as a backend script) every minute and retrieve the GPS available.
//
// Author : Vanessa Torres, Professional Services
var request = require("request");
var fs = require('fs');
var async = require('async');
var crontab = require('node-crontab');
var csvFile = "";
var debug = false;
process.argv.forEach(function(val, idx, array) {
if (val.toLowerCase() === 'debug') {
debug = true;
console.log('\n\n\n******************DEBUG MODE ACTIVE(Do not run in prod)******************\n\n\n')
}
});
console.log('waiting to start running');
var jobId = crontab.scheduleJob('* * * * *', function() {
//Gettting system date and adding leading zeros when are single digits. I need this to build the get request with date filters.
var d = new Date();
var nday = d.getDate();
var nmonth = d.getMonth();
var nhour = d.getHours();
var nmin = d.getMinutes();
var nfullyear = d.getFullYear();
if (nday < 10) {
nday = '0' + nday;
};
//1 minute substraction except for when is zero (the value will negative).
if (nmin != 0) {
var nmin = nmin - 1;
};
if (nmin < 10) {
nmin = '0' + nmin;
};
// added because TLC Plumbing is 2 hours behind us. (MST)
nhour = nhour - 2;
if (nhour < 10) {
nhour = '0' + nhour;
};
var nmonth = nmonth + 1;
if (nmonth < 10) {
nmonth = '0' + nmonth;
};
var options = {
//url: 'https://credentials#api.comettracker.com/v1/gpsdata' + '?fromdate=' + nfullyear + '-' + nmonth + '-' + nday + 'T' + nhour + '%3a' + nmin + '%3a' + '00',
url: 'https://credentials#api.comettracker.com/v1/gpsdata?fromdate=2015-11-23T17%3a00%3a00&todate=2015-11-24T10%3a00%3a00',
method: 'GET',
rejectUnauthorized: !debug
};
function GetUrl(callback) {
return request(options, function(error, response, body) {
console.log('request for links');
if (error) throw new Error(error);
var result = JSON.parse(body)['links'];
var urlnext = result[2].href;
console.log('UrlNext: ' + urlnext);
console.log(result[2].rel);
//moreBlocks = newBlock(urlnext);
moreBlocks = callback(urlnext);
});
// request to get the next block of records (maximun 5,000)
};
// HTTP get request - 1st request
request(options, function(error, response, body) {
if (error) throw new Error(error);
var result = JSON.parse(body)['gps-recs'];
console.log(result.length);
//console.log(result);
//create .csv file if result.length is > = 1 (If we received GPS records)
if (result.length >= 1 && result.length < 5000) {
console.log('entered first if, result.length: ' + result.length);
buildCSV(result);
};
//add the subsequent request when result.length = 5000
if (result.length == 5000) {
console.log('entered second if, result.length: ' + result.length);
buildCSV(result);
console.log('I came back from buildCSV. result.length is : ' + result.length);
**GetUrl(newBlock)**;
//console.log('moreblocks back from newblock: ' + moreBlocks);
};
});
});
function newBlock(urlnext) {
console.log('URL passed to newBlock: ' + urlnext);
// option 2 - variables needed to built the url (like the first request)
var n = urlnext.length;
var rest = urlnext.substr(40, n);
console.log('Rest: ' + rest);
var options = {
url: 'https://credentials#api.comettracker.com/v1/gpsdata/' + rest,
method: 'GET',
rejectUnauthorized: !debug
};
request(options, function(error, response, body) {
console.log('nextblock request');
if (error) throw new Error(error);
var result = JSON.parse(body)['gps-recs'];
if (result.length == 0) {
//no records for filter
console.log('first if' + result.length);
moreBlocks = false;
};
if (result.length < 5000 && result.length > 0) {
//last block appends record and ends
appendCSV(result);
moreBlocks = false;
console.log('second if' + result.length);
};
if (result.length == 5000) {
//appends records but has to keep running
appendCSV(result);
console.log('third if- moreBlocks' + result.length);
};
console.log('moreBlocks: ' + moreBlocks);
});
};
function buildCSV(result) {
var csvFile = " ";
console.log('before csvFile: ' + csvFile);
//adding headers
csvFile = csvFile.concat('UserNumber' + ',' + 'UserTimeTag' + ',' + 'Latitude' + ',' + 'Longitude' + ',' + 'SpeedMph' + ',' + 'Heading' + ',' + 'Status' + '\r\n');
// loop runs result.length times
for (var i = 0; i < result.length; i++) {
csvFile = csvFile.concat(result[i].UserInfo.UserNumber + ',' + result[i].UserTimeTag + ',' + result[i].Latitude + ',' + result[i].Longitude + ',' + result[i].SpeedMph + ',' + result[i].Heading + ',' + result[i].Status + '\r\n');
};
//console.log(csvFile);
fs.writeFile('file.csv', csvFile, function(err) {
if (err) throw err;
console.log('file saved');
});
};
//appending additional blocks
function appendCSV(result) {
var csvString = " ";
// loop runs result.length times
for (var i = 0; i < result.length; i++) {
csvString = csvString.concat(result[i].UserInfo.UserNumber + ',' + result[i].UserTimeTag + ',' + result[i].Latitude + ',' + result[i].Longitude + ',' + result[i].SpeedMph + ',' + result[i].Heading + ',' + result[i].Status + '\r\n');
};
// console.log(csvString);
fs.appendFile('file.csv', csvString, function(err) {
if (err) throw err;
console.log('Data appended');
});
};
It is difficult to see if moreBlocks is in the same scope as the while. On the other hand, the while statement will start making calls indefinetilly until that flags changes, and that might be too late. You need to make sure you do each subsequent request at a time.
You could use async's ´whilst´ function:
var moreBlocks = true;
whilst(function(){
return moreBlocks === true;
},function(callback){
return request(options, function(err, res, body){
console.log('request for links');
if (err) throw new Error(err);
var result = JSON.parse(body)['links'];
var urlnext = result[2].href;
console.log('UrlNext: ' + urlnext);
console.log(result[2].rel);
//You NEED to make sure you update moreBlocks
moreBlocks = newBlock(urlnext);
// Go to next step.
return callback(res);
})
},{
//You are done!
//Call any function you'd like.
});
Check the docs here!
Edit, if newBlock is asynchronous:
var moreBlocks = true;
whilst(function(){
return moreBlocks === true;
},function(callback){
return request(options, function(err, res, body){
console.log('request for links');
if (err) throw new Error(err);
var result = JSON.parse(body)['links'];
var urlnext = result[2].href;
console.log('UrlNext: ' + urlnext);
console.log(result[2].rel);
//Now status has the info about moreBlocks.
return newBlock(urlnext, function(status){
moreBlocks = status;
// Go to next step.
return callback(res);
});
})
},{
//You are done!
//Call any function you'd like.
});
Somewhere on newBlock:
function newBlock(urlnext, callback) {
// some code..
return request(options, function(){
//here status decides if we are done.
return callback(status);
});
}

Resources