Il problema principale è che findOneAndUpdate
fa esattamente quello che suggerisce il suo nome. Esegue un find
utilizzando il filtro fornito e, se viene trovata una corrispondenza, applica gli aggiornamenti al primo documento corrispondente.
Se la collezione contiene solo questo documento:
[
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_02",
"status": false
}
]
}
]
La parte di ricerca iniziale è essenzialmente
.find({
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
})
Non corrisponde a nulla e poiché upsert è impostato su true, fineOneAndUpdate tenta di creare un documento nuovo di zecca. Anche se fosse in grado di creare un array da un operatore posizionale senza corrispondenza, il documento che proverebbe ad aggiungere sarebbe:
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_03",
"status": false
}
]
}
Questo non è corretto e non verrà inserito a causa di _id
duplicati valore comunque.
Se stai usando MongoDB 4.2, puoi usare una pipeline di aggregazione come secondo argomento per findAndUpdate
per controllare l'array per l'elemento che ti interessa e aggiungerlo se manca.
Un metodo non molto carino è sotto. findOneAndUpdate corrisponderà a _id e la pipeline:
- verificherà se qualsiasi elemento nell'array corrisponde all'anno_mese desiderato
- In tal caso, $riduci l'array per aggiornare il campo di stato in quell'elemento
- In caso contrario, aggiungi un nuovo elemento
- Riassegna il risultato a 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}]
]}
]}
}}]
)