【问题标题】:Partial update of a subdocument with nodejs/mongoose使用 nodejs/mongoose 部分更新子文档
【发布时间】:2013-03-30 06:40:33
【问题描述】:

是否可以使用 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 运算符来实现此目的,但我的问题是此示例中的documentparentDoc 的子文档(嵌入式架构)。所以当我尝试做的时候

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

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

【问题讨论】:

    标签: node.js mongodb mongoose


    【解决方案1】:

    基于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) {});
    

    【讨论】:

    • 是的,我确实先这样做了。但我希望我不必那样做。
    • 接受这个,因为它似乎是唯一的方法。在这种情况下,是否需要 hasOwnProperty 检查,还是不需要,因为它是来自请求正文的纯 JSON 对象?
    • 循环属性的更好方法可能是使用Object.keys,因为它只会包含“自己的属性”(尽管可能不会有任何其他属性)。
    • @NilsH 这不会有什么坏处,但如果它是从请求构建的简单 JS 对象,则不需要。不过,您应该考虑的是是否需要验证所包含的字段,而不是盲目地接受整个对象。
    • 从 v3.9.3 开始,update() 有 2 个附加选项:setDefaultsOnInsert 和 runValidators @see github.com/LearnBoost/mongoose/commit/…
    【解决方案2】:

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

    如果您为此子文档定义Schema,猫鼬将验证data。此代码对文档的任何数组资源都有效。为简单起见,我没有显示所有数据,但这是检查这种情况并正确处理响应错误的好习惯。

    【讨论】:

      【解决方案3】:

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

          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 没有分配。

      【讨论】:

        【解决方案4】:

        我在不使用 $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);
        });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-07-09
          • 2012-10-27
          • 1970-01-01
          • 2017-08-13
          • 1970-01-01
          • 1970-01-01
          • 2022-01-22
          相关资源
          最近更新 更多