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

Nil*_*lsH 16 mongoose mongodb node.js

是否可以使用Mongoose一次性在(子)文档上设置多个属性?我正在尝试做的一个例子:

假设我有这个架构:

var subSchema = new Schema({
    someField: String,
    someOtherField: String
});

var parentSchema = new Schema({
    fieldOne: String,
    subDocs: [subSchema]
})
Run Code Online (Sandbox Code Playgroud)

然后我想做:

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);
    }); 
};
Run Code Online (Sandbox Code Playgroud)

通常,我可以使用$set运算符实现这一点,但我的问题是document在这个例子中是一个子文档(嵌入式模式)parentDoc.所以当我试着做的时候

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

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

Joh*_*yHK 33

$set基于以下字段partialUpdate以编程方式构建对象:使用点表示法更新这些字段:

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) {});
Run Code Online (Sandbox Code Playgroud)

  • 从v3.9.3开始,update()需要另外两个选项:setDefaultsOnInsert和runValidators @see https://github.com/LearnBoost/mongoose/commit/1d8c3e96c7b11497d3325e9cf1f7ae66c9ee560e (3认同)

Gui*_*rme 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);
});
Run Code Online (Sandbox Code Playgroud)

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);
            });
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

很棒的部分是mongoose将验证data你是否定义了Schema这个子文档.此代码对于作为Array的文档的任何资源都有效.为简单起见,我没有显示所有数据,但检查这种情况并正确处理响应错误是一种很好的做法.