Eran Levi
Eran Levi

Reputation: 363

NodeJs: aSync forEach inside mongoose find

I guess it should be simple for those who familiar with Node.js and Mongoose. I'm trying to get the final result inside async forEach and mongoose find, I can see the results fine in the middle of the function, but when Im trying to get it after the second forEach, I cant.

I have tried to catch the last iteration and bring the results back, but because its async, the last iteration arrived before the find in the middle of the function.

Any suggestions? Here is what I have:

function addUsersInfoForEachVote(req, res, next){
    var itemsProcessed = 0;
    Questions.findOne({_id: req.body.question}, {answers: 1}).exec(function (err, question) {
        var finalVotes = [];
        if (err | !question) {
            return res.send({
                errors: err | "No Question Found"
            });
        } else {
            question.answers.forEach((answer, index, array) => {
                var votedUsers = [];
                votedUsers = votedUsers.concat(answer.get("votes"));
                answer._doc.votes = answer.get("votes").length;
                var ansId = answer.answerId;
                var aData = {answer: answer, users: {}};
                AnswersVote.find({'_id': {$in: getUniqueIds(votedUsers)}},{user: 1})
                           .populate('user', 'name gender')
                           .exec(function(err, votesUsers){
                                if (!err) {
                                    votesUsers.forEach(function(vote){
                                        var user = vote.get("user");
                                        if (user){
                                            aData.users[user.get("id")] = user;
                                        }
                                    });
                                    console.log("ADATA:"+JSON.stringify(aData)); //Here I can see it well! (the second time contain it all)
                                }
                            });
                //currAns = votedUsers;
                itemsProcessed++;
                if( itemsProcessed === array.length) {
                //HERE I NEED THE FINAL RESULTS:
                    console.log("ADATA after:"+JSON.stringify(aData));
                    next();
                }
            });    
        }

    });
}

Upvotes: 0

Views: 2144

Answers (3)

Eran Levi
Eran Levi

Reputation: 363

First, Thank you all for your help, I'm personally trying to avoid using external libraries if its unnecessary, and because I can check the amount of answers before, here is the right answer:

function addUsersInfoForEachVote(req, res, next){
      Questions.findOne({_id: req.body.question}, {answers: 1}).exec(function (err, question) {
        var finalVotes = [];
          if (err || !question) {
              return res.send({
                  errors: err || "No Question Found"
              });
          } else {
        var ansNum=0; //FOR MY ITERATIONS
        var fAns = {full : {}}; //FOR MY FINAL ANSWER

        //Here, I can assume we have only two answers so:
    question.answers.forEach(function (answer) {
      var votedUsers = [];
        votedUsers = votedUsers.concat(answer.get("votes"));
        answer._doc.votes = answer.get("votes").length;
        //answer._doc.answer = votedUsers;
      var ansId = answer.answerId;
      var aData = {answer: answer, users: {}};

  AnswersVote.find({'_id': {$in: getUniqueIds(votedUsers)}},{user: 1}).populate('user', 'name gender').exec(function(err, votesUsers){
            if (!err) {
                votesUsers.forEach(function(vote){
                    var user = vote.get("user");
                    if(user){
                    aData.users[user.get("id")] = user;
                  }
                });
            }
fAns.full[ansId] = aData;
if(ansNum > 0){ //The number of the answers here is "2", but this value can be changed
  console.log("FINAL:"+JSON.stringify(fAns)); //Here I have the full response now :)
  res.jsonp(fAns);
}
            ansNum++;
        });
    });

          }

        });

      }

Thanks again anyway.

Upvotes: 0

Abhilash Km
Abhilash Km

Reputation: 116

You can use async eachLimit or each here Read doc(http://caolan.github.io/async/docs.html#.eachLimit)

async.eachLimit((question.answers || []), 4, function(answer, callback) {
        var votedUsers = [];
        votedUsers = votedUsers.concat(answer.get("votes"));
        answer._doc.votes = answer.get("votes").length;
        var ansId = answer.answerId;
        var aData = {
            answer: answer,
            users: {}
        };
        AnswersVote.find({
            '_id': {
                $in: getUniqueIds(votedUsers)
            }
        }, {
            user: 1
        })
            .populate('user', 'name gender')
            .exec(function(err, votesUsers) {
                if (!err) {
                    votesUsers.forEach(function(vote) {
                        var user = vote.get("user");
                        if (user) {
                            aData.users[user.get("id")] = user;
                        }
                    });
                    console.log("ADATA:" + JSON.stringify(aData));
                }
                callback(err);
            });

    },
    function(error) {
        next();
    }
});

Upvotes: 1

Shrabanee
Shrabanee

Reputation: 2766

May be you can use async.waterfall to achieve the desired result.

Refer doc for more information.

Try something like below code:-

function addUsersInfoForEachVote(req, res, next){
var itemsProcessed = 0;
  Questions.findOne({_id: req.body.question}, {answers: 1}).exec(function (err, question) {
    var finalVotes = [];
      if (err | !question) {
          return res.send({
              errors: err | "No Question Found"
          });
     } else {
question.answers.forEach((answer, index, array) => {
  var votedUsers = [];
    votedUsers = votedUsers.concat(answer.get("votes"));
    answer._doc.votes = answer.get("votes").length;
var ansId = answer.answerId;
var aData = {answer: answer, users: {}};

async.waterfall([
     function(callback){
       AnswersVote.find({'_id': {$in: getUniqueIds(votedUsers)}},{user: 1}).populate('user', 'name gender').exec(function(err, votesUsers){
        if (!err) {
            votesUsers.forEach(function(vote){
                var user = vote.get("user");
                if(user){
                aData.users[user.get("id")] = user;
              }
            });
          console.log("ADATA:"+JSON.stringify(aData)); //Here I can see it well! (the second time contain it all)

          callback(null);
        }
      });
    },
    function(callback){
         //currAns = votedUsers;
    itemsProcessed++;
    if(itemsProcessed === array.length) {
       //HERE I NEED THE FINAL RESULTS:
         console.log("ADATA after:"+JSON.stringify(aData));
          callback(null);
        }
     else {callback(null);}
    }
], function(err){next();})
});

This may help you. Let me know if it works.

Upvotes: 0

Related Questions