1

I am trying to use the async module to cut back the 'callback hell' specific to Node.js. Basically, I am trying to use async.series to retrieve some info from the database and display it in my view. Still, I get no results in my view.

This is the code that I have so far:

// Search
exports.search = function(req, res) {

    var x = [];



        async.series([
            function(cb) {
                Lang.find({ lang: req.query.keyword }).sort({ verbal: -1 }).exec(function(err, langs) {              

                    cb(null, langs);

                });
            },

            function(cb) {
                Human.find({}, function(err, humans) {            

                    cb(null, humans);
                });
            }], 

            function(err, results) {
                if (err) {
                    res.send(500);
                }

                for(var i = 0; i < results[0].length; i++) {
                    for(var j = 0; j < results[1].length; j++) {
                        if(results[1][j]._id == results[0][i].human) {
                             x.push(results[1][j]);
                        }
                    }
                } 

                res.render('myView', { title: 'Search Results', humans: x });
            }
        );
    }

I first want to query the Lang model (MongoDB) and find the records that match req.query.keyword. Afterwards, I want to query the Human model and find all the Humans that have that specific language skill. Also, the FOR loop is meant to eliminate duplicates from my array since a Human might have several languages.

vladzam
  • 5,462
  • 6
  • 30
  • 36

1 Answers1

0

If I understand your code correctly, you might want to use async.waterfall instead. It passes the result from one function as an argument to the next. There are also some other optimizations that can be made:

async.waterfall([
  function(done) {
    Lang
      .find({ lang: req.query.keyword })
      .sort({ verbal: -1 }) // (not really useful)
      .select('human')      // not strictly necessary, but saves a bit of space
      .exec(done);          // short for : 
                            //   .exec(function(err, results) {
                            //     done(err, results);
                            //   });
  },
  function(langs, done) {
    // extract the 'human' property from each result
    var _ids = langs.map(function(lang) {
      return lang.human;
    });

    // perform a query finding all humans in the list of ids
    Human
      .find({ _id : { $in : _ids } })
      .exec(done);
  }
], function(err, humans) {
  if (err)
    return res.send(500);
  res.render('myView', {
    title : 'Search Results',
    humans: humans
  });
});

EDIT: because $in doesn't preserve order and this answer suggests that using $or will, try this as an alternative for the second query:

  ...
  function(langs, done) {
    var query = langs.map(function(lang) {
      return { _id : lang.human };
    });

    Human
      .find({ $or : query })
      .exec(done);
  }
  ...
Community
  • 1
  • 1
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • Just tried out your code and it works perfectly! Still, the order descending function is because I want the results to be returned based on their language skill in descending order. So, for example, if I have 2 humans that have an English skill of 5 and 10, I want the person with 10 to be the first in the search results list. This snippet returns the list of humans in the order that it finds them. – vladzam Oct 30 '13 at 10:22
  • @shinpou if the order is importing, I'm not entirely sure if using `$in` will keep it ordered. So please test :) – robertklep Oct 30 '13 at 10:26
  • I've tried it and the order is not the one I am looking for :( But still! I am getting the proper results back! Thank you a lot! Still, could you point me in some direction (documentation) or explain the done callback function. Is it simply integrated in the waterfall function and is used to pass the parameter to the next function? – vladzam Oct 30 '13 at 10:29
  • @shinpou the `done` callback is the same as the one you called `cb` (I just call it `done` usually, but you can give it any name you want). I added some extra comments to my code to make it a bit more clear. – robertklep Oct 30 '13 at 11:03
  • Is there any way I can make the _ids array be ordered ascending based on lang.verbal? Could I build a two dimensional array with lang.human and lang.verbal and afterwards generate a new array ordered ascending? – vladzam Oct 30 '13 at 11:17
  • @shinpou the `_ids` array will be ordered correctly (because it's based on the ordering returned by the first query), it's just that the `$in` operation doesn't return the results in the same order. Instead of using `$in`, [this answer](http://stackoverflow.com/a/13185509/893780) suggests that you could use `$or` instead (but that requires a bit of a rewrite, I'll edit my answer) – robertklep Oct 30 '13 at 11:23