Hovedproblemet er, at findOneAndUpdate
gør præcis, hvad navnet antyder. Den udfører en find
ved at bruge det medfølgende filter, og hvis et match er fundet, anvender opdateringerne til det første matchende dokument.
Hvis samlingen kun indeholder dette dokument:
[
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_02",
"status": false
}
]
}
]
Den indledende find-del er i det væsentlige
.find({
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
})
Dette matcher intet, og da upsert er sat til sand, forsøger fineOneAndUpdate at oprette et helt nyt dokument. Selv hvis det var i stand til at oprette et array fra en uovertruffen positionsoperator, ville dokumentet, det ville forsøge at tilføje, være:
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_03",
"status": false
}
]
}
Dette er ikke korrekt og vil ikke kunne indsættes på grund af dublet _id
værdi alligevel.
Hvis du bruger MongoDB 4.2, kan du bruge en aggregeringspipeline som det andet argument til findAndUpdate
for at kontrollere arrayet for det element, du er interesseret i, og tilføje det, hvis det mangler.
En ikke særlig smuk metode er nedenfor. FindOneAndUpdate vil matche _id'et, og pipelinen vil:
- kontrollere, om et element i arrayet matcher den ønskede year_month
- Hvis ja, $reducere arrayet for at opdatere statusfeltet i det element
- Hvis ikke, tilføj et nyt element
- Tildel resultatet tilbage til 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}]
]}
]}
}}]
)