3 回答

TA貢獻(xiàn)1946條經(jīng)驗(yàn) 獲得超4個(gè)贊
這里的問(wèn)題實(shí)際上是關(guān)于一些不同的東西,根本不需要$lookup。但是,對(duì)于僅從“ $ lookup之后過(guò)濾”標(biāo)題到達(dá)此處的任何人,這些都是適合您的技術(shù):
MongoDB 3.6-子管道
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"let": { "id": "$id" },
"pipeline": [
{ "$match": {
"value": "1",
"$expr": { "$in": [ "$$id", "$contain" ] }
}}
],
"as": "childs"
}}
])
較早-$ lookup + $ unwind + $ match合并
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" }
}}
])
如果您質(zhì)疑為什么不$unwind使用$filter該數(shù)組,請(qǐng)閱讀Aggregate $ lookup匹配管道中文檔的總大小超出了所有文檔的最大文檔大小,以了解為什么這是通常必需的并且是最佳方法。
對(duì)于MongoDB 3.6及更高版本,通常要在將所有內(nèi)容都返回到數(shù)組之前“過(guò)濾”外部集合的結(jié)果,來(lái)表達(dá)更具表現(xiàn)力的“子管道”。
回到答案,盡管實(shí)際上描述了為什么所提問(wèn)題根本不需要“加入”。
原版的
$lookup像這樣使用并不是在這里執(zhí)行所需操作的最“有效”方法。但是稍后會(huì)詳細(xì)介紹。
作為一個(gè)基本概念,只需$filter在結(jié)果數(shù)組上使用:
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" ] }
}
}
}}
]);
或$redact改為使用:
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"
}
}}
]);
兩者都得到相同的結(jié)果:
{
"_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 ]
}
]
}
最重要的是,$lookup它本身不能“還”查詢(xún)以?xún)H選擇某些數(shù)據(jù)。因此,所有“過(guò)濾”操作都需要在$lookup
但是,實(shí)際上對(duì)于這種類(lèi)型的“自我連接”,您最好根本不使用它$lookup,并且完全避免額外讀取和“哈希合并”的開(kāi)銷(xiāo)。只需獲取相關(guān)項(xiàng)目,即可$group:
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 ] }
}
}
}}
])
由于我有意刪除了多余的字段,所以結(jié)果僅稍有不同。如果您確實(shí)要添加它們,請(qǐng)自己添加:
{
"_id" : 100,
"value" : "0",
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [ 100 ]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [ 100 ]
}
]
}
因此,這里唯一真正的問(wèn)題是“過(guò)濾” null數(shù)組中的任何結(jié)果,該數(shù)組是在當(dāng)前文檔正在parent處理中時(shí)創(chuàng)建的$push。
您在這里似乎還缺少的是,您要查找的結(jié)果根本不需要聚合或“子查詢(xún)”。您已經(jīng)結(jié)束或可能在其他地方找到的結(jié)構(gòu)是“設(shè)計(jì)的”,以便您可以在單個(gè)查詢(xún)請(qǐng)求中獲得“節(jié)點(diǎn)”及其所有“子代”。
這意味著只有“ query”才是真正需要的,而數(shù)據(jù)收集(由于沒(méi)有真正“減少”任何內(nèi)容而已)的全部工作只是迭代游標(biāo)結(jié)果的功能:
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);
這做的完全一樣:
{
"_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
]
}
]
}
并證明您在這里真正需要做的就是發(fā)出“單個(gè)”查詢(xún)以選擇父項(xiàng)和子項(xiàng)。返回的數(shù)據(jù)是相同的,并且您在服務(wù)器或客戶(hù)端上所做的所有工作都在“按摩”為另一種收集的格式。
這是在這種情況下您可以“思考”如何在“關(guān)系”數(shù)據(jù)庫(kù)中做事的情況之一,而沒(méi)有意識(shí)到由于數(shù)據(jù)的存儲(chǔ)方式已“改變”,因此您不再需要使用同樣的方法。
這正是文檔示例“帶有子引用的模型樹(shù)結(jié)構(gòu)”結(jié)構(gòu)中的要點(diǎn),在該示例中可以輕松地在一個(gè)查詢(xún)中選擇父級(jí)和子級(jí)。

TA貢獻(xiàn)1757條經(jīng)驗(yàn) 獲得超7個(gè)贊
就“性能”而言$lookup
,實(shí)際上是在“服務(wù)器”上進(jìn)行“兩個(gè)”查詢(xún)?!翱蛻?hù)端”可以執(zhí)行“兩個(gè)”查詢(xún),但是當(dāng)然會(huì)涉及網(wǎng)絡(luò)和響應(yīng)開(kāi)銷(xiāo),這將減慢該過(guò)程。因此,為什么要這么做$lookup
。但是我也是說(shuō)您的案子不需要這個(gè)。所有項(xiàng)目已經(jīng)在同一個(gè)集合中,因此只需簡(jiǎn)單地選擇它們并$group
相應(yīng)地選擇它們即可。就“性能”而言,到目前為止,這是三個(gè)中更好的選擇。

TA貢獻(xiàn)1852條經(jīng)驗(yàn) 獲得超7個(gè)贊
我認(rèn)為您仍然停留在“關(guān)系思考”中,這使您在實(shí)際上不需要“子查詢(xún)”或“自我加入”的情況下也不需要。在此處添加了純“無(wú)聚合”方法來(lái)說(shuō)明這一點(diǎn)。
添加回答
舉報(bào)