¿Cómo llenar un subdocumento en mongoose después de crearlo?

Estoy agregando un comentario a una lista item.comments. Necesito obtener los datos de usuario de comment.created_by antes de mostrarlos en la respuesta. ¿Cómo debería hacer esto?

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 

Necesito llenar el campo comment.created_by aquí en mi salida res.json:

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

comment.created_by es una referencia de usuario en mi mongoose CommentSchema. Actualmente solo me proporciona una identificación de usuario, la necesito completada con todos los datos de usuario, a excepción de los campos de contraseña y sal.

Aquí está el esquema que las personas han pedido:

 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] }); 

Para completar los subdocumentos a los que se hace referencia, debe definir explícitamente la colección de documentos a la que hace referencia el ID (como created_by: { type: Schema.Types.ObjectId, ref: 'User' } ).

Dado que esta referencia está definida y su esquema también está bien definido, ahora puede simplemente completar populate como de costumbre (por ejemplo, populate('comments.created_by') )

Código de prueba de concepto:

 // 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); }); 

Por último, tenga en cuenta que populate solo funciona para las consultas, por lo que primero debe pasar su elemento a una consulta y luego llamarlo:

 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) }); }); }); 

Esto podría haber cambiado desde que se escribió la respuesta original, pero parece que ahora puede usar la función de llenado de Modelos para hacer esto sin tener que ejecutar un findOne adicional. Ver: http://mongoosejs.com/docs/api.html#model_Model.populate . Querrá utilizar esto dentro del controlador de guardar al igual que findOne.

@ user1417684 y @ chris-foster tienen razón!

extracto del código de trabajo (sin manejo de errores):

 var SubItemModel = mongoose.model('subitems', SubItemSchema); var ItemModel = mongoose.model('items', ItemSchema); var new_sub_item_model = new SubItemModel(new_sub_item_plain); new_sub_item_model.save(function (error, new_sub_item) { var new_item = new ItemModel(new_item); new_item.subitem = new_sub_item._id; new_item.save(function (error, new_item) { // so this is a valid way to populate via the Model // as documented in comments above (here @stack overflow): ItemModel.populate(new_item, { path: 'subitem', model: 'subitems' }, function(error, new_item) { callback(new_item.toObject()); }); // or populate directly on the result object new_item.populate('subitem', function(error, new_item) { callback(new_item.toObject()); }); }); }); 

Enfrenté el mismo problema, pero después de horas de esfuerzo encontré la solución. Puede ser sin usar ningún complemento externo 🙂

 applicantListToExport: function (query, callback) { this .find(query).select({'advtId': 0}) .populate({ path: 'influId', model: 'influencer', select: { '_id': 1,'user':1}, populate: { path: 'userid', model: 'User' } }) .populate('campaignId',{'campaignTitle':1}) .exec(callback); }