How to return Mongoose results from the find method?


Question

Everything I can find for rending a page with mongoose results says to do it like this:

users.find({}, function(err, docs){
    res.render('profile/profile', {
        users:     docs
    });
});

How could I return the results from the query, more like this?

var a_users = users.find({}); //non-working example

So that I could get multiple results to publish on the page?

like:

/* non working example */
var a_users    = users.find({});
var a_articles = articles.find({});

res.render('profile/profile', {
      users:    a_users
    , articles: a_articles
});

Can this be done?

1
55
5/31/2011 12:37:19 AM

Accepted Answer

You're trying to force a synchronous paradigm. Just does't work. node.js is single threaded, for the most part -- when io is done, the execution context is yielded. Signaling is managed with a callback. What this means is that you either have nested callbacks, named functions, or a flow control library to make things nicer looking.

https://github.com/caolan/async#parallel

async.parallel([
   function(cb){
      users.find({}, cb);
   },
   function(cb){
      articles.find({}, cb);
   }
], function(results){
   // results contains both users and articles
});
67
11/6/2012 5:27:59 AM

I'll play the necromancer here, as I still see another, better way to do it.

Using wonderful promise library Bluebird and its promisifyAll() method:

var Promise = require('bluebird');
var mongoose = require('mongoose');

Promise.promisifyAll(mongoose); // key part - promisification

var users, articles; // load mongoose models "users" and "articles" here

Promise.props({
    users: users.find().execAsync(),
    articles: articles.find().execAsync()
  })
  .then(function(results) {
    res.render('profile/profile', results);
  })
  .catch(function(err) {
    res.send(500); // oops - we're even handling errors!
  });

Key parts are as follows:

Promise.promisifyAll(mongoose);

Makes all mongoose (and its models) methods available as functions returning promises, with Async suffix (.exec() becomes .execAsync(), and so on). .promisifyAll() method is nearly-universal in Node.JS world - you can use it on anything providing asynchronous functions taking in callback as their last argument.

Promise.props({
    users: users.find().execAsync(),
    articles: articles.find().execAsync()
  })

.props() bluebird method takes in object with promises as its properties, and returns collective promise that gets resolved when both database queries (here - promises) return their results. Resolved value is our results object in the final function:

  • results.users - users found in the database by mongoose
  • results.articles - articles found in the database by mongoose (d'uh)

As you can see, we are not even getting near to the indentation callback hell. Both database queries are executed in parallel - no need for one of them to wait for the other. Code is short and readable - practically corresponding in length and complexity (or rather lack of it) to wishful "non-working example" posted in the question itself.

Promises are cool. Use them.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon