Search code examples
node.jsmongodbmonk

Chaining nested asynchronous finds with Node.js monk and MongoDB


Using Node.js monk and MongoDB, I want to mimic a table join:

  1. Do a find on collection A
  2. For each result X, do a find in collection B, and update X
  3. Return updated list of results

The asynchronous nature of database commands in monk is giving me trouble. This is my initial code. It doesn't work because the second call to find returns a promise immediately, and the results in xs are sent in the response before they can be updated.

var db = require('monk')('localhost/mydb');
db.get('collection').find({}, function(e,xs) {
  xs.forEach(function(x){
    coll_b.find({a_id:x._id}, function(e,bs) {
      a['bs'] = bs;
    });
  });
  res.json({'results':as});
});

I feel like I should use promise chaining here, but I cannot figure out how to do it. Any help would be greatly appreciated.


Solution

  • I think I solved it in this way, inspired by this answer:

    var db = require('monk')('localhost/mydb');
    
    // Initial find
    db.get('collection').find({}, function(e,xs) {
    
      // Get inner finds as a list of functions which return promises
      var tasks = xs.map(function(x){
        return function() {
          return coll_b.find({a_id:x._id}, function(e,bs) {
            a['bs'] = bs;
          });
        }
      });
    
      // Chain tasks together
      var p = tasks[0](); // start the first one
      for(var i = 1; i < tasks.length; i++) p = p.then(tasks[i]);
    
      // After all tasks are done, output results
      p.then(function(_x){
        res.json({'results':xs});
      });
    
    });
    

    I still feel like this code could be minimised by using chain(), but at least this works as expected.

    Note: I realise that performing a second find for each result is not necessarily efficient, but that's not my concern here.