Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

How to Make Mongoose's .populate() work with an embedded schema/subdocument?

How to Make Mongoose's .populate() work with an embedded schema/subdocument?

Problem

I read up that you can make Mongoose auto pouplate ObjectId fields. However I am having trouble structuring a query to populate fields in a subdoc.

My models:

var QuestionSchema = new Schema({
    question_text: String,
    type: String,
    comment_field: Boolean,
    core_question: Boolean,
    identifier: String
});

var SurveyQuestionSchema = new Schema({
    question_number: Number,
    question: {type: Schema.Types.ObjectId, ref: 'Question', required: true} //want this popuplated
});

var SurveySchema = new Schema({
    start_date: Date,
    end_date: Date,
    title: String,
    survey_questions: [SurveyQuestionSchema]
});

Right now I achieve the effect by doing:

Survey.findById(req.params.id, function(err, data){
    if(err || !data) { return handleError(err, res, data); }

    var len = data.survey_questions.length;
    var counter = 0;

    var data = data.toJSON();

    _.each(data.survey_questions, function(sq){
        Question.findById(sq.question, function(err, q){
            sq.question = q;

            if(++counter == len) {
                res.send(data);
            }
        });
    });
});

Which obviously is a very error-prone way of doing it...

Problem courtesy of: Toli Zaslavskiy

Solution

As I noted in the comments above, this is an issue currently under scrutiny by the mongoose team (not yet implemented).

Also, looking at your problem from an outsider's perpsective, my first thought would be to change the Schema to eliminate SurveyQuestion, as it has a very relational db "join" model feel. Mongoose embedded collections have a static sort order, eliminating the need for keeping a positional field, and if you could handle question options on the Survey itself, it would reduce the schema complexity so you wouldn't need to do the double-populate.

That being said, you could probably reduce the queries down to 2, by querying for all the questions at once, something like:

Survey.findById(req.params.id, function(err, data){
    if(err || !data) { return handleError(err, res, data); }

    var data = data.toJSON();
    var ids = _.pluck(data.survey_questions, 'question');

    Question.find({_id: { $in: ids } }, function(err, questions) {
        _.each(data.survey_questions, function(sq) {
            sq.question = _.find(questions, function(q) { 
                var id = q._id.toString();
                return id == sq.question; 
            });

        });
        res.send(data);
    });
});
Solution courtesy of: numbers1311407

Discussion

View additional discussion.



This post first appeared on Node.js Recipes, please read the originial post: here

Share the post

How to Make Mongoose's .populate() work with an embedded schema/subdocument?

×

Subscribe to Node.js Recipes

Get updates delivered right to your inbox!

Thank you for your subscription

×