Le principal problème est que findOneAndUpdate
fait exactement ce que son nom implique. Il exécute une find
à l'aide du filtre fourni, et si une correspondance est trouvée, applique les mises à jour au premier document correspondant.
Si la collection ne contient que ce document :
[
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_02",
"status": false
}
]
}
]
La partie de recherche initiale est essentiellement
.find({
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
})
Cela ne correspond à rien, et comme upsert est défini sur true, fineOneAndUpdate tente de créer un tout nouveau document. Même s'il était capable de créer un tableau à partir d'un opérateur positionnel sans correspondance, le document qu'il essaierait d'ajouter serait :
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_03",
"status": false
}
]
}
Ce n'est pas correct et l'insertion échouerait en raison d'un _id
en double valeur de toute façon.
Si vous utilisez MongoDB 4.2, vous pouvez utiliser un pipeline d'agrégation comme deuxième argument de findAndUpdate
pour vérifier le tableau de l'élément qui vous intéresse et l'ajouter s'il manque.
Une méthode pas très jolie est ci-dessous. Le findOneAndUpdate correspondra au _id, et le pipeline :
- vérifiera si un élément du tableau correspond à l'année_mois souhaitée
- Si c'est le cas, $réduire le tableau pour mettre à jour le champ d'état dans cet élément
/>- Si ce n'est pas le cas, ajoutez un nouvel élément
- Réattribuez le résultat aux 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}]
]}
]}
}}]
)