Det största problemet är att findOneAndUpdate
gör precis vad namnet antyder. Den kör en find
med det medföljande filtret, och om en matchning hittas, tillämpar uppdateringarna på det första matchande dokumentet.
Om samlingen endast innehåller detta dokument:
[
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_02",
"status": false
}
]
}
]
Den första sökdelen är i huvudsak
.find({
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
})
Detta matchar ingenting, och eftersom upsert är satt till sant, försöker fineOneAndUpdate skapa ett helt nytt dokument. Även om den skulle kunna skapa en array från en oöverträffad positionsoperator, skulle dokumentet som den skulle försöka lägga till vara:
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_03",
"status": false
}
]
}
Detta är inte korrekt och skulle misslyckas att infogas på grund av dubblett av _id
värde ändå.
Om du använder MongoDB 4.2 kan du använda en aggregeringspipeline som det andra argumentet för att findAndUpdate
för att kontrollera arrayen för det element du är intresserad av och lägga till det om det saknas.
En inte särskilt vacker metod är nedan. FindOneAndUpdate kommer att matcha _id, och pipeline kommer:
- kontrollera om något element i arrayen matchar önskad year_month
- Om så är fallet, $reducera arrayen för att uppdatera statusfältet i det elementet
- Om inte, lägg till ett nytt element
- Tilldela resultatet tillbaka till payments
.findOneAndUpdate(
{ "_id": "5e90ae0e0ed9974174e92826" },
[{$set: {
payments: {$cond:[
{$gt:[
{$size:
{$filter:{
input:"$payments",
cond:{$eq:["$$this.year_month","2020_03"]}
}}},
1
]},
{$reduce:{
input:"$payments",
initialValue:[],
in:{$concatArrays:[
"$$value",
[{$cond:[
{$eq:["$$this.j",3]},
{$mergeObjects:["$$this",{status:true}]},
"$$this"
]}]
]}
}},
{$concatArrays:[
"$payments",
[{year_month:"2020_03", status:true}]
]}
]}
}}]
)