forEach Loop not returning Array - mysql

So, i seem to be having a problem with returning a value from my forEach MYSQL query loop in Node JS and then inserting this into an ejs template. Currently, these are my pieces of code:
app.js GET Request
app.get("/admin/stock", function(req, res) {
db.query("SELECT * FROM stock", function (err, stock) {
if (err) {
console.log("Error with showing SQL")
} else {
stock.forEach(function (stock) {
db.query("SELECT * FROM product WHERE productID = " +
stock.stock_productID, function (err, product) {
if (err) {
console.log("Error");
}
else {
stock.stock_productID = product[0].productName
}
});
db.query("SELECT * FROM currency WHERE currencyID = " +
stock.stock_currencyID, function (err, currency) {
if (err) {
console.log("Error");
}
else {
stock.stock_currencyID = currency[0].currencyName
}
});
db.query("SELECT * FROM customer WHERE customerID = " +
stock.stock_customerID, function (err, customer) {
if (err) {
console.log("Error");
}
else {
stock.stock_customerID =
customer[0].customerBusiness;
console.log(stock);
}
})
});
}
console.log(stock);
res.render("admin/stock", {stock:stock, userFName: req.user[1]})
})
});
Stock Page EJS
<table class="generalTable">
<tr>
<th>Product Name</th>
<th>Quantity</th>
<th>Currency</th>
<th>Cost</th>
<th>Date Bought</th>
<th>Bought From</th>
</tr>
<% stock.forEach(function(stock){ %>
<tr>
<td><%=stock.stock_productID%></td>
<td><%=stock.quantityStockCurrent%></td>
<td><%=stock.stock_currencyID%></td>
<td><%=stock.priceBought%></td>
<td><%=stock.boughtDate%></td>
<td><%=stock.stock_customerID%></td>
</tr>
<% }) %>
</table>
The First console.log of stock shows the replaced values, the second however, shows the original array values, and therefore the page is loaded in with the original values (IDs etc, instead of names).
How do I send the inner/edited array to the res.render instead of the original array?
Many Thanks,
Brad
EDIT//////
By using JOINs, the output was correct

I guess the database queries have not yet returned when you already render the stock object.
Remember, nodejs is asynchronous. You have to wait for all DB queries to come back before rendering the result.
I did have a similar problem when I first learned about nodejs: JavaScript nodejs mysql with queries in a loop

The problem is that javascript is asynchronous and that Javascript doesn't have a block scope, which means that you render file before the loop complete so you should use simple for loop for this.
you should replace your code with this :-
app.get("/admin/stock", function(req, res) {
db.query("SELECT * FROM stock", function (err, stocks) {
var stock = JSON.parse(JSON.stringify(stocks));
if (err) {
console.log("Error with showing SQL")
} else {
for(var i = 0 ; i< stock.length; i++) {
db.query("SELECT * FROM product WHERE productID = " +
stock[i].stock_productID, function (err, product) {
if (err) {
console.log("Error");
}
else {
stock[i].stock_productID = product[0].productName
}
});
db.query("SELECT * FROM currency WHERE currencyID = " +
stock[i].stock_currencyID, function (err, currency) {
if (err) {
console.log("Error");
}
else {
stock[i].stock_currencyID = currency[0].currencyName
}
});
db.query("SELECT * FROM customer WHERE customerID = " +
stock[i].stock_customerID, function (err, customer) {
if (err) {
console.log("Error");
}
else {
stock[i].stock_customerID =
customer[0].customerBusiness;
console.log(stock);
}
});
if(i === stock.length-1){
console.log(stock);
res.render("admin/stock", {stock:stock, userFName: req.user[1]})
}
};
}
})
});

You can use async.map for executing query in loop.
function getResults(id,cb){
db.query("SELECT * FROM product WHERE productID = " +
id.stock_productID, function (err, product) {
if (err) {
console.log("Error");
}
else {
id.stock_productID = product[0].productName
}
});
}
async.mapLimit(stock, 5, function(id, callback) {
getResults(id, function (err, res) {
if (err) return callback(err);
callback(null, res);
})
}, function(err, results) {
// results is an array of names
});

Related

NodeJs Mysql returns empty result

I have api's that queries the data and returns the results in json format. if i call the query inside api.get and set the res.send(rows) it is working fine but i need to call that same method in different methods so I thought I could write it outside and call that method whenever it is needed. but the result returns empty when it is outside.
var customerRows[]
app.get('/customers', function(req, res) {
getCustomers();
res.json({
customers : customerRows
});
});
function getCustomersQuery(callback) {
var customersDataQuery = mysqlConnection.query('SELECT * from customer_info', function(err, rows, fields) {
if (!err) {
if (rows) {
callback(null, rows);
}
} else {
callback(err, null);
console.log('Error while performing Query.');
}
});
}
function getCustomers() {
getCustomersQuery(function(err, result) {
if (err) {
console.log(err);
} else {
customerRows.push(result);
console.log(customerRows)//prints values
}
});
console.log("Result : "+customerRows);//prints empty
}
I'm trying to set the result to my global variable customerRows but it returns empty.
use this code
app.get('/customers', function(req, res) {
getCustomersQuery(function(err, result) {
if (err) {
console.log(err);
}
res.json({
customers : result
});
});
});
function getCustomersQuery(callback) {
var customersDataQuery = mysqlConnection.query('SELECT * from customer_info', function(err, rows, fields) {
if (!err) {
if (rows) {
callback(null, rows);
}
} else {
callback(err, null);
console.log('Error while performing Query.');
}
});
}

Node JS Inserting array of objects to mysql database when using transactions

Am using node-mysql to add records to a database but am facing a challenge when the records to be inserted are an array of objects and I need the operation to be a transaction. I have simplified my problem by creating a test project to better explain my problem.
Lets say I have to tables users and orders and the data to be inserted looks like this
var user = {
name: "Dennis Wanyonyi",
email: "example#email.com"
};
var orders = [{
order_date: new Date(),
price: 14.99
}, {
order_date: new Date(),
price: 39.99
}];
I want to first insert a user to the database and use the insertId to add the each of the orders for that user. Am using a transaction since in case of an error, I want to rollback the whole process. Here is how I try to insert all the records using node-mysql transactions.
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO users SET ?', user, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
for (var i = 0; i < orders.length; i++) {
orders[i].user_id = result.insertId;
connection.query('INSERT INTO orders SET ?', orders[i], function(err, result2) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
}
});
});
However I have a problem iterating over the array of orders without having to call connection.commit multiple times within the for loop
I would suggest to construct a simple string for multiple row insert query for orders table in the for loop first and then execute it outside the for loop. Use the for loop to only construct the string. So you can rollback the query whenever you want or on error. By multiple insert query string i mean as follows:
INSERT INTO your_table_name
(column1,column2,column3)
VALUES
(1,2,3),
(4,5,6),
(7,8,9);
You can use Promise.all functionality of Bluebird for this.
var promiseArray = dataArray.map(function(data){
return new BluebirdPromise(function(resolve, reject){
connection.insertData(function(error, response){
if(error) reject(error);
else resolve(response);
}); //This is obviously a mock
});
});
And after this:
BluebirdPromise.all(promiseArray).then(function(result){
//result will be the array of "response"s from resolve(response);
database.commit();
});
This way, you can work all the inserts asyncronously and then use database.commit() only once.
Some kind of task in Node.js are Asynchronous( like I/O , DB and etc..), and there is a lots of LIBS that help to handle it.
but if you want don't use any lib,for iterating an array in JS and use it in an asynchronous functionality its better to implement it as recursive function.
connection.beginTransaction(function(err) {
if (err) {
throw err;
}
connection.query('INSERT INTO users SET ?', user, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
// console.log(result.insertId) --> do any thing if need with inserted ID
var insertOrder = function(nextId) {
console.log(nextId);
if ((orders.length - 1) < nextId) {
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
})
}
console.log(" ok");
});
} else {
console.log(orders[nextId]);
connection.query('INSERT INTO orders SET ?', orders[nextId], function(err, result2) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
insertOrder(nextId + 1);
});
}
}
insertOrder(0);
});
});
as you can see I rewrite your for loop as a recursive function inside.
I would use the async.each to do the iteration and to fire all the queries in parallel. If some of the queries will fail, the asyncCallback will be called with an error and the program will stop processing the queries. This will indicate that we should stop executing queries and rollback. If there is no error we can call the commit.
I' ve decoupled the code a bit more and split it into functions:
function rollback(connection, err) {
connection.rollback(function () {
throw err;
});
}
function commit(connection) {
connection.commit(function (err) {
if (err) {
rollback(connection, err);
}
console.log('success!');
});
}
function insertUser(user, callback) {
connection.query('INSERT INTO users SET ?', user, function (err, result) {
return callback(err, result);
});
}
function insertOrders(orders, userId, callback) {
async.each(orders, function (order, asyncCallback) {
order.user_id = userId;
connection.query('INSERT INTO orders SET ?', order, function (err, data) {
return asyncCallback(err, data);
});
}, function (err) {
if (err) {
// One of the iterations above produced an error.
// All processing will stop and we have to rollback.
return callback(err);
}
// Return without errors
return callback();
});
}
connection.beginTransaction(function (err) {
if (err) {
throw err;
}
insertUser(user, function (err, result) {
if (err) {
rollback(connection, err);
}
insertOrders(orders, result.insertId, function (err, data) {
if (err) {
rollback(connection, err);
} else {
commit(connection);
}
});
});
});
you need to use async library for these kind of operation.
connection.beginTransaction(function(err) {
if (err) { throw err; }
async.waterfall([
function(cb){
createUser(userDetail, function(err, data){
if(err) return cb(err);
cb(null, data.userId);
});
},
function(userid,cb){
createOrderForUser(userid,orders, function() {
if(err) return cb(err);
cb(null);
});
}
], function(err){
if (err)
retrun connection.rollback(function() {
throw err;
});
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
var createUser = function(userdetail, cb){
//-- Creation of Orders
};
var createOrderForUser = function (userId, orders, cb) {
async.each(orders, function(order, callback){
//-- create orders for users
},function(err){
// doing err checking.
cb();
});
};
See if you can write a Stored Procedure to encapsulate the queries, and have START TRANSACTION ... COMMIT in the SP.
The tricky part comes with needing to pass a list of things into the SP, since there is no "array" mechanism. One way to achieve this is to have a commalist (or use some other delimiter), then use a loop to pick apart the list.
currentLogs = [
{ socket_id: 'Server', message: 'Socketio online', data: 'Port 3333', logged: '2014-05-14 14:41:11' },
{ socket_id: 'Server', message: 'Waiting for Pi to connect...', data: 'Port: 8082', logged: '2014-05-14 14:41:11' }
];
console.warn(currentLogs.map(logs=>[ logs.socket_id , logs.message , logs.data , logs.logged ]));

better way of selecting 1 to many?

I have an express.js based rest application. Please have a look on following code and suggest me what would be better way.
I want to select user and its associated images (1 user has many images).
function getUser (connection, req, res) {
var userId = req.params.id;
connection.query('SELECT * FROM user p'
+ ' WHERE p.id = ' + connection.escape(userId), function handleSql(err, rows) {
if (err){ logAndRespond(err,res); return; }
if (rows.length === 0){ res.send(204); return; }
var adId = rows[0].adId;
// load images
connection.query('SELECT id, url FROM image WHERE ad_id = ' + connection.escape(adId), function (err, imgRows) {
if (err){ logAndRespond(err,res); return; }
if (rows.length != 0){
rows[0].images = imgRows;
}
res.json({'user': rows});
connection.release();
});
});
}
You don't have to escape parameters by yourself
You don't release the connection if an error occurred
The problem now is I don't know what you want to do with selected rows. You are also checking the rows.length twice but if there weren't any records in the first query then the second one will not be executed.
function getUser(conn, req, res) {
conn.query("SELECT * FROM user p WHERE p.id = ?;", [req.params.id], function(err, rows) {
if (err) {
return logAndRespond(err, res);
}
if (!rows.length) {
return res.send(204);
}
conn.query("SELECT id, url FROM image WHERE ad_id = ?;", [rows[0].adId], function(err, imgRows) {
if (err) {
return logAndRespond(err, res);
}
if (rows.length) { // ???
rows[0].images = imgRows;
}
res.json({"user": rows});
conn.release();
});
});
}

Node.js mysql transaction

Can anyone provide an example of how I could achieve MySQL transactions in Node.js. I am trying to get my head around using the node-mysql driver and node-mysql-queue.
As far are I can tell, using node-mysql-queue greatly reduces the asynchronous nature of Node.js as new queries have to wait until existing ones have completed. To get around this, has anyone attempted to combine node-mysql-queue with node-mysql's connection-pooling capabilities. i.e starting a new mysql connection for each new http request, and starting transaction queues on individual connections?
Update
See the edit below for async/await syntax
I spent some time writing a generalized version of the transaction example given by node mysql, so I thought I would share it here. I am using Bluebird as my promise library, and used it to 'promisify' the connection object which simplified the asynchronous logic a lot.
const Promise = ('bluebird');
const mysql = ('mysql');
/**
* Run multiple queries on the database using a transaction. A list of SQL queries
* should be provided, along with a list of values to inject into the queries.
* #param {array} queries An array of mysql queries. These can contain `?`s
* which will be replaced with values in `queryValues`.
* #param {array} queryValues An array of arrays that is the same length as `queries`.
* Each array in `queryValues` should contain values to
* replace the `?`s in the corresponding query in `queries`.
* If a query has no `?`s, an empty array should be provided.
* #return {Promise} A Promise that is fulfilled with an array of the
* results of the passed in queries. The results in the
* returned array are at respective positions to the
* provided queries.
*/
function transaction(queries, queryValues) {
if (queries.length !== queryValues.length) {
return Promise.reject(
'Number of provided queries did not match the number of provided query values arrays'
)
}
const connection = mysql.createConnection(databaseConfigs);
Promise.promisifyAll(connection);
return connection.connectAsync()
.then(connection.beginTransactionAsync())
.then(() => {
const queryPromises = [];
queries.forEach((query, index) => {
queryPromises.push(connection.queryAsync(query, queryValues[index]));
});
return Promise.all(queryPromises);
})
.then(results => {
return connection.commitAsync()
.then(connection.endAsync())
.then(() => {
return results;
});
})
.catch(err => {
return connection.rollbackAsync()
.then(connection.endAsync())
.then(() => {
return Promise.reject(err);
});
});
}
If you wanted to use pooling as you suggested in the question, you could easily switch the createConnection line with myPool.getConnection(...), and switch the connection.end lines with connection.release().
Edit
I made another iteration of the code using the mysql2 library (same api as mysql but with promise support) and the new async/await operators. Here is that
const mysql = require('mysql2/promise')
/** See documentation from original answer */
async function transaction(queries, queryValues) {
if (queries.length !== queryValues.length) {
return Promise.reject(
'Number of provided queries did not match the number of provided query values arrays'
)
}
const connection = await mysql.createConnection(databaseConfigs)
try {
await connection.beginTransaction()
const queryPromises = []
queries.forEach((query, index) => {
queryPromises.push(connection.query(query, queryValues[index]))
})
const results = await Promise.all(queryPromises)
await connection.commit()
await connection.end()
return results
} catch (err) {
await connection.rollback()
await connection.end()
return Promise.reject(err)
}
}
The following transaction example was added to the documentation a month ago:
https://github.com/felixge/node-mysql#transactions
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
var log = 'Post ' + result.insertId + ' added';
connection.query('INSERT INTO log SET data=?', log, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});
I am using the following approach. There is an add function in my Model where I am performing database operations.
add : function (data, callback) {
//Begin transaction
connection.beginTransaction(function(err) {
if (err) {
throw err;
}
var user_query = "INSERT INTO `calldata`.`users` (`username`, `password`, `enabled`, `accountNonExpired`, `accountNonLocked`, `credentialsNonExpired`) VALUES ('" + data.mobile + "', '" + sha1(data.password) + "', '1', '1', '1', '1')";
connection.query(user_query, function(err, results) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
var accnt_dtls_query = "INSERT INTO `calldata`.`accnt_dtls` (`req_mob_nmbr`, `usr_nme`, `dvce_id`, `mngr_id`, `cmpny_id`, `actve_flg`, `crtd_on`, `usr`) VALUES (" + data.mobile + ", '" + data.name + "', '', " + data.managerId + ", " + data.companyId + ", 1, now(), '" + data.mobile+ "')";
connection.query(accnt_dtls_query, function(err, results) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
var user_role_query = "INSERT INTO `calldata`.`user_roles` (`username`, `ROLE`) VALUES ('" + data.mobile + "', '" + data.role + "')";
connection.query(user_role_query, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
//add an entry to manager table
var mngr_dtls_query = "INSERT INTO `calldata`.`mngr_dtls` (`mngr_nm`, `cmpny_id`, `crtd_on`, `usr_nm`, `eml_id`) VALUES ('" + data.name + "'," + data.companyId + " , now(), '" + data.mobile + "', '" + data.mobile + "')";
connection.query(mngr_dtls_query, function(err, result) {
if (err) {
return connection.rollback(function () {
throw err;
});
}
console.log('Changed ' + result.changedRows + ' results');
connection.commit(function (err) {
console.log('Commiting transaction.....');
if (err) {
return connection.rollback(function () {
throw err;
});
}
console.log('Transaction Complete.');
connection.end();
callback(null, result);
});
});
});
});
});
});
//transaction ends here
}
and calling from controller:
agentAccountModel.add(data, function(err, results) {
if(err)
{
res.status(500);
res.json({
"status": 500,
"message": err
});
}
res.status(200);
res.json({
"status": 200,
"message": "Saved successfully"
});
});
I have come up with a solution using a recursive function.
var sql = 'INSERT INTO logs SET data = ?';
// array of rows to insert
var rows = [[/*first row*/], [/*additional row*/]];
connection.beginTransaction(function (err) {
if (err) {
throw err;
}
var insertEachRow = function () {
var row = rows.shift();
if (! row) {
// Done, now commit
return noMoreRows();
}
connection.query(sql, row, function (err, result) {
if (err) {
connection.rollback(function () {
throw err;
});
}
insertEachRow();
});
};
var noMoreRows = function () {
connection.commit(function (err) {
if (err) {
connection.rollback(function () {
throw err;
});
}
console.log('success!');
});
};
insertEachRow();
});
I found one useful links which uses node js mysql pooling with transaction. Database Connection pooling is always useful. One can check this link
https://github.com/mysqljs/mysql
I created a wrapper ORM type thing for this specific purpose, hope it helps SQl-connecton - pool ORM type helper methods
You can use it with the mysql also, just use this function without any parameters /call back .
Make sure you have async function and use these instead
await con.rollback();
await con.beginTransaction();
await con.commit();
Life set.....

How to get the results from nodejs using mysql package?

first, i connect the db and select DB:
var defaultOptions = {
user: "root",
pwd:'admin',
db:"britcham_dev_local",
server:"local", // Maybe we don't need this variable.
};
var client = new Client();
client.user = defaultOptions.user;
client.password = defaultOptions.pwd;
client.connect(function (error, results) {
//
});
client.query('USE ' + defaultOptions.db, function (error, results) {
//
});
Second, I query with client object:
var self = this;
var this.users;
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
}
});
console.log(this.users);
it's nothing output ??? Why ??
Since node.js is non-blocking and asynchronous, then in this code:
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
}
});
console.log(this.users);
data from DB are not probably loaded yet into users variable when you are trying to log it into console. You can check it out if you do your console.log operation within the query, for example:
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
console.log(results);
}
});
To pass the result into a variable when the operation is finished you can wrap your client DB call into a function with callback parameter and set your variable when the callback is invoked, for example:
function query(sql, callback) {
client.query(sql, function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
callback(results);
}
});
}
query("SELECT * FROM users", function(results) {
self.users = results;
console.log(self.users);
});
Above code is just a concept.
How is the suggested answer different from this?
var self = this;
var this.users;
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
console.log(this.users);
}
});
I might be wrong this is not different from the suggested answer in that it writes to console no sooner than when we have the data back from the DB.
The suggested answer seems only to add yet another function?