Nếu bạn đã từng làm việc với MongoDB trước đây thì rất có thể bạn đã lưu trữ các tài liệu phụ trong mảng, đây là một trong nhiều lý do khiến MongoDB tỏa sáng hơn các cơ sở dữ liệu truyền thống khác
cách tiếp cận gật gù
Tuy nhiên, nếu bạn có một tài liệu mà bạn muốn chèn một tài liệu con vào một mảng khi nó không tồn tại, hoặc cập nhật toàn bộ tài liệu trong mảng khi nó tồn tại thì rất có thể bạn sẽ kéo mục đó ra khỏi mảng
// Insert a test item document
db.items.insertOne[{
"_id" : ObjectId["623ded6e1ce9aa98b37ce86a"],
"items": [
{ "_id": 1, "content": "aaa"},
{ "_id": 2, "content": "bbb"},
{ "_id": 3, "content": "ccc"},
]
}]
// Pull the array item which we want to update
db.items.updateOne[
{ "_id" : ObjectId["623ded6e1ce9aa98b37ce86a"]},
{
"$pull": {
"items": { "_id": 2 }
}
}
]
// Push the updated item back in to the array [we're updating content to "zzz"]
db.items.updateOne[
{ "_id" : ObjectId["623ded6e1ce9aa98b37ce86a"]},
{
"$push": {
"items": {
"_id": 2, "content": "zzz"
}
}
}
]
// Check final document
db.items.find[]
[
{
_id: ObjectId["623ded6e1ce9aa98b37ce86a"],
items: [
{ _id: 1, content: 'aaa' },
{ _id: 3, content: 'ccc' },
{ _id: 2, content: 'zzz' }
]
}
]
Điều này khá ồn ào và nếu ai đó tìm nạp tài liệu trong khi cập nhật nửa chừng thì họ cũng sẽ nhận được chế độ xem tài liệu không nhất quán với mục có id là 2 bị thiếu. Tuy nhiên, chúng tôi có thể khắc phục sự cố với sự không nhất quán bằng cách bắt đầu giao dịch và hoàn thành 2 thao tác cùng nhau
giao dịch
Đoạn mã dưới đây cho thấy 2 hoạt động được bao bọc trong một giao dịch
// Start a session
var session = db.getMongo[].startSession[ { readPreference: { mode: "primary" } } ];
// Start a transaction
session.startTransaction[ { readConcern: { level: "local" }, writeConcern: { w: "majority" } } ];
try {
var items = session.getDatabase["test"].items;
// Pull the array item which we want to update
items.updateOne[
{ "_id" : ObjectId["623ded6e1ce9aa98b37ce86a"]},
{
"$pull": {
"items": { "_id": 2 }
}
}
]
// Push the updated item back in to the array [we're updating content to "zzz"]
items.updateOne[
{ "_id" : ObjectId["623ded6e1ce9aa98b37ce86a"]},
{
"$push": {
"items": {
"_id": 2, "content": "zzz"
}
}
}
]
// Commit the transaction using write concern set at transaction start
session.commitTransaction[];
} catch [error] {
// Abort transaction on error
session.abortTransaction[];
throw error;
}
session.endSession[];
Đây là một cách tuyệt vời để thực hiện nhiều thao tác trên một tài liệu, tuy nhiên, nó yêu cầu phải có MongoDB 4. 0 đang chạy trong bộ bản sao hoặc MongoDB 4. 2 nếu dữ liệu của bạn được phân vùng trên nhiều phân đoạn
Cập nhật đường ống tổng hợp
Một cách khác là sử dụng thao tác cập nhật với đường dẫn tổng hợp [Điều này cũng yêu cầu MongoDB v4. 2], điều này cho phép một thao tác cập nhật duy nhất mà không cần bất kỳ giao dịch nào
Bản cập nhật bên dưới với quy trình tổng hợp rất giống với thao tác trên, chúng tôi đang sử dụng giai đoạn tổng hợp $addFields
để tiếp tục thay thế trên đầu trường myItems
db.items.updateOne[
{ "_id" : ObjectId["6176d58d636041dbac68233c"]},
[
{
$addFields: {
"myItems": {
$filter: {
input: "$myItems",
as: "item",
cond: { $ne: [
"$$item.id",
2
]
}
}
}
}
},
{
$addFields: {
"myItems": { $concatArrays: [ "$myItems",
[
{
"id": 2,
"extraProps": true,
"content": "Hello World"
}
]
]
}
}
}
]
]
Giai đoạn $addFields
đầu tiên xóa tài liệu khỏi mảng dựa trên một điều kiện nhất định bằng cách sử dụng toán tử $filter
và sau đó giai đoạn $addFields
thứ hai sau đó nối thêm tài liệu mới bằng cách sử dụng toán tử $concatArrays
Cách tốt nhất?
Giống như hầu hết mọi thứ trong quá trình phát triển phần mềm, không có cách nào tốt nhất để giải quyết vấn đề, quy trình tổng hợp cập nhật có thể trở nên phức tạp nhưng ít trò chuyện hơn và không yêu cầu giao dịch. Tuy nhiên, cách tiếp cận giao dịch có thể làm cho mã của bạn dễ hiểu hơn, nhưng sau đó có phí giao dịch và cũng yêu cầu một bộ bản sao để phát triển cục bộ