2013-04-08 59 views
15

是否可以使用Mongoose一次性在(子)文档上设置多个属性?什么我想一个例子做:使用nodejs/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运算符,但我的问题是document在这个示例中是parentDoc的子文档(嵌入模式)。所以,当我试图做

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

它换成subDocs._id确定的子文档实例。目前我通过手动设置字段来“解决”它,但我希望有一个更好的方法来做到这一点。

回答

29

树立编程基础上,partialUpdate领域的更新只是那些使用点符号字段$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) {}); 
+0

是的,我没有这样做第一。但我希望我不必这样做。 – NilsH 2013-04-08 14:18:53

+0

接受这一点,因为它似乎是唯一的方法。在这种情况下,是否需要'hasOwnProperty'检查,或者这是不需要的,因为它是来自请求主体的普通JSON对象? – NilsH 2013-04-09 07:36:20

+0

循环属性的稍微好一点的方法可能是使用Object.keys,因为它只包含“自己的属性”(尽管可能不会有任何其他属性)。 – NilsH 2013-04-09 11:36:49

6

我已经做了不同的,在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); 
      }); 
     } 
    }); 
} 

灿烂的部分是猫鼬将验证data如果此子文档定义Schema。该代码对于作为数组的文档的任何资源都是有效的。为了简单起见,我没有显示所有数据,但是检查这种情况并正确处理响应错误是一种很好的做法。

1

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

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不分配。

0

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

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