Về cơ bản, bạn muốn có $elemMatch
và $exists
toán tử, vì điều này sẽ kiểm tra từng phần tử để xem điều kiện "trường không tồn tại" có đúng với bất kỳ phần tử nào không:
Model.find({
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
},function(err,docs) {
});
Điều đó chỉ trả về tài liệu thứ hai vì trường không có trong một trong các tài liệu con của mảng:
{
"id" : 2,
"line_items" : [
{
"id" : 1,
"review_request_sent" : false
},
{
"id" : 39
}
]
}
Lưu ý rằng biểu mẫu này "khác" với biểu mẫu này:
Model.find({
"line_items.review_request_sent": { "$exists": false }
},function(err,docs) {
})
Trường hợp mà bạn đang hỏi thì "tất cả" các phần tử mảng không chứa trường này, điều này không đúng khi tài liệu có ít nhất một phần tử có trường này. Vì vậy, $eleMatch
làm cho điều kiện được kiểm tra dựa trên "từng" phần tử mảng và do đó bạn nhận được phản hồi chính xác.
Nếu bạn muốn cập nhật dữ liệu này để bất kỳ phần tử mảng nào được tìm thấy không chứa trường này sẽ nhận trường đó với giá trị false
(có lẽ), sau đó bạn thậm chí có thể viết một câu lệnh như sau:
Model.aggregate(
[
{ "$match": {
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
}},
{ "$project": {
"line_items": {
"$setDifference": [
{"$map": {
"input": "$line_items",
"as": "item",
"in": {
"$cond": [
{ "$eq": [
{ "$ifNull": [ "$$item.review_request_sent", null ] },
null
]},
"$$item.id",
false
]
}
}},
[false]
]
}
}}
],
function(err,docs) {
if (err) throw err;
async.each(
docs,
function(doc,callback) {
async.each(
doc.line_items,
function(item,callback) {
Model.update(
{ "_id": doc._id, "line_items.id": item },
{ "$set": { "line_items.$.review_request_sent": false } },
callback
);
},
callback
);
},
function(err) {
if (err) throw err;
// done
}
);
}
);
Trong đó .aggregate()
kết quả không chỉ khớp với các tài liệu mà còn lọc ra nội dung khỏi mảng mà trường không có mặt, để chỉ trả về "id" của tài liệu con cụ thể đó.
Sau đó, .update()
được lặp lại các câu lệnh khớp với từng phần tử mảng được tìm thấy trong mỗi tài liệu và thêm trường bị thiếu với một giá trị ở vị trí đã khớp.
Bằng cách đó, bạn sẽ có trường hiện diện trong tất cả các tài liệu con của mọi tài liệu mà trước đó nó bị thiếu.
Nếu bạn muốn làm điều đó, thì cũng nên thay đổi lược đồ của bạn để đảm bảo rằng trường cũng luôn ở đó:
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent: { type: Boolean, default: false }
}],
total_price: String,
name: String,
order_number: Number
}
Vì vậy, lần sau khi bạn thêm các mục mới vào mảng trong mã của mình, phần tử sẽ luôn tồn tại với giá trị mặc định của nó nếu không được đặt rõ ràng. Và có lẽ bạn nên làm như vậy, cũng như đặt required
trên các trường khác mà bạn luôn muốn, chẳng hạn như "id".