On below data set, I want to find "pId=123" and "gmData.CarData.cars.oid=’car_1’", if found then update "gmData.CarData.cars.dt" with new value, else Push document in "gmData.CarData.cars".
{
"_id" : ObjectId("65a52a03ed163a5b8a06a80d"),
"pId" : 123,
"fv" : "1.2.3",
"gmData" : {
"CarData" : {
"cars" : [
{
"oid" : "car_1",
"dt" : {
"id" : "car_1",
"age" : 123,
"add" : "add_1"
}
},
{
"oid" : "car_2",
"dt" : {
"id" : "car_2",
"age" : 234,
"add" : "add_2"
}
},
{
"oid" : "car_3",
"dt" : {
"id" : "car_3",
"age" : 345,
"add" : "add_3"
}
}
]
}
}
}
Tried so far using:
db.test.updateOne(
{ $and: [{pId:123}, {"gmData.CarData.cars.oid":"car_1"}],},
{
"$set":{"gmData.CarData.cars.$[curr].dt":{"id":"car_11","age":1123,"add":"add_11"}}
},
{
arrayFilters: [ { "curr.oid": { $eq: "car_1" } } ]
}
)
Above query is working, if "gmData.CarData.cars.oid.car_1" exists. But I want to perform Push object .dt":{ "id" : "car_11","age" : 1123,"add" : "add_11"}}
in "gmData.CarData.cars", if not exist.
2
Answers
I’ve worked on an answer without
arrayFilters
.Here is what i have :
It will look for the document with
pId == 123
and update or create the car whereoid == 'car_1'
.You can try it for yourself on mongoplayground.
There are two basic ways to solve this:
Update with aggregation pipeline
The most efficient way is to use a single update operation using an aggregation pipeline (see also @Fourchette’s answer). I have also put together a MongoPlayground that contains a similar aggregation pipeline. In the first stage, the
cars
array is initialized as an empty array if it does not exist yet:Doing this in an extra step simplifies the following stage that upserts the array element using a complex expression:
The expression first checks whether a sub-document with a matching id exists; in this case, a
$map
expression maps each array element. If the id of the array element matches the one you are looking for, thedt
property is updated. Otherwise the original array element is returned.In case there is no array element with a matching id, a
$concatArray
expression adds the new sub-document to the array.Multiple update statements
A simpler approach is to use two update statements: the first statement looks for a document that already has an element with a matching id:
(mongoplayground)
It is important to check the result of the operation. If a document was matched and modified, the following operation can be omitted. Otherwise, you’d issue another update statement – this time checking for the
pId
without a matchingoid
value in thecars
array. In this case, the new item is pushed into the array:(mongoplayground)
Please note that the second approach is safe against race conditions due to the filters for the update statements. However, it is not as efficient as the update with an aggregation pipeline, but maybe better understandable for team members that are not experienced with MongoDB.