Reputation: 12530
I have the following Schema:
var TestSchema = db.Schema({
name: { type: String, required: true, unique: true }
data: []
},
{ strict: false });
var Test = db.model('Test', TestSchema);
Notice it is strict: false
. I would like to be able to upsert
new documents. The thing is, I don't want the data
array to be overwritten, but rather for new documents to be pushed into it.
For example, assuming this is an existing document:
{ name: "hello world",
data:
[ { one: '123',
two: '456' } ]
}
And I wish to upsert
this one:
{ name: "hello world",
new_field: "to be updated"
data:
[ { one: 'pushed to the array',
two: 'xyz' } ]
}
The expected result would be:
{ name: "hello world",
new_field: "to be updated"
data:
[ { one: 'abc',
two: 'xyz' },
{ one: 'pushed to the array',
two: 'xyz'} ]
}
To explicitly explain: the document already exists, so it should be updated. The new field new_field
is updated. However instead of overwriting the existing data
array (as a regular update would), we push the new documents to the array.
I have a very ugly working version that uses three calls to achieve this, which is completely unusable (asynchronous -> duplicates get inserted instead of updated when you throw many queries at the same time).
Can this actually be achieved in Mongoose?
Upvotes: 2
Views: 293
Reputation: 12530
I've solved this in a nice and clean way:
// Let 'test' be a Test object who's values we want to upsert
test = {
name: "hello world",
new_field: "to be updated"
data:
[ { one: 'pushed to the array',
two: 'xyz' } ]
}
// We do this to 'test'
test['$addToSet'] = {
'data': { '$each': test.data }
};
delete test.data
// 'test' now looks like this
// test = {
// 'name': 'hello world',
// 'new_field': 'to be updated'
// '$addToSet': { data: { '$each': [Object] } }
// }
// And this is how we upsert 'test'
// while also upserting values into the data array
Test.findOneAndUpdate({
name: test.name,
}, test, { upsert: true, new: true }, function(error, doc) {
console.log(doc);
});
I've been looking how to do this for a while, and I didn't find it straightforwardly explained anywhere, but it works and it's useful.
Upvotes: 0
Reputation: 203
You can use $push like this:
{$set:{ new_field: "to be updated" },$push:{data:{ one: 'pushed to the array',two: 'xyz' }}}
Update query would be:
db.test.update({name: "hello world"}, {
$set:{ new_field: "to be updated" }
$push:{data:{ one: 'pushed to the array',two: 'xyz' }}});
Upvotes: 2