Node JS MySQL query dependent on another query - mysql

In the following code, I have a select query called queryString which retrieves information about some tweets that are already stored in the database. I want to retrieved the hashtags that are mentioned in those tweets so that I show to a user the tweet information along with the its hashtags:
var queryString = 'select Tweet.Label, Tweet.TDate, Tweet.TLink, Author.Lable, Author.ALink from Tweet, Author where Tweet.AuthorID IN (select ID from Author where Lable = ?) AND Author.ID IN (select ID from Author where Lable = ?)';
var query = connection.query(queryString, [term,term], function(err, rows) {
console.log(rows);
//res.write(JSON.stringify(rows));
var tweet = JSON.parse(JSON.stringify(rows));
for(var i in tweet){
res.write("Author: ");
res.write("<a href='" + tweet[i].ALink + "' target='_blank'>" + tweet[i].Lable + "</a> <br/>");
res.write("Date: " + tweet[i].TDate + "<br/>");
res.write("Tweet: " + "<a href='" + tweet[i].TLink + "' target='_blank'>" + tweet[i].Label + "</a> <br/>");
var query1 = connection.query('select Label from Hashtag where ID IN (select HashID from tweethashs where TweetID IN (select ID from Tweet where Label = ?))', [tweet[i].Label], function(err, rows1) {
var tweet1 = JSON.parse(JSON.stringify(rows1));
for(var i in tweet1){
res.write("Hashtag: ");
res.write(tweet1[i].Label);
}
}
);
res.write("<br/><br/>");
}
res.end();
});
What I did is I included a query for the hashtags inside the loop of tweets so that I take the tweet as a parameter for the hashtags query (in where clause). When I run the code, I got the following error:
events.js:154
throw er; // Unhandled 'error' event
^
Error: write after end
at ServerResponse.OutgoingMessage.write (_http_outgoing.js:426:15)
at Query._callback (C:\Users\Nasser\Desktop\Spring Semester\Intelligent Web\
Node JS\SocialSearch.js:58:11)
at Query.Sequence.end (C:\Users\Nasser\Desktop\Spring Semester\Intelligent W
eb\Node JS\node_modules\mysql\lib\protocol\sequences\Sequence.js:96:24)
at Query._handleFinalResultPacket (C:\Users\Nasser\Desktop\Spring Semester\I
ntelligent Web\Node JS\node_modules\mysql\lib\protocol\sequences\Query.js:144:8)
at Query.EofPacket (C:\Users\Nasser\Desktop\Spring Semester\Intelligent Web\
Node JS\node_modules\mysql\lib\protocol\sequences\Query.js:128:8)
at Protocol._parsePacket (C:\Users\Nasser\Desktop\Spring Semester\Intelligen
t Web\Node JS\node_modules\mysql\lib\protocol\Protocol.js:280:23)
at Parser.write (C:\Users\Nasser\Desktop\Spring Semester\Intelligent Web\Nod
e JS\node_modules\mysql\lib\protocol\Parser.js:73:12)
at Protocol.write (C:\Users\Nasser\Desktop\Spring Semester\Intelligent Web\N
ode JS\node_modules\mysql\lib\protocol\Protocol.js:39:16)
at Socket.<anonymous> (C:\Users\Nasser\Desktop\Spring Semester\Intelligent W
eb\Node JS\node_modules\mysql\lib\Connection.js:96:28)
at emitOne (events.js:90:13)
Can someone help me solving this problem

Basically you make several asynchronous db queries in your for loop, but you close your response when the synchronous for loop is ending.So when the db queries end you try to write on an response which is already closed.
var queryString = 'select Tweet.Label, Tweet.TDate, Tweet.TLink, Author.Lable, Author.ALink from Tweet, Author where Tweet.AuthorID IN (select ID from Author where Lable = ?) AND Author.ID IN (select ID from Author where Lable = ?)';
var query = connection.query(queryString, [term,term], function(err, rows) {
console.log(rows);
//res.write(JSON.stringify(rows));
var tweets = JSON.parse(JSON.stringify(rows));
var queries_made = 0;
var queries_success = 0;
tweets.forEach(function(tweet){
connection.query('select Label from Hashtag where ID IN (select HashID from tweethashs where TweetID IN (select ID from Tweet where Label = ?))', [tweet.Label], function(err, rows1) {
res.write("Author: ");
res.write("<a href='" + tweet.ALink + "' target='_blank'>" + tweet.Lable + "</a> <br/>");
res.write("Date: " + tweet.TDate + "<br/>");
res.write("Tweet: " + "<a href='" + tweet.TLink + "' target='_blank'>" + tweet.Label + "</a> <br/>");
var tweet1 = JSON.parse(JSON.stringify(rows1));
for(var j in tweet1){
res.write("Hashtag: ");
res.write(tweet1[j].Label);
}
res.write("<br/><br/>");
queries_success++;
if(queries_made==queries_success)
res.end();
}
);
queries_made++;
})
});
I have added two counter that will track the db queries you requested and the queries that have replied. When those two counters are equal all db transactions and response writes are completed, so you can close the response stream.
Although this is not best practice. You should check the async module or try to use promises with your db queries.
Edit
I changed the code so that the writes occure when the inside queries complete. Be careful now because the order of the writes will not respect the order of the first query but it will write based on what inside query ends first. I must tell you that this is not the best way to accomplish that and you need to read the async module and the promises i told you before. Also take a look at how to mix asynchronous and synchronous operations in javascript!

If you choose to do this by stalling the res.end() you are not sure whether your response will be written in the same order as the tweets are in the array.
The for loop is synchronous but the queries are asynchronous which means they could finish faster than some which started earlier. Which will result theoretically in the writes to happen at random.
What you can use is an async.map.
Basically, instead of that foreach loop you can put something like the following code:
Note: To better understand this first read the async.map code.
//the tweet for which I get the information from DB
var getTweetInfo = function(tweet, callback){
con.query(query, function(err, tweetInformation){
if(err) {
console.log(err);
//with this you are passing the error back into the async.map function. Otherwise it would be lost (Bad javascript :P)
callback("Error getting tweets" + err);
}
else{
//in here you can make any changes to the tweet information before sending it as a response
//this will send the tweetInformation into the results array
callback(null, tweetInformation);
}
});
}
//tweetsArray is the array with tweets
//getTweeetInfo is a function that will get each tweet from the tweets array and will apply the query to it
async.map(tweetsArray, getTweetInfo, function(error, tweetInformationArray){
if (error) {
console.log(error);
}else{
//'results' will hold an array with the information for each tweet in the same order as the initial tweets
res.json({
response: tweetInformationArray
});
}
})
After this you can parse the response on the front-end. The data is consisted, asynchronous (you do not keep the event loop waiting), in order which means you can go through it and display it nicely on the screen.

Related

multiple dependent mysql queries in nodejs...promise not the answer?

I have recently learned how to execute promises in order to run three mysql requests synchronously. I'm disappointed, because I'm learning this is not the solution if I want to run the dependent mysql requests in order. For example, the first gets a variable, which is needed for the second:
My nodejs code:
data.used_time = 7;
var sql;
var sesh_save_val;
var mysql = require('mysql');
//Delete whiteboard session
var con = mysql.createPool({
connectionLimit: 15,
host: "xxxx",
user: "xx",
password: "xxxx",
database: "xxxx"
});
var current_min;
new Promise ((resolve,reject) => {
sql = `SELECT minutes FROM Students WHERE sesh_save = (?)`;
con.query(sql, id, function (err, result)
{
if (err) throw err;
current_min = result[0].minutes;
console.log("no error thrown I think..");
console.log("current_min: " + current_min);
});
resolve("did first func");
}).then((val)=> {
console.log(val);
sql = 'UPDATE Students SET minutes = (?) WHERE sesh_save = (?)';
var min_left = current_min - data.used_time;
console.log("used time: " + data.used_time + " ");
console.log("min_left: " + min_left + " " + "id: " + id);
con.query(sql,[min_left, id], function (err, result)
{
if (err) {console.log(err);}
});
return "did second func";
});
The output is only partially what I would expect. It is:
"did first func", "used_time: 0", min_left: NaN, id: whiteboard-xxxxx-xxxx
"did second func"
"current_min: 4299"
And then error on the line with "con.query(sql,[min_left, id], function (err, result)" because min_left did not exist. Why did min_left not exist? Output showed that current_min existed. However, it existed AFTER the first mysql query resolved, which took so long, that we were able to get into the second .then statement while it's still taking it's time with the first sql query, and the second dependent mysql statement wasn't able to process correctly. How do I fix this?
Thanks for any remarks.
You don't really need 2 queries. You will be good to go with:
sql = 'UPDATE Students SET minutes = minutes - (?) WHERE sesh_save = (?)';
con.query(sql,[data.used_time, id], function (err, result)...

Node function inputting undefined variables into SQL database

I'm sure this is a simple error in my syntax but I'm currently using a nodejs function to input into my SQL database, however, while the overall query works, and some variables get input correctly, a couple are input as undefined, which has thrown me for a loop. I'll input the query below and I presume I either added extra punctuation where not required or something.
con.query("INSERT INTO _rounds(roundnum, roundse, roundtk, winner) VALUES('"+ roundnumres +"', '"+ roundse +"', '"+ roundtk +"', '"+ roundwinner +"')", function (err, result) {
});
For more information, the roundnumres and the roundtk variables are the ones inserted as undefined, and are both defined by a random string generator which looks as follows:
function makese(length) {
var roundse = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var d = 0; d < length; d++ ) {
roundse += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return roundse;
}
var roundse = makese(20);
Any help would be appreciated!
you could do this. you don't have to concat strings using plus
const query = `INSERT INTO _rounds(roundnum, roundse, roundtk, winner) VALUES('${roundnumres}', '${roundse}, '${roundtk}', '${roundwinner}')"
con.query(query, () => {})

Loop through MySQL rows and store results in array

I am trying to store details of affectedRows from a MySQL INSERT query using NodeJS. My mind is melting trying to comprehend callbacks and Promises. Being a single-man dev team I wanted to reach out and ask for the clearest explanation as to how a callback can be applied here in a foreach loop.
The goal should be clear from these few lines of code; store data in the affected_rows[] array.
var affected_rows = [];
asset_array.forEach(function(asset) { // Populate the asset table
var query_string = "INSERT IGNORE INTO " + asset_table + " SET symbol = '" + asset[0] + "', name = '" + asset[1] + "'";
connection.query(query_string, function(err, rows, fields) {
if (err) throw err;
if ( rows.affectedRows > 0 ) {
data_to_push = [asset_table, asset[0], asset[1]];
affected_rows.push(data_to_push);
}
});
});
console.log(affected_rows); // [] for obvious async reasons
One option would be to process the asset_array inside a function and pass a callback into it and when loops through asset_array check if the current index matches the asset_array length (-1). If so call the callback.
var affected_rows = [];
function processAssets(cb) {
var array_len = asset_array_len.length
asset_array.forEach(function(asset, index) {
var query_string = 'INSERT IGNORE INTO ' + asset_table + ' SET symbol = \'' + asset[0] + '\', name = \'' + asset[1] + '\'';
connection.query(query_string, function(err, rows, fields) {
if (err) throw err
if (rows.affectedRows > 0) {
data_to_push = [asset_table, asset[0], asset[1]];
affected_rows.push(data_to_push);
}
if (index === (array_len - 1)) cb()
});
});
}
processAssets(function() {
console.log(affected_rows)
})
Will suggest you to have a look at async Queue.
You can change your code like this to use it.
//2nd Step - Perform each task and then call callback() to move to next task
var q = async.queue(function(query_string, callback) {
connection.query(query_string, function(err, rows, fields) {
if (err) throw err;
if ( rows.affectedRows > 0 ) {
data_to_push = [asset_table, asset[0], asset[1]];
affected_rows.push(data_to_push);
}
callback(); //call next task
});
}, 2); //here 2 means concurrency ie 2 tasks will run in parallel
//Final Step - Drain gives you end of queue which means all tasks have finished processing
q.drain = function() {
//Do whatever you want after all tasks are finished
};
//1st Step - create a queue of all tasks that you need to perform
for (var i = 0; i < asset_array.length ; i++) {
var query_string = "INSERT IGNORE INTO " + asset_table + " SET symbol = '" + asset[0] + "', name = '" + asset[1] + "'";
q.push(query_string);
}

JSON data not behaving

I have a review and submit page where I get data from mysql, send it to my handlebars page as JSON, where I parse and display it.
However, when I plug {{data1}} in my script tag it shows up as
{"id":"32","position":"qweqweq","date":"sadasd&q
where I cant parse it anymore since all the " is now &quot.
However, though it shows up as perfect JSON if I just plug it straight into the handlebars page as {{data1}}
[{"id":"32","position":"qweqweq","date":"sadasd","howUhear":"advertisement".....
Please help steer me in the right direction, thank you.
my index.js
router.get('/jobappsum', authenticationMiddleware(), function(req, res){
const db = require('../db.js');
const userIDpre = (`req.session.passport.user: ${JSON.stringify(req.session.passport)}`);
var userID = userIDpre.match(/\d+/)[0];
db.query("SELECT * FROM Page1 WHERE id = '"+ userID +"'", function(err, results1, field){
console.log(results1);
sum1 = JSON.stringify(results1);
db.query("SELECT * FROM Page2 WHERE id = '"+ userID +"'", function(err, results2, field){
sum2 = results2;
db.query("SELECT * FROM Page3 WHERE id = '"+ userID +"'", function(err, results3, field){
sum3 = results3;
}) res.render('jobappsum', {
data1: sum1,
data2: sum2,
data3: sum3
});
;
});
});
});
jobappsum.hbs
{{> header }}
<p id="demo"></p>
<script>
var obj = JSON.parse({{data1}});
//{{data1}} " shows up up as &quot and gives me error
obj = obj[0];
document.getElementById("demo").innerHTML = obj.id + ", " + obj.position;
</script>
{{result}}
//will show up as the correct JSON
Your code:
res.render('jobappsum', {
Your "jobappsum" is meant to output HTML, so it encodes your quotes. It transforms " to &quot ; as you can see. When you display it in your view, you see " as normal, but it's not the " character itself. It's &quot ; and is displayed as " in the browser.
So, you should use a renderer that doesn't HTML escape.
If you don't want something in the output escaped in your view, try:
!{data1}
or
{{{data1}}}
within the view.
Or you can use decodeURI() function to unescape the escaped JSON before parsing:
var obj = JSON.parse(decodeURI("{{data1}}"));
If using Swig.js, you can use:
{{ data1|json|raw }}

How to pass parameters to mysql query callback in nodejs

I'm trying to figure out the correct way of passing custom data to a query call to be made available in the callback.
I'm using MySQL library in nodejs (all latest versions).
I have a call to connection.query(sql, function(err, result) {...});
I couldn't find a way to 1) pass custom data/parameter to the call so that 2) it can be made available when the callback is invoked.
So what is the proper way of doing so?
I have the following (pseudo-code):
...
for (ix in SomeJSONArray) {
sql = "SELECT (1) FROM someTable WHERE someColumn = " + SomeJSONArray[ix].id;
connection.query(sql, function (err, result) {
...
var y = SomeJSONArray[ix].id;
};
}
From the code above, I need to be able to pass the current value of "ix" used in the query to the callback itself.
How do I do that?
If you are using node-mysql, do it like the docs say:
connection.query(
'SELECT * FROM table WHERE id=? LIMIT ?, 5',[ user_id, start ],
function (err, results) {
}
);
The docs also have code for proper escaping of strings, but using the array in the query call automatically does the escaping for you.
https://github.com/felixge/node-mysql
To answer the initial question with a complete answer/example to illustrate, wrap the callback with an anonymous function which immediately creates a scope containing a "snapshot" if you will of the data passed in.
var ix=1;
connection.query('SELECT 1',
(function(ix){
return function(err, rows, fields) {
console.log("ix="+ix);
console.log(rows);
};
})(ix));
For those new to this concept as I was 20 minutes ago, the last })(ix)); is the outer var ix=1 value which is passed into (function(ix){. This could be renamed (function(abc){ if you changed the console.log("ix="+abc);
fwiw (Thanks Chris for the link which filled in the blanks to arrive at a solution)
While it is OK to pass variables or objects to a mysql query callback function using the tactic described earlier -- wrapping the callback function in an anonymous function -- I think it is largely unnecessary, and I'll explain why with an example:
// This actually works as expected!
function run_query (sql, y) {
var y1 = 1;
connection.query (sql, function (error, rows, fields) {
if (! error)
{
var r = rows[0];
console.log ("r = " + r[1]);
console.log ("x = " + x);
console.log ("y = " + y);
console.log ("y1= " + y);
console.log ("");
}
else
{
console.log ("error = " + error);
}
});
};
var x = 5;
console.log ("step 1: x = " + x);
run_query ("SELECT 1", x);
x = x + 1;
console.log ("step 2: x = " + x);
run_query ("SELECT 1", x);
x = x + 1;
console.log ("step 3: x = " + x);
Produces the following output:
step 1: x = 5
step 2: x = 6
step 3: x = 7
r = 1
x = 7
y = 5
y1= 5
r = 1
x = 7
y = 6
y1= 6
The fear is that the second call to run_query() will overwrite the variable y and/or y1 before the first call to run_query() has a chance to invoke its callback function. However, the variables in each instance of the called run_query() function are actually isolated from each other, saving the day.
MySQL con.query has overloaded function. Inside of callback you use global variable or any variable that is passed into your function parameter. For example:
Example 1: it takes sql string and callback function
var adr = 'Mountain 21';
var sql = 'SELECT * FROM customers;
con.query(sql, function (err, result) {
if (err) throw err;
console.log(adr);
});
Example 2: it takes sql string, parameter and callback function
var adr = 'Mountain 21';
var sql = 'SELECT * FROM customers WHERE address = ?';
con.query(sql, [adr], function (err, result) {
if (err) throw err;
console.log(adr);
});