Основният проблем е, че findOneAndUpdate
прави точно това, което подсказва името му. Той изпълнява find
използва предоставения филтър и ако бъде намерено съвпадение, прилага актуализациите към първия съответстващ документ.
Ако колекцията съдържа само този документ:
[
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_02",
"status": false
}
]
}
]
Първоначалната част за намиране е по същество
.find({
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
})
Това не съвпада с нищо и тъй като upsert е зададено на true, fineOneAndUpdate се опитва да създаде чисто нов документ. Дори и да може да създаде масив от несъвпадащ позиционен оператор, документът, който ще се опитва да добави, ще бъде:
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_03",
"status": false
}
]
}
Това не е правилно и няма да може да се вмъкне поради дублиран _id
стойност така или иначе.
Ако използвате MongoDB 4.2, можете да използвате тръбопровод за агрегиране като втори аргумент за findAndUpdate
за да проверите масива за елемента, който ви интересува, и да го добавите, ако липсва.
Един не много красив метод е по-долу. FindOneAndUpdate ще съвпадне с _id и тръбопроводът ще:
- провери дали някой елемент в масива съвпада с желания year_month
- Ако е така, $редуцирайте масива, за да актуализирате полето за състояние в този елемент
- Ако не, добавете нов елемент
- Присвоете резултата обратно към 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}]
]}
]}
}}]
)