On this page
$graphLookup (aggregation)
在本页面
在版本 3.4 中更改。
Definition
$graphLookup
- 对集合执行递归搜索,其中包含用于通过递归深度和查询过滤器限制搜索的选项。
$graphLookup
搜索过程总结如下:
Importing 文档进入聚合操作的
$graphLookup
阶段。$graphLookup
将搜索定位到from
参数指定的集合(有关搜索参数的完整列表,请参见下文)。对于每个 Importing 文档,搜索均以
startWith
指定的值开始。$graphLookup
与from
集合中其他文档中connectToField
指定的字段匹配startWith
值。对于每个匹配的文档,
$graphLookup
取connectFromField
的值,并在from
集合中的每个文档中检查匹配的connectToField
值。对于每个匹配项,$graphLookup
将from
集合中的匹配文档添加到由as
参数命名的数组字段中。
此步骤以递归方式 continue 进行,直到找不到更多匹配的文档,或者直到操作达到maxDepth
参数指定的递归深度为止。然后$graphLookup
将数组字段追加到 Importing 文档中。 $graphLookup
完成对所有 Importing 文档的搜索后返回结果。
$graphLookup具有以下原型形式:
{
$graphLookup: {
from: <collection>,
startWith: <expression>,
connectFromField: <string>,
connectToField: <string>,
as: <string>,
maxDepth: <number>,
depthField: <string>,
restrictSearchWithMatch: <document>
}
}
$graphLookup接收包含以下字段的文档:
Field | Description |
---|---|
from |
$graphLookup操作要搜索的目标集合,将connectFromField 与connectToField 递归匹配。不能对from 集合进行分片,并且必须与该操作中使用的任何其他集合位于同一数据库中。有关信息,请参见Sharded Collections。 |
startWith |
Expression指定用于开始递归搜索的connectFromField 的值。可选地,startWith 可以是值的数组,每个值都分别经过遍历过程。 |
connectFromField |
值$graphLookup用于与集合中其他文档的connectToField 递归匹配的字段名称。如果值是数组,则每个元素都将分别经过遍历过程。 |
connectToField |
其他文档中与connectFromField 参数指定的字段值相匹配的字段名。 |
as |
添加到每个输出文档中的数组字段的名称。包含在$graphLookup阶段遍历的文档。 |
Note
不能保证在as
字段中返回的文档以任何 Sequences 排列。
|
| maxDepth
| *可选.*非负整数,指定最大递归深度。
| depthField
| *可选.*要添加到搜索路径中每个已遍历文档的字段名称。该字段的值是文档的递归深度,表示为NumberLong。递归深度值从零开始,因此第一次查找对应于零深度。
| restrictSearchWithMatch
| *可选.*为递归搜索指定其他条件的文档。语法与query filter语法相同。
> [!NOTE|label:Note]
您不能在此过滤器中使用任何aggregation expression。例如,查询文档如{ lastName: { $ne: "$lastName" } }
在这种情况下将无法找到lastName
值与 Importing 文档的lastName
值不同的文档,因为"$lastName"
将充当字符串 Literals,而不是字段路径。
|
Considerations
Sharded Collections
from
中指定的集合不能为sharded。但是,可以对运行aggregate()方法的集合进行分片。也就是说,在下面:
db.collection.aggregate([
{ $graphLookup: { from: "fromCollection", ... } }
])
collection
可以被分片。fromCollection
无法分片。
要加入多个分片集合,请考虑:
Max Depth
将maxDepth
字段设置为0
等效于非递归$graphLookup搜索阶段。
Memory
$graphLookup阶段必须保持在 100 MiB 内存限制内。如果为aggregate()操作指定了allowDiskUse: true
,则$graphLookup阶段将忽略该选项。如果在aggregate()操作中还有其他观察allowDiskUse: true
的阶段,则allowDiskUse: true
选项对这些其他阶段有效。
有关更多信息,请参见聚集管线限制。
视图和排序规则
如果执行涉及多个视图的聚合(例如$lookup或$graphLookup),则这些视图必须具有相同的collation。
Examples
在一个集合内
名为employees
的集合具有以下文档:
{ "_id" : 1, "name" : "Dev" }
{ "_id" : 2, "name" : "Eliot", "reportsTo" : "Dev" }
{ "_id" : 3, "name" : "Ron", "reportsTo" : "Eliot" }
{ "_id" : 4, "name" : "Andrew", "reportsTo" : "Eliot" }
{ "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" }
{ "_id" : 6, "name" : "Dan", "reportsTo" : "Andrew" }
以下$graphLookup操作在employees
集合中的reportsTo
和name
字段上递归匹配,返回每个人的报告层次结构:
db.employees.aggregate( [
{
$graphLookup: {
from: "employees",
startWith: "$reportsTo",
connectFromField: "reportsTo",
connectToField: "name",
as: "reportingHierarchy"
}
}
] )
该操作返回以下内容:
{
"_id" : 1,
"name" : "Dev",
"reportingHierarchy" : [ ]
}
{
"_id" : 2,
"name" : "Eliot",
"reportsTo" : "Dev",
"reportingHierarchy" : [
{ "_id" : 1, "name" : "Dev" }
]
}
{
"_id" : 3,
"name" : "Ron",
"reportsTo" : "Eliot",
"reportingHierarchy" : [
{ "_id" : 1, "name" : "Dev" },
{ "_id" : 2, "name" : "Eliot", "reportsTo" : "Dev" }
]
}
{
"_id" : 4,
"name" : "Andrew",
"reportsTo" : "Eliot",
"reportingHierarchy" : [
{ "_id" : 1, "name" : "Dev" },
{ "_id" : 2, "name" : "Eliot", "reportsTo" : "Dev" }
]
}
{
"_id" : 5,
"name" : "Asya",
"reportsTo" : "Ron",
"reportingHierarchy" : [
{ "_id" : 1, "name" : "Dev" },
{ "_id" : 2, "name" : "Eliot", "reportsTo" : "Dev" },
{ "_id" : 3, "name" : "Ron", "reportsTo" : "Eliot" }
]
}
{
"_id" : 6,
"name" : "Dan",
"reportsTo" : "Andrew",
"reportingHierarchy" : [
{ "_id" : 1, "name" : "Dev" },
{ "_id" : 2, "name" : "Eliot", "reportsTo" : "Dev" },
{ "_id" : 4, "name" : "Andrew", "reportsTo" : "Eliot" }
]
}
下表提供了文档{ "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" }
的遍历路径:
起始值 | 文档的reportsTo 值:{ ... "reportsTo" : "Ron" } |
---|---|
Depth 0 | { "_id" : 3, "name" : "Ron", "reportsTo" : "Eliot" } |
Depth 1 | { "_id" : 2, "name" : "Eliot", "reportsTo" : "Dev" } |
Depth 2 | { "_id" : 1, "name" : "Dev" } |
输出生成层次结构Asya -> Ron -> Eliot -> Dev
。
跨多个收藏集
像$lookup一样,$graphLookup可以访问同一数据库中的另一个集合。
在以下示例中,数据库包含两个集合:
- 具有以下文档的集合
airports
:
{ "_id" : 0, "airport" : "JFK", "connects" : [ "BOS", "ORD" ] }
{ "_id" : 1, "airport" : "BOS", "connects" : [ "JFK", "PWM" ] }
{ "_id" : 2, "airport" : "ORD", "connects" : [ "JFK" ] }
{ "_id" : 3, "airport" : "PWM", "connects" : [ "BOS", "LHR" ] }
{ "_id" : 4, "airport" : "LHR", "connects" : [ "PWM" ] }
- 具有以下文档的集合
travelers
:
{ "_id" : 1, "name" : "Dev", "nearestAirport" : "JFK" }
{ "_id" : 2, "name" : "Eliot", "nearestAirport" : "JFK" }
{ "_id" : 3, "name" : "Jeff", "nearestAirport" : "BOS" }
对于travelers
集合中的每个文档,以下聚合操作将查找airports
集合中的nearestAirport
值,并将connects
字段与airport
字段递归匹配。该操作将最大递归深度指定为2
。
db.travelers.aggregate( [
{
$graphLookup: {
from: "airports",
startWith: "$nearestAirport",
connectFromField: "connects",
connectToField: "airport",
maxDepth: 2,
depthField: "numConnections",
as: "destinations"
}
}
] )
该操作返回以下结果:
{
"_id" : 1,
"name" : "Dev",
"nearestAirport" : "JFK",
"destinations" : [
{ "_id" : 3,
"airport" : "PWM",
"connects" : [ "BOS", "LHR" ],
"numConnections" : NumberLong(2) },
{ "_id" : 2,
"airport" : "ORD",
"connects" : [ "JFK" ],
"numConnections" : NumberLong(1) },
{ "_id" : 1,
"airport" : "BOS",
"connects" : [ "JFK", "PWM" ],
"numConnections" : NumberLong(1) },
{ "_id" : 0,
"airport" : "JFK",
"connects" : [ "BOS", "ORD" ],
"numConnections" : NumberLong(0) }
]
}
{
"_id" : 2,
"name" : "Eliot",
"nearestAirport" : "JFK",
"destinations" : [
{ "_id" : 3,
"airport" : "PWM",
"connects" : [ "BOS", "LHR" ],
"numConnections" : NumberLong(2) },
{ "_id" : 2,
"airport" : "ORD",
"connects" : [ "JFK" ],
"numConnections" : NumberLong(1) },
{ "_id" : 1,
"airport" : "BOS",
"connects" : [ "JFK", "PWM" ],
"numConnections" : NumberLong(1) },
{ "_id" : 0,
"airport" : "JFK",
"connects" : [ "BOS", "ORD" ],
"numConnections" : NumberLong(0) } ]
}
{
"_id" : 3,
"name" : "Jeff",
"nearestAirport" : "BOS",
"destinations" : [
{ "_id" : 2,
"airport" : "ORD",
"connects" : [ "JFK" ],
"numConnections" : NumberLong(2) },
{ "_id" : 3,
"airport" : "PWM",
"connects" : [ "BOS", "LHR" ],
"numConnections" : NumberLong(1) },
{ "_id" : 4,
"airport" : "LHR",
"connects" : [ "PWM" ],
"numConnections" : NumberLong(2) },
{ "_id" : 0,
"airport" : "JFK",
"connects" : [ "BOS", "ORD" ],
"numConnections" : NumberLong(1) },
{ "_id" : 1,
"airport" : "BOS",
"connects" : [ "JFK", "PWM" ],
"numConnections" : NumberLong(0) }
]
}
下表提供了递归搜索的遍历路径,深度为2
,起始airport
为JFK
:
起始值 | 来自travelers 集合的nearestAirport 值:{ ... "nearestAirport" : "JFK" } |
---|---|
Depth 0 | { "_id" : 0, "airport" : "JFK", "connects" : [ "BOS", "ORD" ] } |
Depth 1 | { "_id" : 1, "airport" : "BOS", "connects" : [ "JFK", "PWM"] } { "_id" : 2, "airport" : "ORD", "connects" : [ "JFK"] } |
Depth 2 | { "_id" : 3, "airport" : "PWM", "connects" : [ "BOS", "LHR" ] } |
使用查询过滤器
下面的示例使用一个集合,该集合包含一组文档,其中包含人的名字以及他们的朋友和爱好的数组。聚集操作找到一个特定的人,并遍历她的联系网络,以找到在他们的兴趣中列出golf
的人。
名为people
的集合包含以下文档:
{
"_id" : 1,
"name" : "Tanya Jordan",
"friends" : [ "Shirley Soto", "Terry Hawkins", "Carole Hale" ],
"hobbies" : [ "tennis", "unicycling", "golf" ]
}
{
"_id" : 2,
"name" : "Carole Hale",
"friends" : [ "Joseph Dennis", "Tanya Jordan", "Terry Hawkins" ],
"hobbies" : [ "archery", "golf", "woodworking" ]
}
{
"_id" : 3,
"name" : "Terry Hawkins",
"friends" : [ "Tanya Jordan", "Carole Hale", "Angelo Ward" ],
"hobbies" : [ "knitting", "frisbee" ]
}
{
"_id" : 4,
"name" : "Joseph Dennis",
"friends" : [ "Angelo Ward", "Carole Hale" ],
"hobbies" : [ "tennis", "golf", "topiary" ]
}
{
"_id" : 5,
"name" : "Angelo Ward",
"friends" : [ "Terry Hawkins", "Shirley Soto", "Joseph Dennis" ],
"hobbies" : [ "travel", "ceramics", "golf" ]
}
{
"_id" : 6,
"name" : "Shirley Soto",
"friends" : [ "Angelo Ward", "Tanya Jordan", "Carole Hale" ],
"hobbies" : [ "frisbee", "set theory" ]
}
以下聚合操作分为三个阶段:
$match与包含字符串
"Tanya Jordan"
的name
字段的文档匹配。返回一个输出文档。$graphLookup将输出文档的
friends
字段与集合中其他文档的name
字段连接,以遍历Tanya Jordan's
连接网络。此阶段使用restrictSearchWithMatch
参数仅查找hobbies
数组包含golf
的文档。返回一个输出文档。$project对输出文档进行整形。
connections who play golf
中列出的名称取自 Importing 文档golfers
数组中列出的文档的name
字段。
db.people.aggregate( [
{ $match: { "name": "Tanya Jordan" } },
{ $graphLookup: {
from: "people",
startWith: "$friends",
connectFromField: "friends",
connectToField: "name",
as: "golfers",
restrictSearchWithMatch: { "hobbies" : "golf" }
}
},
{ $project: {
"name": 1,
"friends": 1,
"connections who play golf": "$golfers.name"
}
}
] )
该操作返回以下文档:
{
"_id" : 1,
"name" : "Tanya Jordan",
"friends" : [
"Shirley Soto",
"Terry Hawkins",
"Carole Hale"
],
"connections who play golf" : [
"Joseph Dennis",
"Tanya Jordan",
"Angelo Ward",
"Carole Hale"
]
}