我正在努力寻找特定孩子的祖先。在某些时候,那个孩子的祖先姓氏改变了。我想找到这个具有给定姓氏的孩子的最后一个父母。例如:
{
"_id":1,
"parent":null
"first":"Bob",
"last":"Sagget"
},
{
"_id":2,
"parent":1,
"first":"Jane",
"last":"Dor"
},
{
"_id":3,
"parent":2,
"first":"Crane",
"last":"Dor"
},
{
"_id":4,
"parent":3,
"first":"Ho",
"last":"Dor"
},
{
"_id":5,
"parent":4,
"first":"Mor",
"last":"Dor"
}
我想查询_id
5 并获取姓氏为 Dor 的祖先。在这个数据库中会有其他名为Dor的人,我不想看到,所以我不能只查询“Dor”的姓氏。
这是我当前的聚合查询 - 这是给我每个祖先一直到_id
的1.我怎么能停在_id
的2?:
db.PeopleDb.aggregate(
[
{
$graphLookup: {
"from": "PeopleDb",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "_id",
"as": "linearAncestors"
}
},
{
$match: {
"_id":5
}
},
]);
答案 0 :(得分:2)
您正在寻找的基本查询实际上是#34;启动"限制仅将单数文档与_id: 5
匹配,然后执行$graphLookup
以查找祖先。
关于"条件",涉及一些步骤,因此最好遍历整个过程并了解发生的事情:
db.PeopleDb.aggregate([
{ "$match": { "_id": 5 } },
{ "$graphLookup": {
"from": "PeopleDb",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "_id",
"as": "people",
"depthField": "depth"
}}
])
然后返回将parent
连接到_id
的所有递归链,并注意"可选" "depthField"
设置为包含实际的"深度"返回结果中的匹配项:
{
"_id" : 5,
"parent" : 4,
"first" : "Mor",
"last" : "Dor",
"people" : [
{
"_id" : 1,
"parent" : null,
"first" : "Bob",
"last" : "Sagget",
"depth" : NumberLong(3)
},
{
"_id" : 2,
"parent" : 1,
"first" : "Jane",
"last" : "Dor",
"depth" : NumberLong(2)
},
{
"_id" : 3,
"parent" : 2,
"first" : "Crane",
"last" : "Dor",
"depth" : NumberLong(1)
},
{
"_id" : 4,
"parent" : 3,
"first" : "Ho",
"last" : "Dor",
"depth" : NumberLong(0)
}
]
}
因此,请注意4
已返回,因为它是初始文档的父级,然后3
为其父级,然后是2
,因为它是父级等等。
您可以限制"深度"使用"可选"进行匹配"maxDepth"
参数指向管道阶段,或者在"特定情况下除了ROOT"您可以使用"restrictSearchWithMatch"
选项简单地使用null
父级排除结果:
db.PeopleDb.aggregate([
{ "$match": { "_id": 5 } },
{ "$graphLookup": {
"from": "PeopleDb",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "_id",
"as": "people",
"depthField": "depth",
"restrictSearchWithMatch": { "parent": { "$ne": null } }
}}
])
返回相同的结果,但不包括" ROOT"记录"parent"
字段为null
的位置:
{
"_id" : 5,
"parent" : 4,
"first" : "Mor",
"last" : "Dor",
"people" : [
{
"_id" : 2,
"parent" : 1,
"first" : "Jane",
"last" : "Dor",
"depth" : NumberLong(2)
},
{
"_id" : 3,
"parent" : 2,
"first" : "Crane",
"last" : "Dor",
"depth" : NumberLong(1)
},
{
"_id" : 4,
"parent" : 3,
"first" : "Ho",
"last" : "Dor",
"depth" : NumberLong(0)
}
]
}
当然,同样的原则适用于您的"last"
条件,该条件只能匹配条件为真的文档。在这里,我将显示两个条件,但$or
是可选的:
db.PeopleDb.aggregate([
{ "$match": { "_id": 5 } },
{ "$graphLookup": {
"from": "PeopleDb",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "_id",
"as": "people",
"depthField": "depth",
"restrictSearchWithMatch": {
"$or": [
{ "parent": { "$ne": null }},
{ "last": "Dor" }
]
}
}}
])
注意然而"restrictSearchWithMatch"
是"递归"条件,因此,如果有任何"祖先"在链中不符合"last"
条件,则链已断开,并且不会检索到更多祖先。为了得到所有的祖先"但只显示匹配"last"
然后您$filter
生成的数组内容的人:
db.PeopleDb.aggregate([
{ "$match": { "_id": 5 } },
{ "$graphLookup": {
"from": "PeopleDb",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "_id",
"as": "people",
"depthField": "depth",
}},
{ "$addFields": {
"people": {
"$filter": {
"input": "$people",
"cond": { "$eq": [ "$$this.last", "Dor" ] }
}
}
}}
])
或实际上"动态"通过比较"祖先"的初始匹配文档的值。与使用字段值表达式而不是硬编码值相关:
db.PeopleDb.aggregate([
{ "$match": { "_id": 5 } },
{ "$graphLookup": {
"from": "PeopleDb",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "_id",
"as": "people",
"depthField": "depth",
}},
{ "$addFields": {
"people": {
"$filter": {
"input": "$people",
"cond": { "$eq": [ "$$this.last", "$last" ] }
}
}
}}
])
在这种情况下,它是相同的2,3,4
祖先,但如果说祖先3
实际上具有"last"
的不同值,则使用$filter
实际上会返回2,4
,而"restrictSearchWithMatch"
只返回4
,因为3
会打破链#34;这是主要区别。
N.B 使用
$graphLookup
你不能做的一件事是"字段比较表达"不被允许。如果你想要沿着这些方向做某些事情,那么你可以使用$filter
进行进一步的操作,如果你的实际意图确实是为了打破链条,那么可能会进行$indexOfArray
等其他操作。在那个"字段比较"在递归搜索中没有得到满足。