Node MySQL Populate Children as an array - mysql

I'm new to MySQL and coming from a Mongo background, so I'm used to Mongoose's .populate() method for retrieving a document's associations as a sub-array. Is there a way to do that in MySQL? Right now I'm trying to retrieve a single quiz with all of its questions in a single query. I have a quizzes table and a questions table, which has a parent_quiz_id field that refers to a quiz id. I've tried several permutations of SELECT * FROM quizzes JOIN questions ON questions.parent_quiz_id = quizzes.quiz_id WHERE quizzes.quiz_id = ? but they give me all the quiz information for each question, and that seems inefficient.
Is there a way for a single query to retrieve the quiz information once, and in subsequent rows populate all the questions that belong to that quiz?
This is what I'm doing now, with two separate queries:
connection.beginTransaction(function(err) {
if (err) { throw err; }
//First get the quiz data
connection.execute('SELECT * FROM quizzes WHERE quiz_id = ? LIMIT 1', [id], function (err, quiz_results) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
const quiz = quiz_results[0];
if(!quiz){
return done({ message: "No quiz found with that id"}, false);
}
//Now get all of the quiz's questions
connection.execute('SELECT * FROM questions WHERE parent_quiz_id = ?', [id], function (err, question_results) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
quiz['questions'] = question_results;
return done(null, quiz);
});
});
});
});
But I'd rather not use two separate queries. For references, I'm using Node MySQL2

Related

NodeJS get the mysql query result

I have a table containing an ISBN number and a the available number of books. I want to make a query to to ISBN number and get the response about the number of the books with this ID, but I don't know how to write the proper function to get the query result?
db.checkPeldanyszam( rentISBN, response, callback) => {
if (err) {
res.status(500).render('error', { message: `Insertion unsuccessful: ${err.message}` });
} else {
console.log(err);
next();
}
});
exports.checkPeldanyszam = (req,callback) => {
console.log(req);
const query = `SELECT Peldanyszam, IF(Peldanyszam>0, "Jo", "Hibas") as isOkay FROM konyv
WHERE ISBN=${req};`
pool.query(query,callback);
}
Thank you.

Undefined push to array - Basic Application using ExpressJs and MySQL

First of all, I have to tell you I'm pretty noob in this "universe". I'm using: ExpressJs, MySql, Body-Parser, Express-session, Ejs template for creating an Basic Contacts Application in Node.
My database is composed from 3 tables:
user (user_id, first, second name, username, password)
contacts (ct_id, first, second name, phone numb.)
user_contacts (user_id, ct_id) --> foreign keys for user and contacts
I want to listing on /myProfile page all details about user and his contacts.
I don't know how to handle the select queries.
So, after some documentation I did this:
conn.query('SELECT * FROM user_contacts WHERE user_id= ?', req.session.user_id, function (err, result) {
if(err) throw err;
console.log(result);
var queryArray = "";
for(var i = 0; i < result.length; i++){
queryArray += `SELECT * FROM contacts WHERE ct_id= ${result[i].ct_id}; `;
}
console.log(queryArray);
conn.query(queryArray, function (err, result) {
if(err) throw err;
console.log(result);
res.render('myProfile/contacts', {
title: `${req.session.user_nickname}'s Contacts`,
data: result
});
});
});
But I have an error
ER_PARSE_ERROR: You have an error in your SQL syntax;
..when queryArray.length > 1
I searched and it's something about Multiple statement queries but I dont know how to solve it.
Edit 2:
I modify my code..
conn.query('SELECT * FROM user_contacts WHERE user_id= ?', req.session.user_id, function (err, result) {
if(err) throw err;
var datas = [];
for(var i = 0; i < result.length; i++){
getContacts = function(query){
conn.query(query, function (err, result) {
console.log('Creating data');
data = {
user: req.session.user_nickname,
contact:{
ct_firstName: result[0].ct_firstName,
ct_SecondName: result[0].ct_SecondName,
ct_PhoneNumber: result[0].ct_PhoneNumber
}
}
return data;
});
}
console.log('Send data to array');
datas.push(getContacts(`SELECT * FROM contacts WHERE ct_id = ${result[i].ct_id}`));
}
console.log(datas); // [ undefined, undefined ]
res.render('myProfile/contacts',{
title: `${req.session.user_nickname}'s profile`,
data: datas
})
});
But now my array contain undefined objects?? Any solution?
Maybe is something about scope?
My result:
Send data to array
Send data to array
[ undefined, undefined ]
Creating data
Creating data
I push the object to array before creating it. How is it possible?
1797,
I noticed you have several small queries grabbing the contact info for a given user. You could simplify your code by combining your queries into a single one. Often times 1 big query is more efficient (plus it's easier to maintain). I'm using a join. More info here.
const contacts = [];
const query = "
SELECT c.*
FROM user_contact uc
JOIN contact c ON uc.contact_id = c.contact_id
WHERE uc.user_id = ?
GROUP BY c.contact_id
";
conn.query(query, req.session.user_id, (err, results) => {
if (err) throw new Error(err);
// it seems that this could just be 'contacts = results' since they
// have the same structure
contacts = results.map(result => {
return {
ct_firstName: result[0].ct_firstName,
ct_SecondName: result[0].ct_SecondName,
ct_PhoneNumber: result[0].ct_PhoneNumber
};
});
res.render('myProfile/contacts',{
title: `${req.session.user_nickname}'s profile`,
data: contacts
});
});

Select and Insert in node loop not working as expected

I am trying to insert data in table before checking that Is it already exist in database table? If exist then loop continue with console message "Already exist" and If not exist then I try to insert in table. But some of the records are already in database table then also Inserted in table.
Following My NodeJS Code
(function loop(index){
if(index==apires.items.length){
console.log("Cron completed");
res.send("Cron completed");
return false;
}
inventoryObj = apires.items[index];
hash_name = inventoryObj.market_hash_name;
db.query('SELECT market_hash_name FROM inventory_master WHERE market_hash_name = "'+hash_name+'"', function(err,result, fields){
if(result.length){
console.log('already exist');
loop(++index);
}
else
{
var post = {data_here};
var query = db.query('INSERT INTO inventory_master SET ?', post, function (error, results, fields) {
if (error) throw error;
loop(++index);
});
}
});
})(0);
I guess this is happening due to the asynchronous behavior of your code. You can use async library to make it working, this will allow your code to execute on element at a time. Example
// assuming apires.itemsis an array
async.each(apires.items, function(inventoryObj, callback) {
hash_name = inventoryObj.market_hash_name;
db.query('SELECT market_hash_name FROM inventory_master WHERE market_hash_name = "'+hash_name+'"', function(err,result, fields){
if(result.length){
console.log('already exist');
callback('success'); // go for next iteration
}
else
{
var post = {data_here};
var query = db.query('INSERT INTO inventory_master SET ?', post, function (error, results, fields) {
if (error) throw error;
callback('success'); // go for next iteration
});
}
});
}, function(err) {
//once all finished, it will come here,if no error occurred then err will be null
});

Store mysql query rows in variable for later use

I'm doing a monitoring system project in which I have Arduino sensors data being sent to a node.js server (thru GET requests) and then stored in a MySQL DB.
Whenvever I successfully send data to the server, it connects to the MySQL DB and queries the last 5 received records to do some processing.
Therefore, I need to store the rows of those 5 records in a variable for later use. Meaning that I have to get rows from a connection.query in a variable.
I read that the fact that I'm not able to do this is because node.js being async. So my questions are:
Is it possible to do the described tasks the way I'm trying?
If not, is there any other way to do so?
I'm not putting the whole code here but I'm running a separated test that also doesn't run properly. Here it is:
var mysql = require('mysql');
var con = mysql.createConnection({
host : "127.0.0.1",
user : "root",
password: "xxxx",
database: "mydb",
port : 3306
});
var queryString = "SELECT id, temp1, temp2, temp3, temp4, level_ice_bank, flow FROM tempdata ORDER BY id DESC LIMIT 5";
con.connect(function(err) {
if (err) throw err;
});
var result_arr = [];
function setValue (value) {
result_arr = value;
}
con.query(queryString, function (err, rows, fields) {
if (err) throw err;
else {
//console.log(rows);
setValue(rows);
}
});
console.log(result_arr);
It logs:
[]
But if I uncomment console.log(rows); it logs what I need to store in the variable result_arr.
Thanks in advance to all.
You're seeing this behaviour because con.query(...) is an asynchronous function. That means that:
console.log(result_arr);
Runs before:
con.query(queryString, function (err, rows, fields) {
if (err) throw err;
else {
//console.log(rows);
setValue(rows);
}
});
(Specifically, the setValue(rows) call)
To fix this in your example, you can just do:
con.query(queryString, function (err, rows, fields) {
if (err) throw err;
else {
setValue(rows);
console.log(result_arr);
}
});
If you want to do more than just log the data, then you can call a function which depends on result_arr from the con.query callback, like this:
con.query(queryString, function (err, rows, fields) {
if (err) throw err;
else {
setValue(rows);
doCleverStuffWithData();
}
});
function doCleverStuffWithData() {
// Do something with result_arr
}

MySQL Nesting Relations

I use backbone and need to nest Answers in Questions and Questions in Categories.
My problem is the data I get from MySQL.
I would like to have an array I can easily use with backbone, starting at the top (Category) and nest down to the bottom (Answers).
[Category1: [Question1: [Answer1: {...} ] ] ]
I use the following query to get all my MySQL data:
var getRecord = function(callback) {
var options = {
sql: 'SELECT * FROM Categories ' +
'LEFT JOIN Questions ON Categories.idCategories = Questions.idCategory ' +
'LEFT JOIN Answers ON Questions.idQuestions = Answers.idQuestion ',
nestTables: true
}
req.app.sql.query(options, function(err, result) {
if (err)
return callback(err, null)
outcome.record = result
return callback(null, 'done')
})
}
And the output looks something like this:
[
0: [CategoryObj, QuestionObj, AnswerObj]
1: ...
]
The MySQL Node Package does not nest 1:n relations, instead it creates an array with the length of most matches, so in the case I have 2 Categories, with each two Questions, with each two Answers -> Array length of 8, because I have 8 Answers in total.
But I cannot nest this array, in backbone collections without writing crazy loops and hacks.
Am I doing something wrong in the query or is there a packages that does the parsing job?
(I'm used to MongoDB (using embedded documents was quite easy) and now I have to use MySQL for this project..)
This is the MySQL Node Package on npm
There is nothing wrong with the package or how you use it. It just gives you the results returned by MySQL. As you probably know, MySQL itself does not format its results in a "nested" way when you're dealing with 1:n relations. If you use JOINs, it will give you a table with a row for each result it found. As it's a "table-formated" result, all rows have the same number of cells.
You can try to see the result of your request in PHPmyAdmin for example.
Thus, you have to post-format the results. There are probably modules to do that, but I have never used one yet.
If you want to do it yourself, you could do something like :
var nestedResult = {};
result.forEach(function(val){
var category = val[0],
question = val[1],
answer = val[2];
if (!nestedResult[category]){
nestedResult[category] = {};
}
if (!nestedResult[category][question]){
nestedResult[category][question] = [];
}
nestedResult[category][question].push(answer);
});
Which will give you something like :
{
"mysql" : {
"what is JOIN" : ["answer 1 blabla....","answer 2 blabla"],
"innoDB vs MyISAM" : ["answer 1","answer 2"]
},
"php" : {
"why no php 6 ?" : ["answeeeerr"]
}
}
I ended up parsing it myself. For some reason I was not able to find a well working ORM helper, that could do this job for me. Anyway I tried to avoid this solution, but here you go if you have the same problem one day this might help.
var async = require('async')
var getAnswers = function (id, callback) {
req.app.sql.query('SELECT * FROM Answers WHERE idQuestion LIKE ?', [id], function(err, result) {
if (err)
return callback(err, null)
return callback(null, result)
})
}
var getQuestions = function (id, callback) {
req.app.sql.query('SELECT * FROM Questions WHERE idCategory LIKE ?', [id], function(err, result) {
if (err)
return callback(err, null)
// Pair answers to questions
async.times(result.length, function(n, next) {
getAnswers(result[n].idQuestions, function (err, answers) {
result[n].answers = answers
next(err, result[n])
})
}, function(err, questions) {
callback(null, questions)
})
})
}
var getRecord = function(callback) {
req.app.sql.query('SELECT * FROM Categories', function(err, result) {
if (err)
return callback(err, null)
// Pair questions to categories
async.times(result.length, function(n, next) {
getQuestions(result[n].idCategories, function (err, questions) {
result[n].questions = questions
next(err, result[n])
})
}, function(err, final) {
callback(null, final)
})
})
}
var asyncFinally = function(err, results) {
if (err)
return next(err)
// we call results[0] because async.times leaves all the categories in there..
// sendSomewhere( results[0] )
}
async.parallel([getRecord], asyncFinally)