Câu hỏi ở đây thực sự là về một cái gì đó khác và không cần $lookup
ở tất cả. Nhưng đối với bất kỳ ai đến đây chỉ với tiêu đề "lọc sau khi tìm kiếm $" thì đây là những kỹ thuật dành cho bạn:
MongoDB 3.6 - Đường dẫn con
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"let": { "id": "$id" },
"pipeline": [
{ "$match": {
"value": "1",
"$expr": { "$in": [ "$$id", "$contain" ] }
}}
],
"as": "childs"
}}
])
Trước đó - $ lookup + $ unwind + $ match hợp lại
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$unwind": "$childs" },
{ "$match": { "childs.value": "1" } },
{ "$group": {
"_id": "$_id",
"id": { "$first": "$id" },
"value": { "$first": "$value" },
"contain": { "$first": "$contain" },
"childs": { "$push": "$childs" }
}}
])
Nếu bạn thắc mắc tại sao bạn sẽ $unwind
trái ngược với việc sử dụng $filter
trên mảng, sau đó đọc Aggregate $ lookup.
Đối với các bản phát hành của MongoDB 3.6 trở đi, thì "đường ống con" biểu cảm hơn thường là thứ bạn muốn "lọc" kết quả của tập hợp nước ngoài trước khi bất kỳ thứ gì được trả lại vào mảng.
Quay lại câu trả lời mặc dù thực sự mô tả lý do tại sao câu hỏi được hỏi cần "không tham gia" nào cả ....
Bản gốc
Sử dụng $lookup
như đây không phải là cách "hiệu quả" nhất để làm những gì bạn muốn ở đây. Nhưng hãy nói thêm về điều này sau.
Như một khái niệm cơ bản, chỉ cần sử dụng $filter
trên mảng kết quả:
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$project": {
"id": 1,
"value": 1,
"contain": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$eq": [ "$$child.value", "1" ] }
}
}
}}
]);
Hoặc sử dụng $redact
thay vào đó:
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$redact": {
"$cond": {
"if": {
"$or": [
{ "$eq": [ "$value", "0" ] },
{ "$eq": [ "$value", "1" ] }
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
]);
Cả hai đều nhận được cùng một kết quả:
{
"_id" : ObjectId("570557d4094a4514fc1291d6"),
"id" : 100,
"value" : "0",
"contain" : [ ],
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [
100
]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [
100
]
}
]
}
Điểm mấu chốt là $lookup
bản thân nó không thể truy vấn "chưa" để chỉ chọn một số dữ liệu nhất định. Vì vậy, tất cả "quá trình lọc" cần phải diễn ra sau khi $lookup
Nhưng thực sự đối với kiểu "tự tham gia" này, bạn không nên sử dụng $lookup
và tránh được toàn bộ chi phí đọc bổ sung và "hợp nhất băm". Chỉ cần tìm nạp các mục liên quan và $group
thay vào đó:
db.test.aggregate([
{ "$match": {
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}},
{ "$group": {
"_id": {
"$cond": {
"if": { "$eq": [ "$value", "0" ] },
"then": "$id",
"else": { "$arrayElemAt": [ "$contain", 0 ] }
}
},
"value": { "$first": { "$literal": "0"} },
"childs": {
"$push": {
"$cond": {
"if": { "$ne": [ "$value", "0" ] },
"then": "$$ROOT",
"else": null
}
}
}
}},
{ "$project": {
"value": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$ne": [ "$$child", null ] }
}
}
}}
])
Điều này chỉ xuất hiện một chút khác biệt vì tôi đã cố tình loại bỏ các trường không liên quan. Hãy thêm chúng vào chính bạn nếu bạn thực sự muốn:
{
"_id" : 100,
"value" : "0",
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [ 100 ]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [ 100 ]
}
]
}
Vì vậy, vấn đề thực sự duy nhất ở đây là "lọc" bất kỳ null
nào kết quả từ mảng, được tạo khi tài liệu hiện tại là parent
trong việc xử lý các mục thành $push
.
Điều mà bạn dường như còn thiếu ở đây là kết quả bạn đang tìm kiếm không cần tổng hợp hay "truy vấn phụ" nào cả. Cấu trúc mà bạn đã kết luận hoặc có thể tìm thấy ở nơi khác được "thiết kế" để bạn có thể nhận được một "nút" và tất cả các "nút con" của nó trong một yêu cầu truy vấn duy nhất.
Điều đó có nghĩa là chỉ cần "truy vấn" là tất cả những gì thực sự cần thiết và việc thu thập dữ liệu (là tất cả những gì đang xảy ra vì không có nội dung nào thực sự bị "giảm") chỉ là một chức năng lặp lại kết quả con trỏ:
var result = {};
db.test.find({
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}).sort({ "contain.0": 1 }).forEach(function(doc) {
if ( doc.id == 100 ) {
result = doc;
result.childs = []
} else {
result.childs.push(doc)
}
})
printjson(result);
Điều này thực hiện chính xác cùng một điều:
{
"_id" : ObjectId("570557d4094a4514fc1291d6"),
"id" : 100,
"value" : "0",
"contain" : [ ],
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [
100
]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [
100
]
}
]
}
Và đóng vai trò là bằng chứng cho thấy tất cả những gì bạn thực sự cần làm ở đây là đưa ra truy vấn "duy nhất" để chọn cả cha và con. Dữ liệu trả về giống nhau và tất cả những gì bạn đang làm trên máy chủ hoặc máy khách là "chuyển đổi" sang một định dạng được thu thập khác.
Đây là một trong những trường hợp mà bạn có thể bị "cuốn vào" suy nghĩ về cách bạn đã làm mọi thứ trong cơ sở dữ liệu "quan hệ" và không nhận ra rằng vì cách dữ liệu được lưu trữ đã "thay đổi", bạn không cần sử dụng nữa. cùng một cách tiếp cận.
Đó chính xác là điểm của ví dụ tài liệu "Cấu trúc cây mô hình có tham chiếu con" trong cấu trúc của nó, nơi nó giúp dễ dàng chọn cha mẹ và con cái trong một truy vấn.