当文档被更新时,MongooseJS会调用预先导入的数据

我的一个Mongo文档结构是:

{ "_id": ObjectId("xxxxxx..."), "Country" : "UNITED KINGDOM", "Line" : "something", "Records" : [ {"rdata" : "foo", "rtype" : "X", "name" : "John"}, {"rdata" : "bar", "rtype" : "Y", "name" : "Bill"} ], ... 

我正在使用Mongoose通过以下模型访问数据:

 var Record = new Schema({ rdata: String, rtype: String, name: String }, {_id: false}); var ThingSchema = new Schema({ Country: String, Line : String, Records : [Record], 

比方说,我想通过向合适的API URL发送一个PUT请求来更新我的文档之一的“Line”属性,从"Line" : "something"更改为"Line" : "way more interesting" 。 我可以看到发送的数据是正确的。 API就是这样做的:

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

该API回来200 / OK – 但我看到以下更新的数据:

 { "_id": ObjectId("xxxxxx..."), "Country" : "UNITED KINGDOM", "Line" : "way more interesting", <-- updated correctly "Records" : [ {"rdata" : "foo", "rtype" : "X", "name" : "John"}, {"rdata" : "foo", "rtype" : "X", "name" : "John"} ], ... 

请注意,logging数组是如何通过复制第一条logging来覆盖第二条logging的。 (如果我通过Mongoose自动添加'_id'到子文档,那么即使数组中的两个logging中的“_id”字段也是一样的)。

这可能是相关的,最初的logging不是通过Mongoose添加 – 而是通过导入JSON文件。 任何有关如何开始发现这种情况的build议都是太棒了。

尝试将_.merge更改为_.extend ,然后直接调用findById()方法返回的thing文档而不是updated的合并对象:

 exports.update = function(req, res) { if(req.body._id) { delete req.body._id; } Thing.findById(req.params.id, function (err, thing) { if (err) { return handleError(res, err); } if(!thing) { return res.send(404); } _.extend(thing, req.body); thing.save(function (err) { if (err) { return handleError(res, err); } return res.json(200, thing); }); }); } 

另一个select是在调用thing对象的save方法之前,在实体上使用set方法,即thing.set(req.body)

这个由ShitalShah的答案突出了合并和扩展之间的差异,导致您的结果对象合并重复,但基本上重复:

以下是扩展/分配的工作原理:对于源中的每个属性,将其值原样复制到目标。 如果属性值本身是对象,则不会recursion遍历它们的属性。 整个对象将从源获取并设置到目的地。

以下是合并的工作原理:对于源代码中的每个属性,检查该属性是否是对象本身。 如果它是recursion下来,并尝试将源对象的子对象属性映射。 所以基本上我们合并了从源到目标的对象层次。 而对于扩展/分配,它是从源到目的地的简单属性级别的副本。

JSBin来说明差异