Nodejs loops iterating before executing the mysql queries embedded inside them - mysql

I am unable to return the projectIds array after the mysql statements have finished executing as the loop is iterating before executing them. This results in empty array being returned everytime.
function fetchProjects (projects, userId, callback) {
projectIds = [];
Object.keys(projects).forEach(function(key) {
var row = projects[key];
const sql = 'create table if not exists users_' + row.project_id + '(sno int primary key auto_increment, iteration_no int not null, user_id int not null, foreign key(iteration_no) references pro_' + row.project_id + '(iteration_no), foreign key(user_id) references user(user_id))';
connection.query(sql, function(err, result) {
if(err) {
console.log(err.sqlMessage);
return callback(err.sqlMessage, false);
} else {
// console.log("First statement executed");
const sql1 = "select distinct t1.project_id, t1.project_name, t1.client, t1.initial_department_id, t1.start_date, t1.status, t1.current_department, t1.currently_assigned_user from project t1, users_" + row.project_id + " t2 where t1.project_id = '" + row.project_id + "' and (select user_id from users_" + row.project_id +" where user_id = " + userId + ")";
connection.query(sql1, function(err, result1) {
if(err) {
console.log(err.sqlMessage);
return callback(err.sqlMessage, false);
} else {
if(result1.length > 0) {
console.log(result1);
projectIds.push(result1);
}
}
});
}
});
// console.log("Iterating");
});
return callback(false, projectIds);
}

You are trying to execute callback style code in forEach which doesn't wait for the query to finish. That is the reason you are getting emoty array. I would make few modifications
Use promise version of the query method. Check the documentation of mysql node package
Use for..of loop instead of forEach.
Make the function async
Also await when you call the promise version of query method.
Hope this helps.

Related

Node.js + SQL - is Insert query faster than Select?

I have a problem with SQL in node.js.
I have an array 'data' which contains some data returned from the async function and I want to put it into MySQL database if there's no result with the same data. There are some duplicated objects in array but I want to put just one into database.
Function example:
async function data_name(param){
var data = [];
data.push({
name: param,
sname: 'sname',
url: 'url',
report: 'report'
});
return data;
}
Then I run following code:
var data = await data_name('name');
data.forEach(item=>{
var chk = "SELECT * FROM `?` WHERE `name` = ? AND `sname` = ?;";
con.query(chk, [tablename, item.name, item.sname], function(er,items){
if(er) throw er;
if(items.length < 1){
var insert = "INSERT INTO `?` (name, sname, url, report) VALUES (?,?,?,?)";
con.query(insert, [tablename, item.name, item.sname, item.url, item.report], function(erg,added){
if(erg) throw erg;
console.log('Item added: ' + item.name);
});
}
else{
if(items[0].report != item.report){
var upd = "UPDATE `?` SET report = ? WHERE id = ?";
con.query(upd, [tablename, item.report, items[0].id], function(ere,edited){
if(ere) throw ere;
console.log('Item updated: ' +items[0].name);
});
}
}
//broken code below
var adc = "SELECT * FROM `links` WHERE `link` = ?;";
con.query(adc, item.url, function(erc, results){
//tried also with: results[0] === undefined || results[0] === null
var test = item.url + ' ' + results.length;
console.log(test);
if(results.length < 1){
var add_c = "INSERT INTO `links` (link_name, link_url) VALUES (?,?)";
con.query(add_c, [item.name, item.url], function(era, a_link){
if(era) throw era;
});
}
else{console.log('Record exists');}
});
});
});
The first SQL works great: it inserts all records to database, there are no duplicates and it updates values if it is required.
Second SQL (mentioned by a comment in code that I put before) doesn't work properly if the database is empty. It duplicates records in database (after running it database has 470 records while every record is duplicated about 8 times).
test variable prints for eg:
https://google.com 0
https://google.com 0
https://facebook.com 0
https://facebook.com 0
https://google.com 1
https://facebook.com 0
https://google.com 0
The problem exists only if a database is empty (if I run the code second time, the second SQL doesn't insert records new records and print in console 'Record exists'
Any ideas what can be reason of that?

NodeJS and MySQL. Why the field makes an error in the query statement?

Using NodeJS and MySQL I had an error in my query statement of database fields called user_name.
Why user_name makes an error and what is the solution?
function get_role(callback) {
tempCont.query('SELECT * from `users` where `user_name` = ahmed' , function (error, results) {
if (error) callback(null);
callback(results[0].password);
console.log("from query = " + results[0].password);
});
}
You have a bare keyword (ahmed) which should be quoted:
function get_role(callback) {
tempCont.query("SELECT * from `users` where `user_name` = 'ahmed'" , function (error, results) {
if (error) callback(null);
callback(results[0].password);
console.log("from query = " + results[0].password);
});
}
function get_role(callback) {
tempCont.query("SELECT * from users where user_name = 'ahmed'" , function (error, results) {
if (error) callback(null);
callback(results[0].password);
console.log("from query = " + results[0].password);
});
}

Node.js chaining promises in a loop with MySQL

I am trying to make a non-relational DB into a relational DB. So I am starting from data with no unique IDs.
I need to get the result from one SQL call loop through those rows, and for each one, do a SQL SELECT using part of the first result, then another SQL select using the next result, and then a write using IDs from the first and last queries.
I am using Node.js and ES6 promises to keep everything in order, but I seem to be missing something. I was actually trying to do an extra SQL call, and also use that result in the third query, but I am simplifying it to just get one call to feed into another.
Maybe some code will help show what I am trying to do.
Here is my query class that returns promises:
var mysql = require('mysql');
class Database {
constructor() {
this.connection = mysql.createConnection({
host: "localhost",
user: "root",
password: "root",
database: "pressfile"
});
}
query(sql, args) {
return new Promise((resolve, reject) => {
this.connection.query(sql, args, (err, result, fields) => {
if (err) return reject(err);
resolve (result);
});
});
}
close() {
return new Promise((resolve, reject) => {
this.connection.end(err => {
if (err) return reject (err);
resolve();
});
});
}
}
This was stolen pretty much as is from a tutorial site, and this part seems to work pretty well. Then here comes the loop, and the multiple queries:
var contactId;
var address1;
var orgName;
var database = new Database();
database.query("SELECT * FROM contact")
.then( result => {
for (var i = 0; i < result.length; i++) {
contactId = result[i].contactId;
orgName = result[i].org;
var sql2 = "SELECT * FROM organization WHERE (name = \"" + orgName + "\")";
console.log(sql2);
database.query(sql2)
.then(result2 => {
console.log(result2);
var orgId = result2[0].organizationId;
var sql3 = "INSERT INTO contact_organization (contactId, organizationId) VALUES (" + contactId + ", " + orgId + ")";
console.log(sql3);
return ""; //database.query(sql3);
}).then( result3 => {
console.log(result3);
});
}
}).catch((err) => {
console.log(err);
databse.close();
});
I know it is kind of unraveling at the end, but I'm not wanting to do the INSERT query until I know I can get it right. Right now in the console, I get a valid organization object, followed by:
`INSERT INTO contact_organization (contactId, organizationId) VALUES (17848, 29)'
17848 is the final contactId that is returned in the for loop. How can I get the contactId that is assigned before the second query. I know I am not doing this asynchronous stuff right.
Try something like this. Just a quick solution. (not tested).
const selectOrg = (result) => {
contactId = result[i].contactId;
orgName = result[i].org;
var sql = "SELECT * FROM organization WHERE (name = \"" + orgName + "\")";
return database.query(sql);
};
const insertOrg = (result) => {
var orgId = result[0].organizationId;
var sql = "INSERT INTO contact_organization (contactId, organizationId) VALUES (" + contactId + ", " + orgId + ")";
return database.query(sql);
};
database.query("SELECT * FROM contact")
.then(result => {
const promises = [];
for (var i = 0; i < result.length; i++) {
promises << selectOrg(result)
.then(insertOrg);
}
return Promise.all(promises);
})
.then(allResults => {
console.log(allResults);
})
.catch((err) => {
databse.close();
});
I found a way to do this, but it is kind of cheesy. I included the contactId as a constant in the SQL query to get the organization, so I could then pass the value to the .then, keeping everything in order.
My sql2 statement becomes:
var sql2 = "SELECT *, " + contactId + " AS contactId FROM organization WHERE (name = \"" + orgName + "\")";
Then when that query returns, I can just pull the correct contactId out as result[0].contactId, from the same result I get the organizationId from.
Here is the final code:
database.query("SELECT * FROM contact")
.then( result => {
for (var i = 0; i < result.length; i++) {
var contactId = result[i].contactId;
var orgName = result[i].org;
var sql2 = "SELECT *, " + contactId + " AS contactId FROM organization WHERE (name = \"" + orgName + "\")";
database.query(sql2)
.then(result2 => {
var orgId = result2[0].organizationId;
var contactId = result2[0].contactId;
var sql3 = "INSERT INTO contact_organization (contactId, organizationId) VALUES (" + contactId + ", " + orgId + ")";
console.log(sql3);
return database.query(sql3);
}).then( result3 => {
console.log(result3);
});
}
}).catch((err) => {
console.log(err);
databse.close();
});
The console.log(result3) returns a bunch of these:
OkPacket {
fieldCount: 0,
affectedRows: 1,
insertId: 0,
serverStatus: 2,
warningCount: 0,
message: '',
protocol41: true,
changedRows: 0 }
And I got one contact_organization inserted for every contact row returned from the first query.

Bringing separate asynchronous branches back into one branch again

I am working on a node js app which makes use of the express and mysql libraries.
I have a MySQL user table with the following columns:
auto incrementing primary id
username varchar unique
There is no password, etc.
Other tables include:
room
id
room_name
user_room
id
user_id (FK to user table)
room_id (FK to room table)
details
id
user_room_id (FK to user_room table)
col1
col2
col3
Upon trying to connect to a room, I want the database to try pulling their data for that room.
If the data does not exist, I want to see if the username exists in the user table.
If the username does exist, I want to get their id.
If the username does not exist, I want to add their name to the user table and capture the last inserted id
Once having their id, I want to add a record to the user_room table for that user and then several records to the details table based on the newly inserted id in the user_room table.
I seem to be getting into a tangled web going into so many layers.
This is what my code currently looks like:
socket.on('enter room', function(data, callback){
var sql = "select col1, col2, col3 from room JOIN user_room on room.id = user_room.room_id JOIN user on user_room.user_id = user.id JOIN details on user_room.id = details.user_room_id where username = ?";
db_connection.query(sql, [socket.nickname], function (err, result) {
if (err){
console.log("ENTER ROOM DB ERROR: " + err);
return;
}
if (!result.length){
var sql = "select id from user where name = ?";
db_connection.query(sql, [socket.nickname], function (err, result){
if (err){
console.log("ENTER ROOM, SELECT ID DB ERROR: " + err);
return;
}
if (!result.length){
var sql = "insert into user (name) values (?)";
db_connection.query(sql, [socket.nickname], function(err, result){
if (err){
console.log("ENTER ROOM, INSERT ID DB ERROR: " + err);
return;
}
id = result.insertId;
});
}
else {
id = result[0].id;
}
});
//We need to pull things back into one branch again here
//Using the user id and room id I will insert a record into the user_room table
//Then using the newly inserted id in the user_room table, I need to add records to a details table
}
});
//Send col1, col2, and col3 data back to user
//This section here also needs to be pulled back into one branch again
io.sockets.emit('details', result);
});
It mostly works, but because I branch off in two different ways to get the user id (one if it already exists, and one if I need to insert it), I do not know how to pull it back together again into one branch.
What can I do to pull my code back into one branch again so that I can use the id again? Or, is there a better way of approaching this altogether?
A side question: Can I safely remove the "callback" in my opening function, or should I be using this somewhere in my code? I feel that the emit is like a callback to the client so that I do not need "callback" here.
I took a different approach to get userId on upsert. I used promise to send the room data immediately, if available.
socket.on('enter room', function (data, callback) {
let nickName = '';
let roomId = '';
return bookingDetails(nickName).then((details) => {
if (details.length !== 0) {
return Promise.resolve(details);
} else {
return createRoom(nickName, roomId);
}
}).then((details) => {
io.sockets.emit('details', details);
});
});
function createRoom(nickName, roomId) {
return getUserDetails(nickName).then((userId) => {
return insertUserRoom(userId, roomId); //your function
}).then((userRoomDetails) => {
return insertDetails(userRoomDetails); //your function
});
}
function bookingDetails(nickName) {
let sql = "select col1, col2, col3 from room " +
"JOIN user_room on room.id = user_room.room_id " +
"JOIN user on user_room.user_id = user.id " +
"JOIN details on user_room.id = details.user_room_id where username = ?";
return new Promise((resolve, reject) => {
db_connection.query(sql, [nickName], function (err, details) {
if (err) {
return reject("ENTER ROOM DB ERROR: ");
}
return resolve(details);
});
});
}
function getUserDetails(nickName) {
return new Promise((resolve, reject) => {
let sql = "select id from user where name = ?";
db_connection.query(sql, [nickName], function (err, userDetail) {
if (err) {
return reject(err);
}
if (userDetail === null) { //insert
return createUser(nickName);
}
return userDetail;
}).then((userDetail) => {
return resolve(userDetail.id);
});
});
}
function createUser(nickName) {
return new Promise((resolve, reject) => {
let sql = "insert into user (name) values (?)";
db_connection.query(sql, [nickName], function (err, userDetail) {
if (err) {
return reject(err);
}
return resolve(userDetail);
});
});
}

Passing a key-value array in NodeJS for MySQL select query

I can pass an array to an mysql insert in nodeJS like so..
var data = {userId: 3, name: "sample"}
db.query('insert into my_table SET ?', data, function(err, result){...}
Is there a similar way of passing an array to a select query in the where clause... without specifying all the fields?
var data = {userId: 3, name: "sample"}
db.query('select * from my_table WHERE ?', data, function(err, result){...}
Doesn't seem to work.. nor does using the SET name in place of where...
database.conn.config.defaultQueryFormat = function (query, values) {
if (!values) return query;
var updatedQuery = query.replace("?", function () {
var whereClause = "";
for(var index in values){
whereClause += mysql.escapeId(index) + " = " + db.escape(values[index]) + " and ";
}
whereClause = whereClause.substring(0, whereClause.length - 5);
return whereClause;
});
return updatedQuery;
};
This appears to work.. e.g.
var val = db.query('select * from my_table where ?', data, function(err, result) {
}