Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to populate a sub-document in mongoose after creating it?

I am adding a comment to an item.comments list. I need to get the comment.created_by user data before I output it in the response. How should I do this?

    Item.findById(req.param('itemid'), function(err, item){         var comment = item.comments.create({             body: req.body.body             , created_by: logged_in_user         });          item.comments.push(comment);          item.save(function(err, item){             res.json({                 status: 'success',                 message: "You have commented on this item",  //how do i populate comment.created_by here???                  comment: item.comments.id(comment._id)             });         }); //end item.save     }); //end item.find 

I need to populate the comment.created_by field here in my res.json output:

                comment: item.comments.id(comment._id) 

comment.created_by is a user reference in my mongoose CommentSchema. It currently is only giving me a user id, I need it populated with all the user data, except for password and salt fields.

Here is the schema as people have asked:

var CommentSchema = new Schema({     body          : { type: String, required: true }   , created_by    : { type: Schema.ObjectId, ref: 'User', index: true }   , created_at    : { type: Date }   , updated_at    : { type: Date } });  var ItemSchema = new Schema({     name    : { type: String, required: true, trim: true }   , created_by  : { type: Schema.ObjectId, ref: 'User', index: true }   , comments  : [CommentSchema] }); 
like image 768
chovy Avatar asked Oct 23 '12 08:10

chovy


People also ask

Can a document have a sub document in MongoDB?

In Mongoose, subdocuments are documents that are nested in other documents. You can spot a subdocument when a schema is nested in another schema. Note: MongoDB calls subdocuments embedded documents.

How does populate work in Mongoose?

Mongoose Populate() Method. In MongoDB, Population is the process of replacing the specified path in the document of one collection with the actual document from the other collection.

How do you populate with aggregate?

With the latest version of mongoose (mongoose >= 3.6), you can but it requires a second query, and using populate differently. After your aggregation, do this: Patients. populate(result, {path: "patient"}, callback);


2 Answers

In order to populate referenced subdocuments, you need to explicitly define the document collection to which the ID references to (like created_by: { type: Schema.Types.ObjectId, ref: 'User' }).

Given this reference is defined and your schema is otherwise well defined as well, you can now just call populate as usual (e.g. populate('comments.created_by'))

Proof of concept code:

// Schema var mongoose = require('mongoose'); var Schema = mongoose.Schema;  var UserSchema = new Schema({   name: String });  var CommentSchema = new Schema({   text: String,   created_by: { type: Schema.Types.ObjectId, ref: 'User' } });  var ItemSchema = new Schema({    comments: [CommentSchema] });  // Connect to DB and instantiate models     var db = mongoose.connect('enter your database here'); var User = db.model('User', UserSchema); var Comment = db.model('Comment', CommentSchema); var Item = db.model('Item', ItemSchema);  // Find and populate Item.find({}).populate('comments.created_by').exec(function(err, items) {     console.log(items[0].comments[0].created_by.name); }); 

Finally note that populate works only for queries so you need to first pass your item into a query and then call it:

item.save(function(err, item) {     Item.findOne(item).populate('comments.created_by').exec(function (err, item) {         res.json({             status: 'success',             message: "You have commented on this item",             comment: item.comments.id(comment._id)         });     }); }); 
like image 75
jsalonen Avatar answered Sep 24 '22 00:09

jsalonen


This might have changed since the original answer was written, but it looks like you can now use the Models populate function to do this without having to execute an extra findOne. See: http://mongoosejs.com/docs/api.html#model_Model.populate. You'd want to use this inside the save handler just like the findOne is.

like image 33
user1417684 Avatar answered Sep 21 '22 00:09

user1417684