Nested SQL queries in function in node.js - javascript

First of all, i'm new in JS. I have a function that possibly can use multiple requests to get the final data. How can i do this in the right way? In this example participants won't pushed to the dialogs array because it's in async call.
function getDialogs(token, callback) {
//getting user id
con.query("SELECT user_id FROM users_tokens WHERE user_token = '" + token + "'", function(error, results) {
if (error) {
throw error;
}
var userId = results[0].user_id;
//getting all conversation
con.query("SELECT cc.id as conversation_id, cc.type FROM chat_conversations cc INNER JOIN chat_participants cp ON cc.id = cp.conversation_id WHERE cp.user_id = " + userId + " GROUP BY cc.id", function (error, results) {
if (error) {
throw error;
}
var dialogs = [];
for (let i = 0; i < results.length; i++) {
var dialog = {id: results[i].conversation_id};
//getting chat participants
con.query("SELECT user_id FROM chat_participants WHERE conversation_id = " + results[i].conversation_id + " AND user_id != " + userId, function (error, results) {
var participants = [];
for (let j = 0; j< results.length; j++) {
participants.push(results[j].user_id);
}
dialogs[participants] = participants;
});
dialogs.push(dialog);
}
callback(dialogs);
});
});
}

Technically you can use a single request like this
SELECT user_id FROM chat_participants WHERE conversation_id IN (
SELECT
cc.id as conversation_id,
cc.type
FROM
chat_conversations cc
INNER JOIN chat_participants cp ON cc.id = cp.conversation_id
WHERE
cp.user_id IN (
SELECT
user_id
FROM
users_tokens
WHERE
user_token = "TOKEN"
)
GROUP BY
cc.id
)
but there are a few problems with this approach as well.
First of all, it seems like you are only using the user_id of your first row, so please use LIMIT 1 in such cases.
Second of all, it seems like user_id won't ever have a duplicate, so make it a primary key.
Third of all, don't concat your token, node mysql supports having placeholders using ? in your query, like this:
con.query("SELECT * FROM ? WHERE user_id = ?", ["table_name", "userid"])
Fourth of all, promisify your requests so you don't have a callback hell, do something like:
function promiseRequest(query, placeholders) {
return new Promise((res, rej) => {
con.query(query, placeholders, (err, data) => {
if (err) rej(err);
res(data);
});
});
}

Related

NodeJS insert in mysql from array

I has simple function in nodejs, which add result to MYSQL. But result is coming in array, and i can't correctly add this data to the table.
My functions:
function getPreviousGames() {
const previousGames = [];
let gameHash = generateHash(crashHash);
for (let i = 0; i < 1; i++) {
const gameResult = crashPointFromHash(gameHash);
previousGames.push({ gameHash, gameResult });
gameHash = generateHash(gameHash);
}
return previousGames;
}
function verifyCrash() {
const gameResult = crashPointFromHash(crashHash);
const previousHundredGames = getPreviousGames();
for (let i = 0; i < 1; i++) {
var sql = "INSERT INTO `admin_dev`.`crash_numbers` (`id`, `hash`, `multiplier`, `status`, `created_at`, `updated_at`) VALUES (NULL, '" + previousHundredGames(gameHash) + "', '" + gameResult + "', '0', now(), now());";
connection.query(sql, function (err, result) {
if (err) throw err;
console.log("Success!");
});
}
return { gameResult, previousHundredGames };
}
And i received result:
{
previousHundredGames: [
{
gameHash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
gameResult: 2.52
}
]
}
And i can't correcly add this received data to mysql table.
I need add from previousHundredGames value gameHash to hash column, and gameResult value to multiplier column.
How i can correctly made it?
If you are using a query, it is as simple as building an INSERT query.
//Start the query
let query= 'INSER INTO (hash, gameResult) VALUES ';
//Add multiple rows from the array
for (let aGame of previousGames)
query+= `( "${aGame.gameHash}", ${aGame.gameResult}), `
//Remove the last comma
query= queryBuilder.substring(0, queryBuilder.length - 1)
Here is documentation on inserting multiple rows in a single query.
https://www.mysqltutorial.org/mysql-insert-multiple-rows/

Get the count of the times date is inserted to know the last for loop iteration in Node.js

I want to insert the stockid's in the database in a different row and I am using a for loop. I also want to know the last iteration, but because of the async of javascript I am having trouble getting this to work. The current output is that j only increasing by 1 and print 4 times. I will appreciate any assistance or recommendation. Thanks.
var order_id = 827283383;
var stockid = [1,2,3,4];
for(i = 0; i < stockid.length; i++) {
db.query("insert into order_detail( order_id, stock_id) values ('" + order_id + "','" + stockid[i] + "') ;", function (err, rs) {
var j = 0;
if (err) {
console.log(err);
}
else{
j++; // aimed to use as a counter
console.log(j);
}
if(j < stockid.length-1){ //aimed to get last iteration
console.log('This is the last row');
}
});
}
Only one output return when the data is select in then. code below:
const query = promisify(db.query.bind(db));
Promise.all(stockid.map(id => query("insert into order_detail( order_id, stock_id) values ('" + order_id + "','" + id + "') ;")))
.then(
db.query('select *, order_detail.order_id as orderid from sodiq_business.order_detail join sodiq_business.order on sodiq_business.order.order_id = sodiq_business.order_detail.order_id join sodiq_business.stock on sodiq_business.stock.stock_id = sodiq_business.order_detail.stock_id join sodiq_business.customer on sodiq_business.customer.id = sodiq_business.stock.seller_id join sodiq_business.product on sodiq_business.product.prod_id = sodiq_business.stock.prod_id where order_detail.order_id = ?',[order_id], function (err, rs) {
if (err) {
console.log(err);
}
else{
console.log(rs)
})
As mentioned by #Tapler in the comment, your j sets to 0 in every iteration. Make it like
var order_id = 827283383;
var stockid = [1,2,3,4];
var j = 0;
for(i = 0; i < stockid.length; i++) {
db.query(`insert into order_detail( order_id, stock_id) values (${order_id},${stockid[i]})`, function (err, rs) {
if (err) {
console.log(err);
}
else{
j++; // aimed to use as a counter
console.log(j);
}
if(j < stockid.length-1){ //aimed to get last iteration
console.log('This is the last row');
}
});
}
Also read about Prepared statements and SQL Injection to avoid attacks on the database. This is very much prone to database attacks.
I'd suggest you to promisify db.query:
const { promisify } = require('util');
const query = promisify(db.query.bind(db));
Promise.all(stockid.map(id => query("insert into order_detail( order_id, stock_id) values ('" + order_id + "','" + id + "') ;")))
.then(console.log)
.catch(console.error);

Trying to update user points every X, sets all their points to undefined

I'm currently trying to update all user points every 1 minute (currently at 5 seconds for testing purposes), when I try to run it, it gets all the users, but then it sets their points to undefined.
setInterval(async function () {
var uPoints;
await db.each("SELECT points points, id id FROM users", function (err, row) {
if (err) {
console.log(err);
}
var u;
for (u in client.users.array()) {
uPoints = row.points + 10;
}
});
var u, user;
for (u in client.users.array()) {
user = client.users.array()[u];
tools.setPoints(uPoints, user.id.toString());
console.log('Updated ' + user.id.toString() + ' to ' + uPoints);
}
}, 5000);
});
tools.setPoints
module.exports.setPoints = function (amnt, id) {
db.run('UPDATE users SET points = ? WHERE id = ?', amnt, id);
}
The update is executed before or in parallel with the SELECT.
You can fix that if you move the UPDATE inside the callback like so:
setInterval(async function () {
var uPoints;
await db.each("SELECT points points, id id FROM users", function (err, row) {
if (err) {
console.log(err);
}
var u;
for (u in client.users.array()) {
uPoints = row.points + 10;
}
var user;
for (u in client.users.array()) {
user = client.users.array()[u];
tools.setPoints(uPoints, user.id.toString());
console.log('Updated ' + user.id.toString() + ' to ' + uPoints);
}
});
}, 5000);
});
I'm not sure what DB client you are using but generally, you don't want to mix await with callbacks.
You either want to have:
const rows = await db.each("SELECT points points, id id FROM users");
or
db.each("SELECT points points, id id FROM users", function (err, rows) {..});
You should check if your db client supports async/await or you can use promisify.

Render my ejs file after my query finishes

I want to make sure that my application will only call res.render('pages/students' ...) after all my queries finishes and I get all the necessary data. However, I completely have no clue how to do this at all and I am having trouble applying all the examples I see online onto my code. Can someone give me a hand? (There are two con.query(...) in my route. I want to only render('pages/students'...) after both of these con.query completely finishes. Therefore, I am using async to run all my queries to completion. However now for some reason my page wont event load.
app.get('/admin', function(req, res) {
var sql =
'SELECT events.id, name, description, MONTHNAME(start_time) AS month, DAY(start_time) AS day, ' +
"YEAR(start_time) AS year, DATE_FORMAT(start_time, '%h:%i%p') AS start_time, HOUR(start_time) AS start_hour, MINUTE(start_time) AS start_minute, " +
"DATE_FORMAT(end_time, '%h:%i%p') AS end_time, HOUR(end_time) AS end_hour, MINUTE(end_time) AS end_minute, location, max_capacity, hidden_sign_up, " +
'eventleaders.first_name AS eventLeader FROM events LEFT JOIN eventleaders_has_events ON events.id = eventleaders_has_events.events_id ' +
'LEFT JOIN eventleaders ON eventleaders_has_events.eventleaders_id = eventleaders.id;';
var events = {};
con.query(sql, function(err, results) {
if (err) throw err;
async.forEachOf(results, function(result, key, callback) {
var date = result.month + ' ' + result.day + ', ' + result.year;
var other = 'N/A';
if (result.other !== null) {
other = result.other;
}
if (typeof events[date] === 'undefined') {
events[date] = {};
}
var studentAttendees = {};
var sql =
'SELECT * FROM students INNER JOIN students_has_events ON students.id = students_has_events.Students_id ' +
'WHERE students_has_events.Events_id = ?';
var values = [result.id];
con.query(sql, values, function(err, results1) {
if (err) throw err;
async.forEachOf(results1, function(result1, key, callback) {
studentAttendees[result1.first_name + ' ' + result1.last_name] = {
netID: result1.netID,
phoneNumber: result1.phone,
email: result1.email
};
});
//still need to get the event leader attendees
events[date][result.name] = {
startTime: result.start_time,
location: result.location,
endTime: result.end_time,
description: result.description,
eventLeader: result.eventLeader,
numberRegistered: result.hidden_sign_up,
maxCapacity: result.max_capacity,
poster: '/images/sample.jpg',
other: other,
attendees: studentAttendees
};
});
});
});
});
Two options:
app.get('/admin', function(req, res) {
...
con.query(sql, function(err, result) {
...
con.query(sql, values, function(err, result) {
events[date][currRecord.name] = {
...
};
// put it here
console.log(events);
res.render('pages/admin', {
events: events
});
});
}
});
});
Or use the Promise version of mysql
const mysql = require('mysql2/promise');
// make this async
app.get('/admin', async function(req, res) {
try {
...
// might want to move this elsewhere inside async function
const con = await mysql.createConnection({host:'localhost', user: 'root', database: 'test'});
const result1 = await con.query(sql);
// run your logic preferably inside map
// result2 is an array of Promises
const result2 = result1.map((result, index) => {
var currRecord = result[index];
var date =
currRecord.month + ' ' + currRecord.day + ', ' + currRecord.year;
var other = 'N/A';
if (currRecord.other !== null) {
console.log('here');
other = currRecord.other;
}
if (typeof events[date] === 'undefined') {
events[date] = {};
}
//get all the student attendees of this event
var studentAttendees = {};
var sql =
'SELECT * FROM students INNER JOIN students_has_events ON students.id = students_has_events.Students_id ' +
'WHERE students_has_events.Events_id = ?';
var values = [currRecord.id];
return con.query(sql)
})
// result3 is an array of results. If your results is also an array, the structure might look lik [[student], [student]] etc.
const result3 = await Promise.all(result2);
// run your logic to get events
console.log(events);
res.render('pages/admin', {
events: events
});
} catch (e) {
// handle error
console.error(e)
}
});

Javascript call by reference not working

I read this and tried implementing my function so that data doesn't change back, but it isn't working with me.
I have an array of objects, where I send them one by one to another function, to add data.
queries.first(finalObject.sectionProjects[i]);
for each one of the sectionProjects, there is a variable achievements, with an empty array.
Upon sending each sectionProject to the queries.first function, I reassign achievements,
finalObject.sectionProjects[i].achievements = something else
When I return from the queries.first function, I lose the data I added.
Am I doing something wrong?
Here's the function:
module.exports = {
first:function(aProject) {
// Latest achievements
var query =
" SELECT ta.description, ta.remarks, ta.expectedECD " +
" FROM project pr, task ta, milestone mi " +
" WHERE pr.ID = mi.project_ID AND mi.ID = ta.milestone_ID " +
" AND ta.achived = ta.percent AND pr.ID = " + aProject.project_id +
" ORDER BY pr.expectedECD " +
" LIMIT 5;"
;
var stringified = null;
pmdb.getConnection(function(err, connection){
connection.query(query, function(err, rows){
if(err) {
throw err;
}else{
var jsonRows = [];
for( var i in rows) {
stringified = JSON.stringify(rows[i]);
jsonRows.push(JSON.parse(stringified));
}
connection.release();
aProject.achievements = jsonRows;
upcomingTasks(aProject);
}
});
});
}
}
This is pmdb.js:
var mysql = require("mysql");
var con = mysql.createPool({
host: "localhost",
user: "user",
password: "password",
database: "database"
});
module.exports = con;
This is the main function that calls queries.first:
// ...Code...
//Number of section projects
var len = jsonRows.length;
console.log("Number of section projects: " + len);
var internal_counter = 0;
function callbackFun(i){
(finalObject.sectionProjects[i]).achievements = [];
queries.first(finalObject.sectionProjects[i]);
if(++internal_counter === len) {
response.json(finalObject);
}
}
var funcs = [];
for (var i = 0; i < len; i++) {
funcs[i] = callbackFun.bind(this, i);
}
for (var j = 0; j < len; j++) {
funcs[j]();
}
Read That Answer twice. Objects acts as a wrapper for the scalar primitive property. You are passing the Objects in to the "queries.first" function.
See this Object reference issue
Edited for the sample code
pmdb.getConnection(function(err, connection){
connection.query(query, function(err, rows){
if(err) {
throw err;
}else{
var jsonRows = [];
for( var i in rows) {
stringified = JSON.stringify(rows[i]);
jsonRows.push(JSON.parse(stringified));
}
connection.release();
aProject.achievements = jsonRows;
upcomingTasks(aProject)
}
});
});
that is not a problem. change it like this. "upcomingTasks" is not a callback function. it is execute after assign the achievements in aProject

Categories

Resources