Search code examples
javascriptnode.jsmongodbexpressmongoskin

How do you retrieve "related" documents in MongoDB?


I'm using node.js, express, and MongoSkin driver for MongoDB.

I'm trying to render a view in my blog application that shows a list of blog posts. Each post has a reference to an author ID. I'm trying to make a second query for each post to get the full author document for each reference. Everything being asynchronous is confusing me quite a bit.

On the callback for getting the list of blog posts I'm then looping through each post and making a second call to get the associated user. I don't know how to only render the view once all posts have had their author document added to it. I'm not even sure I'm doing any of this right. How to I reference other documents to accomplish what I want to do?

// getPageOfPosts is a custom helper that accepts the page number and number of items to retrieve.
db.collection('blogPosts').getPageOfPosts(req.param('page') || 1, 3, function (err, blogPosts) {
    if (err) req.next(err);
    blogPosts.forEach(function (blogPost) {
        db.collection('users').findOne({ _id: blogPost.authorId }, function (err, user) {
            if (err) req.next(err);
            blogPost.author = user;
        });
    });
    var viewModel = {
        posts: blogPosts
    };
    res.render('blog/index', viewModel);
});

Solution

  • You can use async library in nodejs https://github.com/caolan/async. This helps to execute group of functions in series or parallel and once all those are completed a final callback is invoked where you can do your rendering part.


    Edit by question author:

    This was an excellent suggestion and worked beautifully! Here's how:

    db.collection('blogPosts').getPage(req.param('page') || 1, 3, function (err, blogPosts) {
        if (err) req.next(err);
        var addUsersArray = [];
        blogPosts.forEach(function (blogPost) {
            addUsersArray.push(function (callback) {
                db.collection('users').findOne({ _id: blogPost.author }, function (err, user) {
                    if (err) callback(err);
                    blogPost.author = user;
                    callback(null, blogPost);
                });
            });
        });
        async.parallel(addUsersArray, function (err, blogPosts) {
            if (err) req.next(err);
            var viewModel = {
                posts: blogPosts
            };
            res.renderView('blog/index', viewModel);
        });
    });
    

    the following also works well

    db.collection('blogPosts').getPageOfPosts(req.param('page') || 1, 3, function (err, blogPosts) {
    if (err) req.next(err);
    async.each(blogPosts,function (blogPost,cb) {
        db.collection('users').findOne({ _id: blogPost.authorId }, function (err, user) {
            if (err) req.next(err);
            blogPost.author = user;
            cb(err);
        });
    },function(error)
    {
        var viewModel = {
        posts: blogPosts
        };
        res.render('blog/index', viewModel);
    });
    

    });