here is my router:
router.get('/questions/best', function(req,res,next){
Question.aggregate([
// Protect against missing fields with $ifNull
// Protect against division by zero dislikes using max[1,size]
{$project: {ratio: {$divide:[
{$arrayElemAt:[{$ifNull:["$likes",[0]]}, 0]},
{$max:[1, {$arrayElemAt:[{$ifNull:["$dislikes",[0]]}, 0]}]}
]}
}},
{$sort: {ratio:-1}},
{$limit: 15}
]).exec(function(err,results){
if(err) {return next(err)}
top15 = []
for(let i = 0; i < results.length; i++){
Question.findOne({_id: results[i]._id}).exec(function(err,result){
if(err){return next(err)}
top15.push(result);
})
}
res.render('content', { whichOne: 5, user: req.user, questions: top15 });
});
});
don't worry about the filtering stuff, it works good, the only problem is that the template at the end of the router get rendered automatically right after the queries start, not allowing them time to push the results into an array that gets rendered into the template. I tried using set Timeout function, but the processing time changes drastically and it is impossible to find an exact time. I think I need to use async functions? but promises do not work for some reason?
can anybody make sure the template get rendered only after all the queries return and put their values in the array? Thanks!
From reading mongoose documentation, using promises makes this trivial
using Promises
router.get('/questions/best', (req,res,next) => {
Question.aggregate([
// Protect against missing fields with $ifNull
// Protect against division by zero dislikes using max[1,size]
{$project: {ratio: {$divide:[
{$arrayElemAt:[{$ifNull:["$likes",[0]]}, 0]},
{$max:[1, {$arrayElemAt:[{$ifNull:["$dislikes",[0]]}, 0]}]}
]}
}},
{$sort: {ratio:-1}},
{$limit: 15}
]).exec()
.then(results => Promise.all(results.map(({_id}) => Question.findOne({_id}).exec())))
.then(questions => res.render('content', { whichOne: 5, user: req.user, questions}))
.catch(next);
});
or, using async/await
router.get('/questions/best', async (req,res,next) => {
try {
const results = await Question.aggregate([
// Protect against missing fields with $ifNull
// Protect against division by zero dislikes using max[1,size]
{$project: {ratio: {$divide:[
{$arrayElemAt:[{$ifNull:["$likes",[0]]}, 0]},
{$max:[1, {$arrayElemAt:[{$ifNull:["$dislikes",[0]]}, 0]}]}
]}
}},
{$sort: {ratio:-1}},
{$limit: 15}
]).exec();
const questions = await Promise.all(results.map(({_id}) => Question.findOne({_id}).exec()));
res.render('content', { whichOne: 5, user: req.user, questions});
} catch(err) {
next(err);
}
});