Không, thực sự không có giải pháp nào tốt hơn cho vấn đề này, vì vậy có lẽ cần có lời giải thích.
Giả sử bạn có một tài liệu có cấu trúc như bạn hiển thị:
{
"name": "foo",
"bars": [{
"name": "qux",
"somefield": 1
}]
}
Nếu bạn cập nhật như thế này
db.foo.update(
{ "name": "foo", "bars.name": "qux" },
{ "$set": { "bars.$.somefield": 2 } },
{ "upsert": true }
)
Sau đó, tất cả đều ổn vì tài liệu phù hợp đã được tìm thấy. Nhưng nếu bạn thay đổi giá trị của "bar.name":
db.foo.update(
{ "name": "foo", "bars.name": "xyz" },
{ "$set": { "bars.$.somefield": 2 } },
{ "upsert": true }
)
Sau đó, bạn sẽ nhận được một thất bại. Điều duy nhất thực sự thay đổi ở đây là trong MongoDB 2.6 trở lên, lỗi ngắn gọn hơn một chút:
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 16836,
"errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: bars.$.somefield"
}
})
Theo một số cách thì điều đó tốt hơn, nhưng dù sao thì bạn cũng không thực sự muốn "nâng cao". Những gì bạn muốn làm là thêm phần tử vào mảng mà "tên" hiện không tồn tại.
Vì vậy, những gì bạn thực sự muốn là "kết quả" từ nỗ lực cập nhật mà không có cờ "nâng cấp" để xem có tài liệu nào bị ảnh hưởng không:
db.foo.update(
{ "name": "foo", "bars.name": "xyz" },
{ "$set": { "bars.$.somefield": 2 } }
)
Nhường đáp lại:
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
Vì vậy, khi các tài liệu được sửa đổi là 0
thì bạn biết bạn muốn phát hành bản cập nhật sau:
db.foo.update(
{ "name": "foo" },
{ "$push": { "bars": {
"name": "xyz",
"somefield": 2
}}
)
Thực sự không có cách nào khác để làm chính xác những gì bạn muốn. Vì các phần bổ sung vào mảng không hoàn toàn là kiểu hoạt động "tập hợp", bạn không thể sử dụng $addToSet
kết hợp với chức năng "cập nhật hàng loạt" ở đó, để bạn có thể "phân tầng" các yêu cầu cập nhật của mình.
Trong trường hợp này, có vẻ như bạn cần kiểm tra kết quả hoặc chấp nhận đọc toàn bộ tài liệu và kiểm tra xem có nên cập nhật hoặc chèn một phần tử mảng mới vào mã hay không.