$graphLookup(聚合)
定义
$graphLookup
已更改在版本5.1.
对集合执行递归搜索,具有限制递归深度和查询过滤器的选项。
以下是对
$graphLookup
搜索过程的总结输入文档流入聚合操作的
$graphLookup
阶段。$graphLookup
将搜索目标定位到由from
参数指定的集合(请参阅下文中的搜索参数完整列表)。对于每个输入文档,搜索从由
startWith
指定值开始。$graphLookup
将startWith
值与connectToField
指定的字段进行匹配,该字段位于from
集合中的其他文档中。对于每个匹配的文档,
$graphLookup
会取connectFromField
的值,并在from
集合中检查每个文档的connectToField
值。对于每个匹配项,$graphLookup
会将from
集合中匹配的文档添加到由as
参数命名的数组字段中。此步骤会递归进行,直到找不到更多匹配的文档,或者直到操作达到由
maxDepth
参数指定的递归深度。然后,$graphLookup
将数组字段附加到输入文档上。在完成对所有输入文档的搜索后,$graphLookup
返回结果。
$graphLookup
具有以下原型形式{ $graphLookup: { from: <collection>, startWith: <expression>, connectFromField: <string>, connectToField: <string>, as: <string>, maxDepth: <number>, depthField: <string>, restrictSearchWithMatch: <document> } } $graphLookup
接收一个包含以下字段的文档字段描述from
为
$graphLookup
操作指定的目标集合以递归匹配connectFromField
与connectToField
。该from
集合必须与操作中使用的其他集合位于同一数据库中。从 MongoDB 5.1 开始,
from
参数中指定的集合可以是分片的。startWith
connectFromField
$graphLookup
使用的字段名,用于递归匹配其他文档集合中的connectToField
。如果值是数组,则每个元素都单独通过遍历过程。connectToField
其他文档中的字段名,用于匹配由connectFromField
参数指定的字段的值。as
添加到每个输出文档的数组字段名称。包含通过
$graphLookup
阶段到达文档的文档。as
字段中返回的文档顺序不保证。maxDepth
可选。 非负整数,指定最大递归深度。depthField
可选。 要添加到搜索路径中每个遍历文档的字段名称。此字段的值是文档的递归深度,表示为NumberLong
。递归深度值从零开始,因此第一次查找对应于零深度。restrictSearchWithMatch
考虑因素
分片集合
从MongoDB 5.1版本开始,您可以在分片集合的from
参数中指定$graphLookup
阶段。
在针对分片集合时,您不能在事务中使用$graphLookup
阶段。
最大深度
将maxDepth
字段设置为0
等同于非递归的$graphLookup
搜索阶段。
内存
$graphLookup
阶段必须在100兆字节的内存限制内。如果为aggregate()
操作指定了allowDiskUse: true
,则$graphLookup
阶段会忽略该选项。如果aggregate()
操作中还有其他阶段,则这些其他阶段将生效allowDiskUse: true
选项。
有关更多信息,请参阅聚合管道限制。
未排序的结果
$graphLookup
阶段不会返回排序后的结果。为了排序结果,请使用$sortArray
运算符。
视图和排序
如果执行涉及多个视图的聚合操作,例如使用 $lookup
或 $graphLookup
,这些视图必须具有相同的 排序规则。
示例
单个集合内
名为 employees
的集合有以下文档
db.employees.insertMany( [ { _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: 2, name: "Eliot", reportsTo: "Dev" }, { _id: 1, name: "Dev" } ] } { _id: 4, name: "Andrew", reportsTo: "Eliot", reportingHierarchy: [ { _id: 2, name: "Eliot", reportsTo: "Dev" }, { _id: 1, name: "Dev" } ] } { _id: 5, name: "Asya", reportsTo: "Ron", reportingHierarchy: [ { _id: 2, name: "Eliot", reportsTo: "Dev" }, { _id: 3, name: "Ron", reportsTo: "Eliot" }, { _id: 1, name: "Dev" } ] } { "_id" : 6, "name" : "Dan", "reportsTo" : "Andrew", "reportingHierarchy" : [ { _id: 4, name: "Andrew", reportsTo: "Eliot" }, { _id: 2, name: "Eliot", reportsTo: "Dev" }, { _id: 1, name: "Dev" } ] }
以下表格提供了文档 { "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" }
的遍历路径
起始值 | 文档的
| |
深度 0 |
| |
深度 1 |
| |
深度 2 |
|
输出生成层级 Asya -> Ron -> Eliot -> Dev
。
跨多个集合
类似于 $lookup
,$graphLookup
可以访问同一数据库中的另一个集合。
例如,创建一个包含两个集合的数据库
一个名为
airports
的集合,包含以下文档db.airports.insertMany( [ { _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
的集合,包含以下文档db.travelers.insertMany( [ { _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
起始值 | 来自
| ||
深度 0 |
| ||
深度 1 |
| ||
深度 2 |
|
带有查询过滤器
以下示例使用一个包含一组包含人名、他们的朋友列表和他们的爱好数组的文档的集合。一个聚合操作找到一个人,并遍历她的联系网络以找到列出 golf
作为他们爱好的其他人。
名为 people
的集合包含以下文档
db.people.insertMany( [ { _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
的联系网络。此阶段使用restrictSearchWithMatch
参数仅查找其中hobbies
数组包含golf
的文档。返回一个输出文档。$project
操作用于塑造输出文档。在connections who play golf
中列出的名称来自输入文档中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" ] }