使用nodejs / mongoose部分更新子文档

是否可以使用Mongoose在(子)文档中一次设置多个属性? 我想要做的一个例子:

假设我有这个模式:

var subSchema = new Schema({ someField: String, someOtherField: String }); var parentSchema = new Schema({ fieldOne: String, subDocs: [subSchema] }) 

那我想这样做:

 exports.updateMyDocument = function(req, res) { var parentDoc = req.parentDoc; // The parent document. Set by parameter resolver. var document = req.myDoc; // Sub document of parent. Set by parameter resolver. var partialUpdate = req.body; // updated fields sent as json and parsed by body parser // I know that the statement below doesn't work, it's just an example of what I would like to do. // Updating only the fields supplied in "partialUpdate" on the document document.update(partialUpdate); parentDoc.save(function(err) { if(err) { res.send(500); return; } res.send(204); }); }; 

通常情况下,我可以使用$set操作符实现这一点,但是我的问题是这个例子中的文档是parentDoc一个子文档(embedded模式)。 所以当我试图做

 Parent.update({_id: parentDoc._id, "subDocs._id": document._id}, {$set: {"subDocs.$" : partialUpdate}}, function(err, numAffected) {}); 

它取代了由subDocs._id标识的子文档实例。 目前我通过手动设置字段来“解决”它,但是我希望有一个更好的方法来做到这一点。

基于partialUpdate的字段以编程方式构build一个$set对象,以仅使用点符号更新这些字段:

 var set = {}; for (var field in partialUpdate) { set['subDocs.$.' + field] = partialUpdate[field]; } Parent.update({_id: parentDoc._id, "subDocs._id": document._id}, {$set: set}, function(err, numAffected) {}); 

我已经做了不同的,在一个REST应用程序。

首先,我有这条路线:

 router.put('/:id/:resource/:resourceId', function(req, res, next) { // this method is only for Array of resources. updateSet(req.params.id, req.params.resource, req, res, next); }); 

updateSet()方法

 function updateSet(id, resource, req, res, next) { var data = req.body; var resourceId = req.params.resourceId; Collection.findById(id, function(err, collection) { if (err) { rest.response(req, res, err); } else { var subdoc = collection[resource].id(resourceId); // set the data for each key _.each(data, function(d, k) { subdoc[k] = d; }); collection.save(function (err, docs) { rest.response(req, res, err, docs); }); } }); } 

辉煌的部分是mongoose将validationdata如果您定义该子文档的Schema 。 此代码对于作为数组的文档的任何资源都是有效的。 我并不是为了简单而显示所有的数据,但是检查这种情况并正确处理响应错误是一个很好的做法。

您可以分配或扩展embedded式文档。

  Doc.findOne({ _id: docId }) .then(function (doc) { if (null === doc) { throw new Error('Document not found'); } return doc.embeded.id(ObjectId(embeddedId)); }) .then(function(embeddedDoc) { if (null === embeddedDoc) { throw new Error('Embedded document not found'); } Object.assign(embeddedDoc, updateData)); return embeddedDoc.parent().save(); }) .catch(function (err) { //Do something }); 

在这种情况下,你应该确保_id不是分配的。

我用稍微不同的方式处理了这个,而不使用$ set对象。 我的方法与Guilherme的方法类似,但是一个区别是我将我的方法封装到静态function中,以便在整个应用程序中重用。 下面的例子。

在CollectionSchema.js服务器模型中。

 collectionSchema.statics.decrementsubdocScoreById = function decreasesubdoc (collectionId, subdocId, callback) { this.findById(collectionId, function(err, collection) { if (err) console.log("error finding collection"); else { var subdoc = collection.subdocs.filter(function (subdoc) { return subdoc._id.equals(subdocId); })[0]; subdoc.score -= 1; collection.save(callback); } }); }; 

在服务器控制器

 Collection.decrementsubdocScoreById(collectionId, subdocId, function (err, data) { handleError(err); doStuffWith(data); });