Główny problem polega na tym, że findOneAndUpdate
robi dokładnie to, co sugeruje jego nazwa. Wykonuje find
używając dostarczonego filtra, a jeśli zostanie znalezione dopasowanie, zastosuje aktualizacje do pierwszego pasującego dokumentu.
Jeśli kolekcja zawiera tylko ten dokument:
[
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_02",
"status": false
}
]
}
]
Początkowa część wyszukiwania to zasadniczo
.find({
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
})
To nie pasuje do niczego, a ponieważ upsert jest ustawione na true, fineOneAndUpdate próbuje utworzyć zupełnie nowy dokument. Nawet gdyby był w stanie utworzyć tablicę z niedopasowanego operatora pozycyjnego, dokument, który próbowałby dodać, byłby:
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_03",
"status": false
}
]
}
To nie jest poprawne i nie udałoby się wstawić z powodu zduplikowanego _id
wartość mimo wszystko.
Jeśli używasz MongoDB 4.2, możesz użyć potoku agregacji jako drugiego argumentu findAndUpdate
aby sprawdzić tablicę dla elementu, który Cię interesuje i dodać go, jeśli go brakuje.
Poniżej znajduje się jedna niezbyt ładna metoda. findOneAndUpdate dopasuje _id, a potok:
- sprawdzi, czy jakikolwiek element w tablicy pasuje do żądanego rok_miesiąc
- Jeśli tak, $redukuj tablicę, aby zaktualizować pole statusu w tym elemencie
- Jeśli nie, dołącz nowy element
- Przypisz wynik z powrotem do 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}]
]}
]}
}}]
)