0

I'm trying to fetch IDs from one call with mongoose. Afterwards, each of these IDs is used to make another call that returns multiple objects. I am trying to fetch all of these objects.

My current attempt looks something like this:

  var members;
  var memberTimes = [];

  // Use the Group model to find a specific group
  Group.find({
    members: {
      $elemMatch: {
        $eq: req.user._id
      }
    },
    _id: req.params.group_id
  }, function(err, group) {
    if (err) {
      res.send(err);
    } else if (!group) {
      //res.send(new Error("User not in group or it does not exist"));
    }

    members = group[0].members;

    for (var member of members) {
      // Use the Time model to find a specific time
      Time.find({
        user_id: member
      }, function(err, times) {
        if (err) {
          res.send(err);
        }

        for (var time of times) {
          memberTimes.push(time);
        }
      });
    }
    //on completion of all above code, execute res.json(memberTimes);
 });

This, however, does not work because I am not waiting for all the callbacks from the Time#find. I have look at using promises but I am unsure as to how exactly make it work.

Does anyone know how this could be made to work?

Thank you, Daniel

DVassilev
  • 242
  • 1
  • 5
  • 20

2 Answers2

1

You need to do asynchronous looping and wait for the final response. You can do something like

var members;

function getTimes(mTimes, times, i, done) {
    console.log("time " + i);
    if (i < times.length) {
      console.log(times[i]);
      mTimes.push(times[i]);
      getTimes(mTimes, times, i + 1, done);
    } else {
      done(mTimes);
    }
  }

  function asyncLoop(memberTimes, members, i, callback) {
    if (i < members.length) {
      Time.find({
        user_id: members[i]
      }, function(err, times) {
        if (err) {
          res.send(err);
        }
        console.log(times);
        getTimes(memberTimes, times, 0, function(result) {
          asyncLoop(memberTimes, members, i + 1, callback);
        });
      });
    } else {
      callback(memberTimes);
    }
  }

  // Use the Group model to find a specific group
  Group.find({
    members: {
      $elemMatch: {
        $eq: req.user._id
      }
    },
    _id: req.params.group_id
  }, function(err, group) {
    if (err) {
      res.send(err);
    } else if (!group) {
      //res.send(new Error("User not in group or it does not exist"));
    }

    members = group[0].members;

    var memberTimes = [];
    asyncLoop(memberTimes, members, 0, function(memberTimes) {
      res.json(memberTimes);
    });
  });

The above code may not run because I didn't run it but this is how you can achieve what you want.

DVassilev
  • 242
  • 1
  • 5
  • 20
0

Yes, applying promises sounds like a good idea.

You'll need to choose an implementation, on which it will depend how to promisify your find method calls. Let's assume you have a find function that takes the receiver and the options and returns you a promise - (edit) in mongoose, you're lucky and .exec() already seems to yield a promise:

function find(model, options) {
  return model.find(options).exec();
}

Then your code will look like this:

find(Group, {
  members: {$elemMatch: {$eq: req.user._id}},
  _id: req.params.group_id
}).then(function(group) {
  if (!group)
    throw new Error("User not in group or it does not exist");

  return Promise.all(group[0].members.map(function(member) {
    return find(Time, {user_id: member});
  }));
}).then(function(memberTimes) {
  res.json(memberTimes);
}, function(err) {
  res.send(err);
});

Here, Promise.all is the part where it waits until the multiple time-find-promises are resolved (in parallel).

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks @DVassilev for the `find` function, though a comment would've been better than suggesting an edit (it did change a bit much to most reviewers). – Bergi Mar 27 '15 at 15:01
  • 1
    No problem, sorry about that. There is also a small error with your your code so I suggested an edit with the corrected version. Thank you. @Bergi – DVassilev Mar 28 '15 at 02:06