MySQL Nesting Relations - mysql

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)

Related

Struggling to print multiple MySQL rows to a web page using Node.js

Trying to build a simple web app using Node.js. One of the pages requires multiple rows from one of my SQL table columns to be printed out like so:
Here is the most recently added string from column X
Here is the second most recently added string
Here is the third
Here is the fourth
Here is the fifth
I've got the hang of printing the first row using something like this:
var q = 'SELECT mycolumn AS string FROM users';
connection.query(q, function (error, results) {
if (error) throw error;
var string = results[0].string;
res.render('thanks', {string: string});
});
});
However, I can't figure out how to print the results from rows 1-5. Closest I've got from Google is:
connection.query('SELECT mycolumn AS string FROM users LIMIT 5', function (error, results) {
if (error) throw error;
var string = JSON.stringify(results);
res.render('thanks', {string: string});
});
});
but this gives me [{"mycolumn":"The content I'm trying to isolate"},{.....etc ] as the output and I can't figure out a way to clean that up.
I believe that by specifying results[0] you are only getting the record with the index of 0 - which should be the first one.
Here is a sample of select query in one of my apps:
app.get('/categories', async(req, res) => {
try {
const allCategories = await pool.query("SELECT id, title FROM categories WHERE visible=true ORDER BY id desc");
res.json(allCategories.rows);
} catch (error) {
console.error(error.message);
}
});
You can see I don't have the [0] on my rows result.
Solved it:
connection.query(q, function (error, results) {
if (error) throw error;
var string1 = results[0].string;
var string2 = results[1].string;
var string3 = results[2].string;
res.render('thanks', {string1: string1, string2: string2, string3: string3});
});
});```

Getting value only from Node.js MySQL result object

I am trying to get a list of distinct values from a MySQL Column and return these in a single JSON array.
I have this current code:
app.get('/experiences/', function(req, res) {
res.setHeader('Access-Control-Allow-Origin', http://localhost:3000');
connection.query('SELECT DISTINCT experience FROM questions', function(err, data) {
err ? res.send(err) : res.json(data);
});
});
I want the result to look like:
{experience: ["1-3","1-5","5+"]}
but it currently looks like:
[{"experience":"1-3"},{"experience":"1-5"},{"experience":"5+"}]
You can reformat the data object you get by looping on the array and recreate an object with the correct format.
Like this :
connection.query('SELECT DISTINCT experience FROM questions', function(err, data) {
if(err)
res.send(err)
else {
let experiences = [];
data.forEach(function(
experiences.push(d.experience);
}
result = {experience : experiences };
res.json(result);
}
});

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
});
});

nodejs mysql queries showing only one records instead of all records in the database

Am trying to retrieve all the database records from a table called post using node js but the problem is that only one record is retrieved instead of all.
In php I can use while() loop to loop through the database record to get all data.
Currently, I do not know how to neatly loop through the database in nodejs to get all the records from database. Some Stackoverflow scholars suggest using await/async method but i do not know to to implement it on the code below to make it work. can someone help me fix the issue.
var connection = require('./config');
module.exports.getpost = function (req, res) {
connection.query('SELECT * FROM posts', function (error, results, fields) {
if (error) {
console.log('error');
res.json({
status : false,
message : 'there are some error with the query'
});
} else {
var postid = results[0].id;
var title = results[0].title;
var content = results[0].content;
var type = -1;
console.log(title);
// Checking user status
connection.query('SELECT count(*) as cntStatus,type FROM like_table WHERE userid= ? and postid=?', [userid,postid], function (error, results, fields) {
if (error) {
console.log('error');
res.json({
status : false,
message : 'there are some error with the query'
});
} else {
var total_count = results[0].cntStatus;
if(total_count > 0){
type = results[0].type;
}
var total_count = results[0].cntStatus;
var result = {
"id" : postid,
"title" : title,
"content" : content,
"type" : type,
"likes" : total_count
};
console.log('query okay');
res.json({
//data:results,
data : result
});
}
});
}
});
}
I'm assuming you're using mysql npm. In that case I'm not sure what is the problem in your case. Results param is an array of rows returned by your select statement. So you can use loop to iterate trough all the rows.
You don't actually need to use async/await (which doesn't have any advantage in terms of functionality but looks cleaner). But if you want to get rid of callbacks you need to wrap connection query into a promise or use mysql2 npm which has promise interface. Here is how you can iterate trough all the rows from your select using async/await instead of callback:
var connection = require('./config');
module.exports.getpost = async function (req, res) {
try {
const queryResult = await query('SELECT * FROM posts');
queryResult.forEach(row => {
console.log(row.title);
})
} catch (err) {
console.log('error');
res.json({
status: false,
message: 'there are some error with the query'
});
}
}
Please note that you need to use nodejs 8 to run the code with async/await.
Also you don't need to do another query inside of your posts query, you can merge those two using SQL join
async waterfall - Runs an array of functions in series, each passing their results to the next in the array. However, if any of the functions pass an error to the callback, the next function is not executed and the main callback is immediately called with the error.
var connection = require('./config');
var async = require('async');
module.exports.getpost = function (req, res) {
var arrayOfFuncs = [];
var func_1 = function(callback) {
connection.query('SELECT * FROM posts', function (error, results, fields) {
if (error) {
console.log('error');
callback(error, null);
} else {
var toPass = {};
toPass.postid = results[0].id;
toPass.title = results[0].title;
toPass.content = results[0].content;
toPass.type = -1;
callback(null, toPass);
}
})
}
arrayOfFuncs.push(func_1);
var func_2 = function(prevData, callback) {
connection.query('SELECT count(*) as cntStatus,type FROM like_table WHERE userid= ? and postid=?', [userid,prevData.postid], function (error, results, fields) {
if (error) {
console.log('error');
callback(error, null);
} else {
var total_count = results[0].cntStatus;
if(total_count > 0){
type = results[0].type;
}
var total_count = results[0].cntStatus;
var result = {
"id" : postid,
"title" : title,
"content" : content,
"type" : type,
"likes" : total_count
};
console.log('query okay');
callback(null, result);
}
});
}
arrayOfFuncs.push(func_2);
async.waterfall(arrayOfFuncs, function(errString, finalResult) {
if(errString) {
return res.send(errString);
} else {
return res.send(finalResult);
}
});
}

Node MySQL Populate Children as an array

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