MongoDB 3.6 và mới hơn
Với MongoDB 3.6 trở lên có một tính năng mới cho phép bạn cập nhật các mảng lồng nhau bằng cách sử dụng $\[<identifier>\]
được lọc theo vị trí cú pháp để khớp với các phần tử cụ thể và áp dụng các điều kiện khác nhau thông qua arrayFilters
trong tuyên bố cập nhật:
const { oid, pid } = req.params;
const { name, oName, description, type } = req.body;
collection.update(
{
"_id": 1,
"operations": {
"$elemMatch": {
oid, "parameters.pid": pid
}
}
},
{ "$set": {
"operations.$[outer].parameters.$[inner].name": name,
"operations.$[outer].parameters.$[inner].description": description,
"operations.$[outer].parameters.$[inner].oName": oName,
"operations.$[outer].parameters.$[inner].type": type
} },
{ "arrayFilters": [
{ "outer.oid": oid },
{ "inner.pid": pid }
] }, (err, result) => {
if (err) {
console.log('Error updating service: ' + err);
res.send({'error':'An error has occurred'});
} else {
// console.log('' + result + ' document(s) updated');
res.send(result);
}
});
Đối với MongoDB 3.4 trở lên:
Như @wdberkeley đã đề cập trong bình luận của anh ấy:
MongoDB không hỗ trợ đối sánh thành nhiều cấp của một mảng. Hãy xem xét việc thay đổi mô hình tài liệu của bạn để mỗi tài liệu đại diện cho quá trình tối ưu hóa, với thông tin chung cho một nhóm hoạt động được sao chép trong tài liệu hoạt động.
Tôi đồng tình với điều trên và khuyên bạn nên thiết kế lại giản đồ của mình vì công cụ MongoDB không hỗ trợ nhiều toán tử vị trí (Xem Sử dụng nhiều vị trí $
toán tử để cập nhật các mảng lồng nhau )
Tuy nhiên, nếu bạn biết trước chỉ mục của mảng hoạt động có đối tượng tham số được cập nhật thì truy vấn cập nhật sẽ là:
db.collection.update(
{
"_id" : "04",
"operations.parameters.pid": "011"
},
{
"$set": {
"operations.0.parameters.$.name": "foo",
"operations.0.parameters.$.description": "bar",
"operations.0.parameters.$.type": "foo"
}
}
)
CHỈNH SỬA:
Nếu bạn muốn tạo $set
điều kiện đang diễn ra tức là thứ gì đó sẽ giúp bạn lấy chỉ mục cho các đối tượng và sau đó sửa đổi cho phù hợp, sau đó xem xét sử dụng MapReduce .
Hiện tại, điều này dường như không thể thực hiện được bằng cách sử dụng khung tổng hợp. Có một sự cố JIRA đang mở chưa được giải quyết liên kết với nó. Tuy nhiên, có thể có một giải pháp khác với MapReduce . Ý tưởng cơ bản với MapReduce là nó sử dụng JavaScript làm ngôn ngữ truy vấn nhưng điều này có xu hướng khá chậm hơn so với khung tổng hợp và không nên được sử dụng để phân tích dữ liệu thời gian thực.
Trong thao tác MapReduce của mình, bạn cần xác định một vài bước, tức là bước ánh xạ (ánh xạ một thao tác vào mọi tài liệu trong bộ sưu tập và thao tác này có thể không làm gì hoặc tạo ra một số đối tượng có khóa và giá trị được chiếu) và giảm bước ( lấy danh sách các giá trị đã phát ra và giảm nó thành một phần tử).
Đối với bước bản đồ, lý tưởng nhất là bạn muốn nhận được cho mọi tài liệu trong bộ sưu tập, chỉ mục cho mỗi operations
trường mảng và một khóa khác có chứa $set
chìa khóa.
Bước giảm của bạn sẽ là một hàm (không có chức năng gì) được định nghĩa đơn giản là var reduce = function() {};
Bước cuối cùng trong hoạt động MapReduce của bạn sau đó sẽ tạo một hoạt động thu thập riêng biệt có chứa đối tượng mảng hoạt động được phát ra cùng với một trường có $set
điều kiện. Bộ sưu tập này có thể được cập nhật định kỳ khi bạn chạy thao tác MapReduce trên bộ sưu tập ban đầu. Nhìn chung, phương thức MapReduce này sẽ giống như sau:
var map = function(){
for(var i = 0; i < this.operations.length; i++){
emit(
{
"_id": this._id,
"index": i
},
{
"index": i,
"operations": this.operations[i],
"update": {
"name": "operations." + i.toString() + ".parameters.$.name",
"description": "operations." + i.toString() + ".parameters.$.description",
"type": "operations." + i.toString() + ".parameters.$.type"
}
}
);
}
};
var reduce = function(){};
db.collection.mapReduce(
map,
reduce,
{
"out": {
"replace": "operations"
}
}
);
Truy vấn các hoạt động operations
từ hoạt động MapReduce thường sẽ cho bạn kết quả:
db.operations.findOne()
Đầu ra :
{
"_id" : {
"_id" : "03",
"index" : 0
},
"value" : {
"index" : 0,
"operations" : {
"_id" : "96",
"oName" : "test op 52222222222",
"sid" : "04",
"name" : "test op 52222222222",
"oid" : "99",
"description" : "testing",
"returntype" : "test",
"parameters" : [
{
"oName" : "Param1",
"name" : "foo",
"pid" : "011",
"type" : "foo",
"description" : "bar",
"value" : ""
},
{
"oName" : "Param2",
"name" : "Param2",
"pid" : "012",
"type" : "58222",
"description" : "testing",
"value" : ""
}
]
},
"update" : {
"name" : "operations.0.parameters.$.name",
"description" : "operations.0.parameters.$.description",
"type" : "operations.0.parameters.$.type"
}
}
}
Sau đó, bạn có thể sử dụng con trỏ từ db.operations.find()
để lặp lại và cập nhật bộ sưu tập của bạn cho phù hợp:
var oid = req.params.operations;
var pid = req.params.parameters;
var cur = db.operations.find({"_id._id": oid, "value.operations.parameters.pid": pid });
// Iterate through results and update using the update query object set dynamically by using the array-index syntax.
while (cur.hasNext()) {
var doc = cur.next();
var update = { "$set": {} };
// set the update query object
update["$set"][doc.value.update.name] = req.body.name;
update["$set"][doc.value.update.description] = req.body.description;
update["$set"][doc.value.update.type] = req.body.type;
db.collection.update(
{
"_id" : oid,
"operations.parameters.pid": pid
},
update
);
};