【问题标题】:Mongoose .save not updating databaseMongoose .save 不更新数据库
【发布时间】:2016-02-05 20:21:55
【问题描述】:

我正在尝试使用以下代码更新 MongoDB 文档:

exports.update = function (req, res) {
  if (req.body._id) { delete req.body._id; }
  Product.findById(req.params.id, function (err, product) {
      if (err) { return handleError(res, err); }
      if (!product) { return res.send(404); }
      var updated = _.merge(product, req.body);
      updated.save(function (err) {
          if (err) { return handleError(res, err); }
          return res.status(200).json(product);
      });
  });
};

代码执行成功,但.save 不会更新现有的数据库数组值。 req.body 的内容如下(特别注意的是“start”数组中的值):

{
    "_id" : ObjectId("563a95d9cc2d38622b867ecf"),
    "productName" : "Product Name",
    "productVersion" : "1",
    "productOverview" : "Description of product.",
    "productManager" : ObjectId("563a90de195e72712a197d06"),
    "businessPriority" : "1 Must Do",
    "businessRank" : 2,
    "businessFactors" : {
        "growth" : true,
        "diversification" : true,
        "architecture" : false,
        "riskMitigation" : false,
        "retention" : false
    },
    "complete" : false,
    "phase" : "Discovery",
    "comment" : [
        "Discovery phase comments",
        "Development phase comments",
        "Pilot phase comments",
        "Pre-launch phase comments",
        "Post-launch phase comments"
    ],
    "finish" : [
        "2015-11-30",
        "2016-03-31",
        "2016-05-31",
        "2016-06-30",
        "2016-08-31"
    ],
    "start" : [
        "2015-07-01",
        "2015-12-01",
        "2016-04-01",
        "2016-06-01",
        "2016-07-01"
    ]
}

.findById 成功从数据库中检索到现有文档,其中仅包含“开始”数组:

{
    "_id" : ObjectId("563a95d9cc2d38622b867ecf"),
    "start" : [
        "07-02",
        "12-01",
        "04-01",
        "06-01",
        "07-01"
    ]
}

lodash .merge 函数构造一个正确的“更新”记录(其数据内容与上面的 req.body 相同)。

.save 执行没有错误,并返回 200 状态。但是,数据库中文档的内容仍然包含“开始”元素的原始数据:

{
    "_id" : ObjectId("563a95d9cc2d38622b867ecf"),
    "start" : [
        "07-02",
        "12-01",
        "04-01",
        "06-01",
        "07-01"
    ],
    "businessFactors" : {
        "growth" : true,
        "diversification" : true
    },
    "businessPriority" : "1 Must Do",
    "businessRank" : 2,
    "comment" : [
        "Discovery phase comments",
        "Development phase comments.",
        "Pilot phase comments",
        "Pre-launch phase comments",
        "Post-launch phase comments"
    ],
    "finish" : [
        "2015-11-30",
        "2016-03-31",
        "2016-05-31",
        "2016-06-30",
        "2016-08-31"
    ],
    "phase" : "Discovery",
    "productManager" : ObjectId("563a90de195e72712a197d06"),
    "productName" : "New Product",
    "productOverview" : "Description of product.",
    "productVersion" : "1",
    "__v" : 1
}

Mongoose Schema 如下:

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var productSchema = new Schema(
    {
        productName             : String,
        productVersion          : String,
        productOverview         : String,
        productManager          : Schema.Types.ObjectId,
        businessPriority        : String,
        businessRank            : Number,
        businessFactors         : {
            retention           : Boolean,
            growth              : Boolean,
            diversification     : Boolean,
            architecture        : Boolean,
            riskMitigation      : Boolean
        },
        start                   : [ String ],
        finish                  : [ String ],
        comment                 : [ String ],
        phase                   : String,
        complete                : Boolean
    },
    {
        collection              : 'products'
    }
);

module.exports = mongoose.model('Product', productSchema);

对这里可能发生的事情有任何指导吗?我在 NodeJS 版本 4.1.1 和 Express 版本 4.13.3 上使用 MongoDb 版本 3.0.6 和 Mongoose 版本 4.1.12。

【问题讨论】:

    标签: node.js mongodb express mongoose


    【解决方案1】:

    您可以使用 findOneAndUpdate 而不是先查找 id 然后保存。如果 id 不存在,它将创建一个新的。如果你不希望它保存一个新的设置 upsert 为 false

    Product.findOneAndUpdate({_id:<your id>, {$set: <your merged JSON>}, {upsert:true}, function(err, effected, raw){});
    

    【讨论】:

    • 一点旁注。使用 findOneAndUpdate 不会返回更新后的文档,而是返回原始文档。
    • 我会试试这个作为一种解决方法。似乎没有正确更新的值是数组(基元似乎更新得很好)。
    • 这似乎解决了问题,现在将值发布到数据库中;谢谢你的建议。仍然有点好奇为什么原来的方法不起作用,但我可以接受另一种方法。至于托马斯关于返回值的评论,这对我的应用程序没有影响,因为我在编写文档后就完成了......但是在调试代码时它确实让我有点困惑!感谢您的帮助。
    【解决方案2】:

    尝试使用 _.extend_.assign 代替 _.merge

    var updated = _.assign(product, req.body);
    

    ShitalShah 的 answer 强调了合并和扩展之间的区别:

    extend/assign 的工作方式如下:对于源代码中的每个属性,复制其 价值原样到目的地。如果属性值本身是对象, 它们的属性没有递归遍历。整个对象 将从源中获取并设置到目的地。

    以下是合并的工作原理:对于源中的每个属性,检查是否 属性是对象本身。如果是然后递归下去并尝试 将子对象属性从源映射到目标。所以 本质上,我们将对象层次结构从源合并到目标。 而对于扩展/分配,它是简单的一级属性副本 源到目的地。

    JSBin 来说明差异。

    exports.update = function (req, res) {
      if (req.body._id) { delete req.body._id; }
      Product.findById(req.params.id, function (err, product) {
          if (err) { return handleError(res, err); }
          if (!product) { return res.send(404); }
          var updated = _.assign(product, req.body);
          updated.save(function (err) {
              if (err) { return handleError(res, err); }
              return res.status(200).json(product);
          });
      });
    };
    

    查看下面的演示。

    var dest = {
    	foo : {
    		b1 : "b1 value",
    		b2 : "b2 value"
    	},
    	baz : {
    		q1 : "q1 value"
    	},
    	mofo : "mofo value"
    };
    
    var src = { 
    	foo : { 
    		b1: "overwritten b1", 
    		b3: "b3 value"
    	},
    	mofo : "overwritten mofo"
    };
    
    
    var assigned = _.clone(dest);
    _.assign(assigned,src);
    console.log("assign:", assigned);
    
    var merged = _.clone(dest);
    _.merge(merged,src);
    console.log("merge:", merged);
    
    var defaulted = _.clone(dest);
    _.defaults(defaulted,src);
    console.log("defaults:", defaulted);
    
    pre.innerHTML = "assign: " + JSON.stringify(assigned, null, 4) + "</br>merge: " + JSON.stringify(merged, null, 4) + "</br>defaults: "+ JSON.stringify(defaulted, null, 4);
    <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
    <pre id="pre"></pre>

    【讨论】:

    • 感谢您的建议。代码段最初来自我在之前的项目中使用过的框架,所以我没有修改他们合并这两个对象的方法。无论如何,“更新”的对象具有正确的内容,只是没有在“保存”之后将其返回到数据库中。我看不出改变 lodash 整合方法会如何解决这个问题,但我可能会错过重点。
    • Shital,这是信息。在这种特殊情况下,我可能可以使用 .assign,但是对于其他操作, .merge 是正确的方法(我只发送更新的参数)并且它们都使用相同的接口。我对 .default 操作的行为感到有些惊讶,我将不得不考虑一下。感谢您的意见!
    猜你喜欢
    • 1970-01-01
    • 2014-08-28
    • 2017-01-16
    • 2014-10-27
    • 2014-02-17
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 2013-02-26
    相关资源
    最近更新 更多