use*_*001 6 mongodb mongodb-query aggregation-framework
我试图找到给定孩子的祖先。在某个时候,那个孩子的祖先姓氏改变了。我想用给定的姓氏找到这个孩子的最后一个父母。例如:
{
"_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"
}
Run Code Online (Sandbox Code Playgroud)
我想查询_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
}
},
]);
Run Code Online (Sandbox Code Playgroud)
Nei*_*unn 12
您正在寻找的基本查询实际上是“开始”,限制只匹配单数文档,_id: 5
然后执行$graphLookup
以查找祖先。
至于“条件”,涉及一些步骤,因此最好走一遍整个过程并了解发生了什么:
db.PeopleDb.aggregate([
{ "$match": { "_id": 5 } },
{ "$graphLookup": {
"from": "PeopleDb",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "_id",
"as": "people",
"depthField": "depth"
}}
])
Run Code Online (Sandbox Code Playgroud)
然后返回将 连接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)
}
]
}
Run Code Online (Sandbox Code Playgroud)
因此请注意,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 } }
}}
])
Run Code Online (Sandbox Code Playgroud)
返回相同的结果,但不包括"parent"
字段为的“ROOT”文档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)
}
]
}
Run Code Online (Sandbox Code Playgroud)
当然,同样的原则适用于您的"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" }
]
}
}}
])
Run Code Online (Sandbox Code Playgroud)
但是请注意,这"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" ] }
}
}
}}
])
Run Code Online (Sandbox Code Playgroud)
或者实际上“动态”通过使用字段值表达式而不是硬编码值与“祖先”相关的初始匹配文档的值进行比较:
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" ] }
}
}
}}
])
Run Code Online (Sandbox Code Playgroud)
在这种情况下,它是相同的2,3,4
祖先,但如果说祖先3
实际上具有不同的值,"last"
则使用$filter
实际返回2,4
而"restrictSearchWithMatch"
只会返回,4
因为它3
会“破坏链条”。这是主要的区别。
注意,您还不能使用的一件事
$graphLookup
是不允许使用“字段比较表达式”。如果您想要这些方面的东西,那么您将使用$filter
其他操作和可能的其他操作进行进一步的操作,例如,$indexOfArray
如果您的实际意图确实是在递归搜索中未满足“字段比较”的点上“打破链”。