Vấn đề chính là findOneAndUpdate
thực hiện chính xác những gì tên của nó ngụ ý. Nó thực thi một find
bằng cách sử dụng bộ lọc được cung cấp và nếu tìm thấy kết quả phù hợp, hãy áp dụng các bản cập nhật cho tài liệu phù hợp đầu tiên.
Nếu bộ sưu tập chỉ chứa tài liệu này:
[
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_02",
"status": false
}
]
}
]
Phần tìm kiếm ban đầu về cơ bản là
.find({
_id: '5e90ae0e0ed9974174e92826',
payments: { $elemMatch: { year_month: '2020_03' }}
})
Điều này không khớp với nhau và vì upert được đặt thành true, fineOneAndUpdate cố gắng tạo một tài liệu hoàn toàn mới. Ngay cả khi nó có thể tạo một mảng từ một toán tử vị trí không khớp, tài liệu mà nó đang cố gắng thêm vào sẽ là:
{
"_id": "5e90ae0e0ed9974174e92826",
"payments": [
{
"year_month": "2020_03",
"status": false
}
]
}
Điều này không chính xác và sẽ không thể chèn do _id
trùng lặp vẫn có giá trị.
Nếu bạn đang sử dụng MongoDB 4.2, bạn có thể sử dụng đường dẫn tổng hợp làm đối số thứ hai cho findAndUpdate
để kiểm tra mảng cho phần tử bạn quan tâm và thêm nó nếu nó bị thiếu.
Dưới đây là một phương pháp không đẹp lắm. FindOneAndUpdate sẽ khớp với _id và đường dẫn sẽ:
- kiểm tra xem có phần tử nào trong mảng khớp với năm_tháng mong muốn không
- Nếu vậy, $ giảm mảng để cập nhật trường trạng thái trong phần tử đó
- Nếu không, hãy thêm một phần tử mới
- Gán lại kết quả cho 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}]
]}
]}
}}]
)