Mysql node.js async, wait for query to complete - javascript

Been trying everything to get this to work specifically the async each method. for (const element of resultsHistory) didn't work either.
I'm trying to modify the result array from a previous query by running a foreach over it and doing a mysql query.
However this needs to wait for the query to complete.
Is there a way to access these results without the setTimeout(function() { } I put manually in order to wait for the query to finish?
function getUserLikes(params, callback) {
var usersArrayCat = [];
console.log(`length of array ${params.length}`)
// 1 here means 1 request at a time
async.eachLimit(params, 1, function (element, cb) {
element.liked = 0;
var queryLiked = `SELECT * from users_likes WHERE user_id = \"${req.body.userid}\" AND product_id = \"${element.product_id}\"`;
connectionPromise.query(queryLiked, function (err, result) {
if (!result) {
} else if (result.length == 0) {
} else {
element.liked = result[0].userlike;
usersArrayCat.push(element);
// console.log(usersArrayCat);
cb();
}
})
}, function (err) {
if (err) return callback(err);
callback(null, usersArrayCat)
});
};
getUserLikes(resultsHistory, function (e) {
console.log(e);
});
if(!res.headersSent) {
setTimeout(function() {
res.send(JSON.stringify({"status": 200 ,"error": null, "top3":resultsHistory}));
}, 150);
}

Managed to get it working like this, but still not sure if this is the right way.
async function getUserLikes(resultsHistory) {
for (const element of resultsHistory) {
element.liked = 0;
let queryLiked = `SELECT * from users_likes WHERE user_id = \"${req.body.userid}\" AND product_id = \"${element.product_id}\"`;
let liked = await conn2.query(queryLiked);
if (liked[0]) {
element.liked = liked[0].userlike;
}
}
if(!res.headersSent) {
conn2.release();
let newres = resultsHistory.sort(
firstBy(function (v1, v2) { return v2.rating - v1.rating; })
.thenBy(function (v1, v2) { return v2.dranktimes - v1.dranktimes; })
).filter( function(history) {
return history.event === eventName;
}).slice(0, 3);
res.send({"status": 200 ,"error": null, "top3":newres});
}
}
getUserLikes(resultsHistory);

See my answer:
function getUserLikes(params, callback) {
var usersArrayCat = [];
console.log(`length of array ${params.length}`)
// 1 here means 1 request at a time
async.eachLimit(params, 1, function (element, cb) {
element.liked = 0;
var queryLiked = `SELECT * from users_likes WHERE user_id = \"${req.body.userid}\" AND product_id = \"${element.product_id}\"`;
connectionPromise.query(queryLiked, function (err, result) {
if (!result) {
} else if (result.length == 0) {
} else {
element.liked = result[0].userlike;
usersArrayCat.push(element);
// console.log(usersArrayCat);
}
cb();
})
}, function (err) {
if (err) return callback(err);
callback(null, usersArrayCat)
});
};
getUserLikes(resultsHistory, function (e, usersArrayCat) {
if (e) {
console.log(e);
return res.send({status: 400, error: e}); // your error response
}
console.log(usersArrayCat); // your usersArrayCat with liked property
res.send(JSON.stringify({ // why your need return a string intead of json object ???
"status": 200,
"error": null,
"top3": usersArrayCat // I think return `usersArrayCat` is a right way
}));
});

Related

Loop Childens Custom Mongodb

User.find({ refUser: req.params.userName }).then(function (users) {
var network_users = [];
network_users.push(users);
users.forEach(function (u) {
network_users.push(User.find({ refUser: u.toObject().userName }));
})
return Promise.all(network_users);
I have 4 users, I expected receive a json with all of childrens but I only received the first and the children of this first.
Someone can help me with this loop? Please! Thanks so much!!!!
function asyncLoop(iterations, func, callback, foo) {
var done = false;
var loop = {
next: function () {
if (done) {
return;
}
if (iterations) {
func(loop);
} else {
done = true;
if (callback) callback(foo);
}
},
isEnd: function () {
return done;
},
refresh: function (it) {
iterations = it;
},
break: function () {
done = true;
callback();
}
};
loop.next();
return loop;
}
function bfs(userName, callback) {
userName = String(userName);
var q = [], res = [];
User.findOne({ "refUser" : userName }).lean().exec(function (err, root) {
root.depth = 0;
q.push(root);
asyncLoop(q.length, function (loop) {
res.push(q[0]);
User.find({ "refUser" : q[0].userName }).lean().exec(function (err, new_nodes) {
if (err) console.log(err);
else {
var d = q[0].depth;
q.shift();
loop.refresh(new_nodes.length + q.length);
if (new_nodes.length > 0) {
new_nodes.forEach(function (new_node) {
new_node.depth = d + 1;
q.push(new_node);
});
}
loop.next();
}
});
}, function () { callback(res) });
});
}
Finishing:
bfs(req.params.userName,function(callback){
res.send(callback)
})

how to handle expressJs callback and how to update object's property inside a function?

I have two js files. i am able to get data from mongodb by calliing bookDao.getActiveBookByCategoryId().
My Problem
In categoryDao.js file i am trying to update resultJson.book_countinside BookDao.getActiveBookByCategoryId() method. but it is not updating. So may i know how to fix this.
here book_count property in resultJson is still 0.
categoryDao.js
module.exports.getAllActiveCategory = (callback) => {
Category.find({
is_delete : false
}, (error, result) => {
if(error) {
console.log(error);
callback(commonUtil.ERROR);
}
if(result) {
var categoryArray = [];
for(var i=0; i<result.length; i++) {
var categorySingle = result[i];
var resultJson = {
_id : categorySingle._id,
category_name : categorySingle.category_name,
created_on : categorySingle.created_on,
book_count : 0
}
BookDao.getActiveBookByCategoryId(categorySingle._id, (bookResult) => {
if(bookResult) {
if(bookResult.length > 0) {
resultJson.book_count = bookResult.length;
}
}
});
categoryArray.push(resultJson);
}
callback(categoryArray);
}
});
}
bookDao.js
module.exports.getActiveBookByCategoryId = (categoryId, callback) => {
Book.find({
is_delete : false,
category : categoryId
}, (error, result) => {
if(error) {
console.log(error);
callback(commonUtil.ERROR);
}
if(result) {
callback(result);
}
});
}
Try this, In your code categoryArray.push(resultJson); will not wait for BookDao.getActiveBookByCategoryId to finish because of async behavior.
module.exports.getActiveBookByCategoryId = (categoryId) => {
return Book.count({
is_delete: false,
category: categoryId
});
}
module.exports.getAllActiveCategory = async () => {
try {
// Find all category
const result = await Category.find({
is_delete: false
});
// Create array of promise
const promises = result.map(categorySingle => BookDao.getActiveBookByCategoryId(categorySingle._id));
// Get array of Category count
const data = await Promise.all(promises);
// update count in result
return result.map((categorySingle, i) => {
categorySingle.book_count = data[i];
return categorySingle;
});
} catch (error) {
console.log(error);
}
}

Run loop on the server with node.js

Let's say i need to constantly collecting some data from a lot of clients and in parallel running some complex loop that solving some stuff with this data. How can i do it? Should i just write this in my piece of code:
app.get('/', function(req, res) {
res.sendFile(__dirname + '/public/views/index0.html');
});
io.sockets.on('connection', function(socket) {
// SOME STUFF WITH THE SOCKET
socket.on('disconnect', function(data) {
//SOME OTHER STUFF
});
});
while(...) {
//THE LOOP STUFF
}
Or i need to use the setTimeout() and setInterval() functions? How can i do the loop on the server that runs in parallel with the callbacks' stuff?
Don’t use while for make it, this block a thread. setTimeout() will run only once. You need to use setInterval() function.
You can use the async module to handle the async operation with the callback or use promise to avoid callback.
Here how I handle a complex async for each operation, that might helpfull to you get idea handeling ayncs forach
var cond = { _schedule: schedule_id }; // find curse by schedule id
Course.find(cond, function (err, courses) {
if (err) {
callback({ "success": false, "message": "Not able to update" });
} else {
async.forEachLimit(courses, 1, function (course, coursesCallback) {
async.waterfall([
function (callback) {
var schedule_date = moment(change_data.date).format('YYYY-MM-DD') + "T00:00:00.000Z"
var Assignmentcond = {
assignment_schedule_order: {
$gte: schedule_date
},
_course: course._id,
_schedule: schedule_id,
_user: userid
};
Assignment.find(Assignmentcond)
.populate({
path: '_course',
})
.lean()
.sort({ assignment_schedule_order: 1 })
.exec(function (err, AssignmentList) {
if (err) {
callback(null, '');
} else {
//console.log("------------------AssignmentList---------------------------");
//console.log(AssignmentList);
async.forEachLimit(AssignmentList, 1, function (ThisAssignmentCell, ThisAssignmentCellCallback) {
async.waterfall([
function (callback) {
var SearchObj = items;
var lebelObject = {};
for (var i = 0, flag = 0, insert = 0; i < SearchObj.length; i++) {
if (SearchObj[i].date == ThisAssignmentCell.assignment_schedule_date) {
flag = 1;
}
if (flag == 1 && SearchObj[i].label != "") {
if (ThisAssignmentCell.day == SearchObj[i].day_index) {
insert = 1;
var lebelObject = SearchObj[i];
break;
}
}
}
callback(null, ThisAssignmentCell, lebelObject, insert);
},
function (ThisAssignmentCell, SearchObj, insert, callback) {
console.log('----------------------');
console.log('ThisAssignmentCell', ThisAssignmentCell);
console.log('SearchObj', SearchObj);
console.log('----------------------');
if (insert > 0) {
var query = { _id: ThisAssignmentCell._id },
fields = {
assignment_date: moment(SearchObj.date).format('MM/DD/YYYY'),
assignment_schedule_date: moment(SearchObj.date).format('YYYY-MM-DD'),
assignment_schedule_order: new Date(SearchObj.date),
day: SearchObj.day_index,
dayNum: SearchObj.weekday_num
},
options = { upsert: false };
Assignment.update(query, fields, options, function (err, affected) {
callback(null, '');
});
} else {
// var cond = { _id: ThisAssignmentCell._id};
// Assignment.remove(cond)
// .exec(function (err, cnt) {
// callback(null, '');
// });
}
}
], function (err, result) {
// result now equals 'done'
console.log('done')
ThisAssignmentCellCallback();
});
}, function (err) {
console.log("Assignment For Loop Completed");
callback(null, AssignmentList);
});
}
});
}
], function (err, result) {
// result now equals 'done'
console.log('done')
coursesCallback();
});
}, function (err) {
console.log("courses For Loop Completed");
});
}
});

Push data to out side async function

I still do not handle well the asynchronous functions, I have an array of items, and I'm trying to for each item calculate some values and push to another array outside of the async function. Then I want to make some statistics calculation and send to front end. It's server side, nodejs handler, my code:
exports.register = function (plugin, options, next) {
function isInArray(value, array) {
return array.indexOf(value) > -1;
}
function statistics(values) {
var sum = math.sum(values);
var max = math.max(values);
var min = math.min(values);
var stddev = math.std(values);
var mean = math.mean(values);
var count = values.length;
}
plugin.route({
method: 'GET',
path: '/statistics/{orgId}/layout/{layoutId}',
config: {
pre: [
authorize(hasRole(['OPERATIONAL', 'STRATEGIC', 'LOP', 'TACTICAL']))
],
handler: function (request, reply) {
Category.find()
.where('organization')
.equals(request.params.orgId)
.exec(function (err, categories) {
var weight = [];
var price = [];
var volume = [];
var thisAR = [];
if (err || categories === null) {
return reply(Boom.badRequest('Categoria inexistente'));
} else {
Location.findById(request.params.layoutId)
.exec(function (err, layout) {
if(err) {
console.log(err);
}
var searchItems = function searchItems(category, next) {
Item.find()
.where('category')
.equals(category._id)
.exec(function (err, items) {
if (err) {
console.log(err);
} else {
var valuesToCalculate = [];
var itemsFiltered = [];
_.forEach(items, function(item) {
if(item.location && item.location !== null) {
if(isInArray(item.location.toString(), layout.contents)) {
itemsFiltered.push(item);
}
}
});
valuesToCalculate.push(itemsFiltered.length * category.data.weight);
valuesToCalculate.push(itemsFiltered.length * category.data.price);
valuesToCalculate.push(itemsFiltered.length * category.data.volume);
next(valuesToCalculate);
}
});
}
var onFinish = function onFinish(value, err) {
if(err) {
console.log(err);
}
console.log(value);
thisAR.push.apply(value);
}
async.each(categories, searchItems, onFinish);
console.log(thisAR);
//var arrays = [statistics(weight), statistics(price), statistics(volume)];
//return arrays;
});
}
});
}
}
});
next();
};
A few things stand out to me about this. First, you only call reply if there is an err or categories is null. Also, you are attempting to pass a non null value to an async.each callback. According to this: https://github.com/caolan/async#eacharr-iterator-callback, "if no error has occurred, the callback should be run without arguments or with an explicit null argument". I think you may misunderstand how the onFinish callback works with async.each.. it is not called for each item, it is called when all of the iterator functions have completed. So, rather than pushing items onto thisAR in onFinish, you should do so inside searchItems. I think this should work:
exports.register = function (plugin, options, next) {
function isInArray(value, array) {
return array.indexOf(value) > -1;
}
function statistics(values) {
var sum = math.sum(values);
var max = math.max(values);
var min = math.min(values);
var stddev = math.std(values);
var mean = math.mean(values);
var count = values.length;
}
plugin.route({
method: 'GET',
path: '/statistics/{orgId}/layout/{layoutId}',
config: {
pre: [
authorize(hasRole(['OPERATIONAL', 'STRATEGIC', 'LOP', 'TACTICAL']))
],
handler: function (request, reply) {
Category.find()
.where('organization')
.equals(request.params.orgId)
.exec(function (err, categories) {
var weight = [];
var price = [];
var volume = [];
var thisAR = [];
if (err || categories === null) {
return reply(Boom.badRequest('Categoria inexistente'));
} else {
Location.findById(request.params.layoutId)
.exec(function (err, layout) {
if(err) {
console.log(err);
}
var searchItems = function searchItems(category, next) {
Item.find()
.where('category')
.equals(category._id)
.exec(function (err, items) {
if (err) {
console.log(err);
} else {
var valuesToCalculate = [];
var itemsFiltered = [];
_.forEach(items, function(item) {
if(item.location && item.location !== null) {
if(isInArray(item.location.toString(), layout.contents)) {
itemsFiltered.push(item);
}
}
});
valuesToCalculate.push(itemsFiltered.length * category.data.weight);
valuesToCalculate.push(itemsFiltered.length * category.data.price);
valuesToCalculate.push(itemsFiltered.length * category.data.volume);
thisAR.push.apply(valuesToCalculate);
}
next(err);
});
}
var onFinish = function onFinish(err) {
if(err) {
console.log(err);
}
console.log(thisAR);
// call reply here
}
async.each(categories, searchItems, onFinish);
console.log(thisAR);
//var arrays = [statistics(weight), statistics(price), statistics(volume)];
//return arrays;
});
}
});
}
}
});
next();
};
I can't comment since I'm new but it seems that just removing the .apply in thisAR.push.apply(valuesToCalculate); will return a full array to your console.log.

How promise works with nested function calls

I have a piece of code which deals with user's data. There are bunch of nested function calls :
f1(){
f2(){
....
fn{
///
}
}
}
fn accesses a database which means it's asynchronous, so I wrote it somehow that it returns a promise and in fn-1 (the function which calls fn) , we use .then() to wait for this promise. But it looks like now I have to return a promise in fn-1 and so on. Is that true ?
var keyValueExists = function(key, value) {
var query = {};
query[key] = value;
return new Promise(function(resolve, reject) {
User.count(query, function(err, count) {
if (err) {
console.log(err);
console.log('Problem with `.find` function');
reject('Problem with `.find` function');
} else {
resolve(count !== 0);
}
});
});
};
var addUser = function(newUserInfo) {
var validationResult = Common._validateUserInfo(newUserInfo);
if (validationResult.isOK) {
keyValueExists('userName', newUserInfo.userName).then(function(userNameAlreadyExists) {
if (userNameAlreadyExists) {
validationResult = {
isOK: false,
reason: 'Username already exists',
infoWithBadInput: 'userName'
}
} else {
var newUserId = generateUserId();
//TODO: change it somehting more flexible. e.g. a predefined list of attributes to iterate over
var newUser = {
'userName': newUserInfo.userName,
'password': newUserInfo.password,
'userId': newUserId,
'lastModificationTime': Common.getCurrentFormanttedTime(),
'createdTime': Common.getCurrentFormanttedTime()
};
var user = new User(newUser);
user.save(function(err) {
if (err) {
console.log(err);
console.log('There is a problem saving the user info');
} else {
console.log('A new user added: ');
console.log(newUser);
}
});
}
return validationResult;
});
} else {
return validationResult;
}
};
addUser returns undefined ! It looks like that the caller of addUser doesn't wait for it !
This is what you are effectively doing in your addUser function
var addUser = function(newUserInfo) {
var validationResult = Common._validateUserInfo(newUserInfo);
if (validationResult.isOK) {
// ... do something asynchronously without returning anything
} else {
return validationResult;
}
}
So, yeah, if validationResult.isOK, adduser WILL return undefined
Here's some code loosely based on your code, but it runs standalone to demonstrate how you possibly should be doing things
var keyValueExists = function(key, value) {
// pseudo junk, this simulates any username starting with b as existing
return new Promise(function(resolve, reject) {
resolve(value.substr(0,1) == 'b'); // barny and betty are dupes, fred and wilma are not
});
}
var addUser = function (newUserInfo) {
// var validationResult = Common._validateUserInfo(newUserInfo);
var validationResult = {isOK: !!~(['fred', 'barny'].indexOf(newUserInfo.userName)), username: newUserInfo.userName}; // dummy code
if (validationResult.isOK) {
return keyValueExists('userName', newUserInfo.userName).then(function (userNameAlreadyExists) {
if (userNameAlreadyExists) {
validationResult = {
isOK: false,
reason: 'Username already exists',
infoWithBadInput: 'userName',
username: newUserInfo.userName
}
} else {
// create new user here
validationResult.userNumber = (Math.random() * 100000000) | 0;
}
return validationResult;
});
}
else {
// this function always needs to return a promise, even if it is resolved/rejected immediately
return Promise.reject(validationResult);
}
}
addUser({userName: 'fred'}).then(function (result) {
console.log(result);
}).catch(function(err) {
console.error(err);
});
addUser({userName: 'wilma'}).then(function (result) {
console.log(result);
}).catch(function(err) {
console.error(err);
});
addUser({userName: 'barny'}).then(function (result) {
console.log(result);
}).catch(function(err) {
console.error(err);
});
addUser({userName: 'betty'}).then(function (result) {
console.log(result);
}).catch(function(err) {
console.error(err);
});

Categories