Mongoose .save不更新数据库

时间:2015-11-05 05:09:02

标签: node.js mongodb express mongoose

我正在尝试使用以下代码更新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 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。

2 个答案:

答案 0 :(得分:2)

你可以找到OneAndUndate而不是先找到id然后保存。如果id不在那里,它将创建一个新的。如果你不想让它保存一个新的设置upsert为false

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

答案 1 :(得分:0)

尝试使用 var updated = _.assign(product, req.body); _.assign代替 _.merge

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

ShitalShah的 answer 突出了合并和扩展之间的差异:

  

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

     

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

JSBin 来说明差异。

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>
 public bool notify(string sender, string message)
    {
        var jGcmData = new JObject();
        var jData = new JObject();
        bool Value;

        jData.Add("message", message);
        jData.Add("name", sender);
        jGcmData.Add("to", "/topics/global");
        jGcmData.Add("data", jData);

        var url = new Uri("https://gcm-http.googleapis.com/gcm/send");
        try
        {
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/json"));

                client.DefaultRequestHeaders.TryAddWithoutValidation(
                    "Authorization", "key=" + API_KEY);

                Task.WaitAll(client.PostAsync(url,
                    new StringContent(jGcmData.ToString(), Encoding.Default, "application/json"))
                .ContinueWith(response =>
                {
                    Console.WriteLine(response);
                    Console.WriteLine("Message sent: check the client device notification tray.");
                }));
            }
            Value = true;
        }
        catch (Exception e)
        {
            Console.WriteLine("Unable to send GCM message:");
            Console.Error.WriteLine(e.StackTrace);
            Value = false;
        }
        return Value;
    }