Mongoose .save不更新数据库

4
我将尝试使用以下代码更新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成功地从数据库中检索出现有文档,其中只包含“start”数组。
{
    "_id" : ObjectId("563a95d9cc2d38622b867ecf"),
    "start" : [
        "07-02",
        "12-01",
        "04-01",
        "06-01",
        "07-01"
    ]
}

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

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

{
    "_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模式:

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

任何关于此处可能发生的情况的指导?我正在使用MongoDB版本3.0.6和Mongoose版本4.1.12,运行在NodeJS版本4.1.1和Express版本4.13.3上。
2个回答

3

您可以使用findOneAndUpdate代替先查找id再保存。如果id不存在,它将创建一个新的。如果不想保存新的,请将upsert设置为false。

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

一个小提示。使用findOneAndUpdate不会返回更新后的文档,而是原始文档。 - Thomas Bormans
我会尝试使用这个解决方法。似乎那些没有正确更新的值是数组(基本类型的更新似乎很好)。 - Mike McBride
这似乎解决了问题,现在值已经被提交到数据库中了;感谢您的建议。虽然我还有点好奇为什么原始方法不起作用,但我可以接受替代方案。至于Thomas关于返回值的评论,在我的应用程序中没有影响,因为一旦文档被写入,我就完成了...但在调试代码时确实让我有点困惑!感谢您的帮助。 - Mike McBride

0

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

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

这个答案由ShitalShah强调了合并和扩展之间的区别:

这是扩展/赋值的工作原理:对于源中的每个属性,将其值原样复制到目标中。如果属性值本身是对象,则不会递归遍历其属性。整个对象将从源中取出并设置到目标中。

这是合并的工作原理:对于源中的每个属性,检查该属性是否是对象本身。如果是,则递归向下,并尝试将子对象属性从源映射到目标。因此,我们实际上是将对象层次结构从源合并到目标中。而对于扩展/赋值,只是简单地将属性从源复制到目标。

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合并方法会解决这个问题,但我可能没有理解到重点。 - Mike McBride
1
Shital,这很有启发性。在这种特定情况下,我可能可以使用.assign,但对于其他操作,.merge是正确的方法(其中我只发送更新的参数),它们都使用相同的接口。.default操作的行为让我有点惊讶,需要思考一下。感谢您的建议! - Mike McBride

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接