Chaining Promises In Loop - node.js

I am struggling to loop through the array periods whilst querying the DB and inserting data. The problem I am facing can be seen on the 5th from last line. The final db.queryPromise does not get invoked.
Please see comment 5 lines from the bottom for where the problem is.
// db.js
const mysql = require('mysql');
const connection = mysql.createConnection({
host : 'localhost',
user : ****,
password : ****,
database : ****,
});
module.exports = connection;
module.exports.queryPromise = function (args) {
return new Promise((resolve, reject) => {
connection.query(args, (err, rows, fields) => {
if (err) return reject(err);
resolve(rows);
});
});
};
module.exports.connectPromise = new Promise((resolve, reject) => {
connection.connect(err => {
if (err) reject(err);
resolve();
});
});
// app.js
const db = require('../config/db');
const periods = ['1h','12h','24h','1w','1m','3m','1y','all'];
const sqlCarIds = `SELECT id FROM car_models ORDER BY id DESC LIMIT 200;`;
return db.queryPromise(sqlCarIds)
.then((rows) => {
const car_ids = [];
for (let i = rows.length - 1; i >= 0; i--) {
car_ids.push(rows[i].car_id);
};
for (let i = periods.length - 1; i >= 0; i--) {
const sqlSnapshot = `SELECT price FROM car_models;`;
db.queryPromise(sqlSnapshot)
.then(([row]) => {
if (!row) {
throw new Error('API call found nothin');
}
const highPrice = row.high;
const sqlInsert = `INSERT into price_cache (high) VALUES (` + highPrice` + )`;`
console.log(sqlInsert); // logs correctly formed query
db.queryPromise(sqlInsert)
.then(() => {
console.log('this should fire'); // doesn't fire
});
});
}
});

The SQL syntax for the sqlInsert is invalid. You will need to write it like following example. You need to use the ${expression} literals to add the value of an expression into a "Template String". Your promise doesn't get resolved because there is an error which rejects it.
const sqlInsert = `INSERT into price_cache (high) VALUES (${highPrice})`;

Related

How do we handle socket data emission when the user is changing input parameters? (Node.js, React.js, Postgres, Socket.io)

I have a React.js and Node.js application where I have a Chart.js component whose objective is to show real-time data coming from Postgres database. The chart has a multi-select dropdown from where the user can choose at max 4 parameters and the query takes an array parameter(the array of 4 tags). I am new to sockets and I want to know how to handle socket connection for such a scenario. My code might be wrong entirely and I do not know where I am going wrong. Is it the approach or the logic? Should the query take an array of 4 parameters or should I re-write the query for single parameter? I am totally lost with the logic.
(Last time I tested with this logic, it was emitting the same data and not moving forward in real-time.)
// initialize socket
const io = require('socket.io')(httpServer, options);
// validate request
// eslint-disable-next-line arrow-body-style
io.use((socket, next) => {
// TODO validate token
// const token = socket.handshake.auth.token;
return next();
});
io.on('connection', (socket) => {
logger.info('socket connected');
socket.on('disconnect', (event) => {
logger.info('client disconnected', event);
});
socket.on('fetchChartData', async (obj) => {
const startTime1 = new Date();
let startTime = "";
let endTime = "";
if(startTime1.getSeconds() == 0) {
startTime = format(startTime1, 'yyyy-MM-dd HH:mm:ss');
endTime = format(new Date(startTime1.getTime() + (59 * 1000)), 'yyyy-MM-dd HH:mm:ss');
}
else {
const abc = startTime1.getTime() - (startTime1.getSeconds() * 1000)
const abc1 = startTime1.getTime() - (startTime1.getSeconds() * 1000) + 59000;
startTime = format(new Date(abc), 'yyyy-MM-dd HH:mm:ss');
endTime = format(new Date(abc1), 'yyyy-MM-dd HH:mm:ss');
}
// For converting array to string with parentheses
const arrToString = (oriArr) => {
let newStr = "";
const len = oriArr.length;
oriArr.forEach( (item, index) => {
newStr = newStr + "'"+ item + "'";
if(index < len -1) {
newStr = newStr + ",";
}
});
return newStr;
}
//const query = `select sr_datetime,tagid,tagvalue from md.taghda_${obj.siteid} where sr_datetime = '${startTime}' and tagid in (${arrToString(obj.tagArr)})`;
const query = `SELECT * FROM md.tag_summary_minute WHERE tagid in (${arrToString(obj.tagArr)}) AND bucket between '${startTime}' and '${endTime}'`;
const results = await dbService.executeQuery(query);
io.emit('CHART1RTANALYSIS', {
chartData: results.rows,
});
})
});
And this is my UI side socket code:
useEffect(() => {
if(selectedTagsID.length > 0) {
const socket = socketIOClient('http://localhost:3000', {
auth: {
token: Auth.getToken(),
},
});
const obj = {
tagArr: selectedTagsID,
siteid: resposeState?.selectedSite?.site
};
socket.emit('fetchChartData', obj);
console.log("The data is emitted every 59 seconds", obj);
socket.on("CHART1RTANALYSIS", (data) => {
console.log("chart data", data.chartData);
const chartData = data.chartData[0];
const userTimezone = resposeState?.selectedSite?.timezone
const dateInUserTimezone = userTimezone
? utcToZonedTime(chartData?.bucket, userTimezone)
: chartData?.bucket;
setServerEmittedData((state) => [
...state,
{
x: dateInUserTimezone,
y: chartData?.avg,
},
]);
});
socket.on("connect_error", (err) => {
console.log("connect error", err);
});
return () => {
socket.disconnect();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}
}, []);
console.log("Server emitted data is", serverEmittedData);
Please help me with this and also tell me if I should run a cron job for this since it is a chart. What should be the ideal approach? Running a cron job as long as the user is on this page or creating a new socket connection every time the user selects new tags so that the db query runs again.

Nodejs fast-csv and promises

I feel like things are running out of order here. I want to establish a connection to a mySQL database. Then I want to read in a file. Row by row I want to grab the name and run a query. I would have assumed that my sqlSelectQuery function, which returns a promise, would have waited for the promise to resolve before moving onto the next row. What am I missing here?
const mysql = require('mysql');
const fs = require('fs');
const path = require('path');
const csv = require('fast-csv');
const config = require('./config')
const connection = mysql.createConnection({
user: config.user,
password: config.password,
database: config.database,
host: config.host
});
connection.connect((err) => {
if(err){
console.log('Error connecting to Db');
return;
}
console.log('Connection established');
});
fs.createReadStream(path.resolve(__dirname,'data.csv'))
.pipe(csv.parse({ headers: true }))
.on('error', error => console.error("error", error))
.on('data', row => { // need to get this to block
sqlSelectQuery(row).then(result => console.log("result: ", result))
})
.on('end', rowCount => console.log(`Parsed ${rowCount} rows`));
const sqlSelectQuery = (row) => {
return new Promise((resolve, reject) => {
console.log("inside promise");
const selectQuery = 'SELECT * FROM loans where business_name = ?;';
connection.query(selectQuery, [row.BorrowerName], (err,rows) => {
let result = {};
if(err) reject(err);
if (rows.length === 1){
let res = rows[0];
result = {
business_name: res.business_name,
loan_range: res.loan_range,
loan_amount: row.InitialApprovalAmount,
count: 1
};
resolve(result);
} else {
result = {
business_name: row.BorrowerName,
loan_range: "",
loan_amount: "",
unique: rows.length
};
resolve(result);
}
});
})
}
my console looks like this
inside promise
inside promise //20 times (I have 20 rows)
Parsed 20 rows
Connection established
result: {....}
result: {...}....
I found this answer. I need to add a pause and resume
nodejs async await inside createReadStream
.on('data', async (row) => { // need to get this to block
stream.pause();
await sqlSelectQuery(row).then(result => console.log("result: ", result))
stream.resume();
})
The issue now is that my .on('end') runs before the last row.
You could add each row to a rowsToProcess array, then, once the file data is read, process each row one by one:
const mysql = require('mysql');
const fs = require('fs');
const path = require('path');
const csv = require('fast-csv');
const config = require('./config')
const connection = mysql.createConnection({
user: config.user,
password: config.password,
database: config.database,
host: config.host
});
connection.connect((err) => {
if (err) {
console.error('Error connecting to Db:', err);
return;
}
console.log('Connection established');
const rowsToProcess = [];
fs.createReadStream(path.resolve(__dirname,'data.csv'))
.pipe(csv.parse({ headers: true }))
.on('error', error => console.error("error", error))
.on('data', row => {
// Add row to process.
rowsToProcess.push(row);
})
.on('end', async rowCount => {
await processRows(rowsToProcess);
console.log("processRows: complete.")
})
});
async function processRows(rowsToProcess) {
console.log(`Read ${rowsToProcess.length} row(s) from csv file...`)
for (let i = 0; i < rowsToProcess.length; i++) {
console.log(`processing row ${i+1} of ${rowsToProcess.length}...`);
let result = await sqlSelectQuery(rowsToProcess[i])
console.log(`row ${i+1} result:`, result);
}
}
const sqlSelectQuery = (row) => {
return new Promise((resolve, reject) => {
console.log("Processing row:", row);
const selectQuery = 'SELECT * FROM loans where business_name = ?;';
connection.query(selectQuery, [row.BorrowerName], (err,rows) => {
let result = {};
if(err) reject(err);
if (rows.length === 1){
let res = rows[0];
result = {
business_name: res.business_name,
loan_range: res.loan_range,
loan_amount: row.InitialApprovalAmount,
count: 1
};
resolve(result);
} else {
result = {
business_name: row.BorrowerName,
loan_range: "",
loan_amount: "",
unique: rows.length
};
resolve(result);
}
});
})
}

I am declaring a function, but it is non callable, why?

I am declaring a function using Node.js, but it is being non callable.
I do not understand what is wrong since I declared it as the same way as the other functions and this is the only one that is being non callable.
My code:
const mysql = require('mysql');
const mainKey = '';
const con = mysql.createConnection({
host: "localhost",
user: "root",
database: "hotels"
});
function getUsers() {
return new Promise(function(resolve, reject) {
var users = new Array();
const sql = "SELECT * FROM users";
con.query(sql, function(err, result, fields) {
if(err) throw err;
users = [];
for(var i = 0; i<result.length; i++) {
users.push([result[i].id, result[i].user, result[i].password]);
}
resolve(users);
});
});
}
function regUser(user, password, key) { //this function is non callable
return new Promise(function(resolve, reject){
console.log(test)
});
}
function getHotelNames(idUser) {
return new Promise(function(resolve, reject){
var hotelNames = new Array();
const sql = "SELECT * FROM hotels WHERE user=" + idUser;
con.query(sql, function (err, result, fields) {
if (err) throw err;
hotelNames = [];
for(var i=0; i<result.length; i++) {
hotelNames.push(escape(result[i].name));
}
resolve(hotelNames);
});
})
}
function getURLs() {
return new Promise(function(resolve, reject){
var urlsHotels = new Array();
const sql = "SELECT * FROM hotels";
con.query(sql, function (err, result) {
if (err) throw err;
urlsHotels = [];
for(var i=0; i<result.length; i++) {
urlsHotels.push(result[i].url);
}
resolve(urlsHotels);
});
})
}
function insertValues(hotelNames, url, name, idUser) {
return new Promise(function(resolve, reject){
const id = hotelNames.length+1
const sql = "INSERT INTO hotels (id, name, url, user) VALUES (" + id + ", '" + name + "', '" + url + "', " + idUser +")";
con.query(sql, function (err, result) {
if (err) resolve(['errorNewHotel']); //throw err;
resolve(['NewHotel'])
});
})
}
function deleteValues(name) {
return new Promise(function(resolve, reject){
const sql = "DELETE FROM hotels WHERE name = '" + name + "'";
console.log(sql)
con.query(sql, function (err, result) {
if (err) resolve(['errorDeletingHotel']); //throw err;
resolve(['deletedHotel'])
});
})
}
const funcGetUsers = async ()=> {
const users = await getUsers();
return users;
}
const funcRegisterUser = async (user, password, key)=> {
const regUser = await regUser(user, password, key); //I am calling the function here
return regUser;
}
const funcGetHotelNames = async (idUser)=> {
const hotelNames = await getHotelNames(idUser);
return hotelNames;
}
const funcGetURLs = async ()=> {
const urls = await getURLs();
return urls;
}
const funcInsertValues = async(hotelNames, url, name, idUser)=> {
const message = await insertValues(hotelNames, url, name, idUser);
return message;
}
const funcDeleteValues = async(name)=> {
const message = await deleteValues(name);
return message;
}
module.exports.funcGetUsers = funcGetUsers;
module.exports.funcRegisterUser = funcRegisterUser;
module.exports.funcGetHotelNames = funcGetHotelNames;
module.exports.funcGetURLs = funcGetURLs;
module.exports.funcInsertValues = funcInsertValues;
module.exports.funcDeleteValues = funcDeleteValues;
Even my code editor, Visual Studio Code says it is never read
That is what I get when I try to run function:
I do not think the issue comes from the main file (server.js), so I have tried to copy all the code in a new file and Visual Studio was still saying that it is never read while the other functions were fine.
What am I doing wrong? I do not get it.
Thank you in advance.
The real Javascript error is:
ReferenceError: regUser is not defined
In
const funcRegisterUser = async (user, password, key)=> {
const regUser = await regUser(user, password, key); //I am calling the function here
return regUser;
}
You're trying to assign to a variable named regUser while also calling a function named regUser. But, because you declare a variable named regUser inside that function, any references to a variable named regUser inside that function will refer to that regUser variable. So your await regUser(...) is trying to await the variable which has not been assigned to yet - it's in the temporal dead zone.
Just use a different variable name, and you'll avoid the name collision:
const funcRegisterUser = async (user, password, key)=> {
const result = await regUser(user, password, key);
return result;
}
Or, just return the Promise itself, no need to await something you immediately return:
const funcRegisterUser = (user, password, key) => (
regUser(user, password, key)
);
Or, even better, since funcRegisterUser is just calling regUser, maybe leave out funcRegisterUser entirely, and just export regUser:
module.exports.funcRegisterUser = regUser;
The problem is not in the function, but how you call it.
const regUser = await regUser(user, password, key);
This line will create a new constant regUser as undefined, then try to invoke it as a function. Your function is shadowed in outer scope, and thus inaccessible. The solution is simple: change the name of your constant.

Undefined value after returning an array of values from a MySQL query in a different file

I'm using the Mysql connector because I need to get some data from my database, but I'm having the following issue:
I have two files server.js and dbConnection.js and I have a return in the dbConnection.js file which should return an array and it should show it in the server.js file. However, it prints out an undefined array. I don't understand what I'm doing wrong since I also tried to print out the array before reurning it in the dbConnection.js file and it's shown with the data.
server.js:
const express = require('express');
const dbConnection = require('./dbConnection.js');
app.get('/', function (req, res) {
const val1 = new Promise((resolve, reject) => {
dbConnection
.getData()
.then(data => {
resolve(data)
})
.catch(err => reject('error'))
});
Promise.all([val1])
.then(data => {
console.log(data) //here it prints out [undefined]
});
});
dbConnection.js:
const mysql = require('mysql');
const con = mysql.createConnection({
host: "localhost",
user: "root",
database: "db1"
});
const getData = async ()=> {
var array = new Array();
const sql1 = "SELECT * FROM table1 WHERE active=1";
con.query(sql1, function (err, result, fields) {
if (err) throw err;
array = [];
for(var i=0; i<result.length; i++) {
array.push(result[i].active);
}
console.log(array) //here it prints out the array with its values
return array;
});
}
module.exports.getData = getData;
Edit: Maybe this will be helpful in order to figure out what's happening. I have just tried this and it prints out an empty array []:
const mysql = require('mysql');
var array = new Array();
const con = mysql.createConnection({
host: "localhost",
user: "root",
database: "db1"
});
const getData = async ()=> {
const sql1 = "SELECT * FROM table1 WHERE active=1";
con.query(sql1, function (err, result, fields) {
if (err) throw err;
//array = [];
for(var i=0; i<result.length; i++) {
array.push(result[i].active);
}
console.log(array) //here it prints out its data
//return array;
});
console.log(array); //here it prints out []
}
module.exports.getData = getData;
When I print the array out in the dbConnection.js file:
When I print it out in the server.js file:
Why is this happening and how to fix it?
Thanks in advance.
Use Async/Await with promises. You cannot use the syntax with callback. You have to change your dbConnection.js as below. You have to promisify your callback.
function myQuery(){
return new Promise(function(resolve, reject){
var array = new Array();
const sql1 = "SELECT * FROM table1 WHERE active=1";
con.query(sql1, function (err, result, fields) {
if (err) throw err;
array = [];
for(var i=0; i<result.length; i++) {
array.push(result[i].active);
}
console.log(array) //here it prints out the array with its values
resolve(array);
});
})
}
const getData = async ()=> {
var array= await myQuery();
return array;
}
module.exports.getData = getData;

How can I execute db.copyDatabase through NodeJS's MongoDB native driver?

I do have a shell script that invokes
mongo --eval "db.copyDatabase('somedatabase', 'somedatabase_duplicate', 'sourcehost')"
to copy a database.
Currently I am stuck with doing the same from within a Node.JS application. Calling
mongoCommand = `db.copyDatabase("somedatabase", "somedatabase_duplicate", "localhost")`;
db.command(mongoCommand, function(commandErr, data) {
if(!commandErr) {
log.info(data);
} else {
log.error(commandErr.errmsg);
}
});
Always resulsts in a "no such command" error message.
Edit for clarification: Using db.admin().command() results in the same problem and using the command suggested in enter link description here, too.
What's the correct way to call this command or, alternatively, to clone a database from Node.JS?
Well, you are trying to copy database which is administration operation so have to do with admin account. Again, to copy database command is copydb.
try running this command in shell, db.copyDatabase and you'll see source of command.
try:
var assert = require('assert');
var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/test';
MongoClient.connect(url, function(err, db) {
if (err) {
console.log(err);
}
else {
var mongoCommand = { copydb: 1, fromhost: "localhost", fromdb: "test", todb: "test_dup" };
var admin = db.admin();
admin.command(mongoCommand, function(commandErr, data) {
if (!commandErr) {
console.log(data);
} else {
console.log(commandErr.errmsg);
}
db.close();
});
}
});
//core modules
const assert = require('assert')
const MongoClient = require('mongodb').MongoClient;
const moment = require('moment');
const mongo = require('mongodb')
//custom modules
let { ip, port, database } = require('./dbUpgradeConfig')
const url = `mongodb://${ip}:${port}`
let todayDate = moment().format('DD/MM/YYYY HH:mm')
console.log(todayDate)
const myDate = new Date()
console.log(myDate)
var d = Date(Date.now());
// Converting the number of millisecond in date string
a = d.toString()
// Options for mongoDB
const mongoOptions = { useNewUrlParser: true }
let db
//TODO: handle reconnect
let connect = () => {
return new Promise((resolve, reject) => {
if (db) resolve()
else {
mongo.connect(url, mongoOptions, (err, client) => {
if (err) reject(err)
else {
db = client.db(database)
resolve()
}
})
}
})
}
/**
* #description create duplicate database from current database in mongodb
*/
let CloneDb = () => {
return new Promise((resolve, reject) => {
connect()
.then(() => {
console.log(db)
let mongoCommand = { copydb: 1, fromhost: "localhost", fromdb: "db_name", todb: "db_name_duplicate" }
let adminDB = db.admin()
adminDB.command(mongoCommand, function (commandErr, data) {
if (!commandErr) {
console.log(data)
} else {
console.log(commandErr.errmsg)
}
});
})
})
}
CloneDb().then(data => {
// debugger;
console.log("The clone db", data)
})

Resources