Điều này thực sự không đơn giản như bạn nghĩ, và thực sự thú vị là bạn đã chia phân tích của mình về vấn đề này thành ba phần. Bởi vì, hãy đoán xem? Đó chính xác là những gì bạn phải làm. Hãy xem xét các bước:
1. Chèn tài liệu nếu tài liệu không tồn tại
db.collection.update(
{
"clientId":"123456"
},
{
"$setOnInsert": {
"clientId": "123456",
"devices": [{
"deviceId": "321",
"deviceType" : "kindle",
"notification" : false
}]
}
},
{ "upsert": true }
)
Vì vậy, những gì bạn muốn làm là chèn một tài liệu mới mà "clientId" hiện không tồn tại. Điều này có thể được thực hiện như một "nâng cấp" để tránh xung đột khóa duy nhất có thể xảy ra và ngay cả khi không có ràng buộc "duy nhất", thì bản chất "nâng cấp" của điều này đảm bảo bạn chỉ tạo tài liệu "mới" khi nó không được tìm thấy. Ngoài ra còn có $setOnInsert
ở đây vì bạn không muốn thực hiện bất kỳ điều gì đối với tài liệu được "tìm thấy" tại thời điểm này.
Lưu ý rằng ở đây có không cố gắng so khớp phần tử trong mảng. Điều này là do bạn có thể không muốn "tạo" một tài liệu mới chỉ vì tài liệu hiện có không có phần tử mảng "that". Điều này đưa chúng ta đến bước tiếp theo.
2. Cập nhật nội dung của tài liệu khi nó tồn tại
db.collection.update(
{
"clientId":"123456",
"devices": { "$elemMatch": { "deviceId" : "321" } }
},
{
"$set": {
"devices.$.deviceType" : "kindle",
"devices.$.notification" : false
}
}
)
Bây giờ ở đây, bạn muốn thực sự thử và "khớp" tài liệu cho "clientId" hiện chứa một phần tử trong mảng cũng khớp với "deviceId" mà bạn đang tìm kiếm. Vì vậy, bằng cách chỉ định một điều kiện để đối sánh, bạn có thể sử dụng $
vị trí để đặt các trường ở vị trí "khớp".
Như trên, điều này sẽ khớp với một điều hoặc không có gì vì vậy hoặc cập nhật đã được thực hiện hoặc nó không. Vì vậy, điều đó sẽ chuyển đến phần cuối cùng của thác nước ở đây:
3. Thêm phần tử mảng mà nó không tồn tại
db.collection.update(
{
"clientId":"123456"
},
{
"$addToset": { "devices": {
"deviceId" : "321",
"deviceType" : "kindle",
"notification" : false
}}
}
)
Vì vậy, đây là quan trọng giai đoạn cuối cùng. Lý do là nếu một trong các hoạt động trước đó đã "tạo" hoặc "cập nhật" tài liệu hiện có, sau đó sử dụng $addToSet
ở đây chắc chắn rằng chắc chắn bạn không "đẩy" tài liệu khác vào mảng có cùng "deviceId" nhưng khác giá trị. Nếu một trong số các giai đoạn đó hoạt động, sau đó điều này sẽ thấy tất cả các giá trị của phần tử đó đã tồn tại và sau đó sẽ không thêm một phần tử khác.
Nếu bạn đã cố gắng làm điều đó theo một thứ tự khác, trong trường hợp bạn trình bày, bạn sẽ có hai tài liệu trong mảng có cùng "deviceId", nhưng khác giá trị cho "deviceType" và "thông báo". Vì vậy, đó là lý do tại sao nó đến sau cùng.
Kết luận
Vì vậy, rất tiếc, không có cách đơn giản nào để kết hợp chúng thành một hoạt động. Các toán tử chỉ đơn giản là không tồn tại để điều này có thể được thực hiện trong một câu lệnh duy nhất và do đó bạn phải thực hiện ba cập nhật các hoạt động để làm những gì bạn muốn. Cũng như đã nêu, đơn đặt hàng ứng dụng cho các bản cập nhật đó quan trọng để bạn có được kết quả mong muốn.
Mặc dù điều này chưa tồn tại trong các bản phát hành "sản xuất" hiện tại, nhưng bản phát hành sắp tới (từ 2.6 trở lên tính đến thời điểm viết bài) có một cách để "xử lý hàng loạt" các yêu cầu này bằng một cú pháp mới để cập nhật:
db.runCommand(
"update": "collection",
"updates": [
{
"q": { "clientId":"123456" },
"u": {
"$setOnInsert": {
"clientId": "123456",
"devices": [{
"deviceId": "321",
"deviceType" : "kindle",
"notification" : false
}]
},
"upsert": true
},
{
"q": {
"clientId":"123456",
"devices": { "$elemMatch": { "deviceId" : "321" } }
},
"u": {
"$set": {
"devices.$.deviceType" : "kindle",
"devices.$.notification" : false
}
}
},
{
"q": { "clientId":"123456" },
"u": {
"$addToset": { "devices": {
"deviceId" : "321",
"deviceType" : "kindle",
"notification" : false
}}
}
}
]
)
Vì vậy, trong khi đó vẫn còn về cơ bản là ba thao tác, ít nhất bạn có thể gửi chúng qua dây chỉ một lần