Reputation: 48
A piece of my Mongo document structure is:
{ "_id": ObjectId("xxxxxx..."),
"Country" : "UNITED KINGDOM",
"Line" : "something",
"Records" : [
{"rdata" : "foo", "rtype" : "X", "name" : "John"},
{"rdata" : "bar", "rtype" : "Y", "name" : "Bill"}
], ...
I'm using Mongoose to access the data via the following model:
var Record = new Schema({
rdata: String,
rtype: String,
name: String
}, {_id: false});
var ThingSchema = new Schema({
Country: String,
Line : String,
Records : [Record],
Let's say I want to update the "Line" property of one of my documents, from being "Line" : "something"
to "Line" : "way more interesting"
by sending a PUT request to the appropriate API URL. I can see that the data being sent is all right. This is what the API does:
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);
});
});
};
The API comes back with 200/OK - but I see the following updated data:
{ "_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"}
], ...
Notice, how the Records array got messed up by overwriting my second record by duplicating the first one. (If I switch on the automatic addition of '_id' to the subdocument by Mongoose, then even the "_id" fields will be the same on the two records within the array).
It may be relevant, that originally the Records were not added via Mongoose - but by importing a JSON document. Any suggestion as to how to start finding out why this is happening would be fantastic.
Upvotes: 2
Views: 70
Reputation: 103425
Try changing _.merge
to _.extend
, then call save directly on the thing
document returned by the findById()
method instead of the merged object 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);
});
});
}
Another option is using the set method on the entity i.e. thing.set(req.body)
before calling the save method on the thing
object.
This answer by ShitalShah highlights the differences between merge and extend that is causing duplicates in your resulting object with merge but essentially:
Here's how extend/assign works: For each property in source, copy its value as-is to destination. if property values themselves are objects, there is no recursive traversal of their properties. Entire object would be taken from source and set in to destination.
Here's how merge works: For each property in source, check if that property is object itself. If it is then go down recursively and try to map child object properties from source to destination. So essentially we merge object hierarchy from source to destination. While for extend/assign, it's simple one level copy of properties from source to destination.
JSBin to illustrate the differences
Upvotes: 2