文档菜单
文档首页
/
MongoDB 手册
/ / /

$graphLookup(聚合)

本页

  • 定义
  • 注意事项
  • 示例
  • 其他资源
$graphLookup

已更改在版本5.1.

对集合执行递归搜索,具有限制递归深度和查询过滤器的选项。

以下是对 $graphLookup 搜索过程的总结

  1. 输入文档流入聚合操作的 $graphLookup 阶段。

  2. $graphLookup 将搜索目标定位到由 from 参数指定的集合(请参阅下文中的搜索参数完整列表)。

  3. 对于每个输入文档,搜索从由 startWith 指定值开始。

  4. $graphLookupstartWith 值与 connectToField 指定的字段进行匹配,该字段位于 from 集合中的其他文档中。

  5. 对于每个匹配的文档,$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 操作指定的目标集合以递归匹配 connectFromFieldconnectToField。该 from 集合必须与操作中使用的其他集合位于同一数据库中。

从 MongoDB 5.1 开始,from 参数中指定的集合可以是分片的。

startWith
表达式,用于指定用于启动递归搜索的 connectFromField 的值。可选地,startWith 可以是值数组,每个值都单独通过遍历过程。
connectFromField
$graphLookup 使用的字段名,用于递归匹配其他文档集合中的 connectToField。如果值是数组,则每个元素都单独通过遍历过程。
connectToField
其他文档中的字段名,用于匹配由 connectFromField 参数指定的字段的值。
as

添加到每个输出文档的数组字段名称。包含通过 $graphLookup 阶段到达文档的文档。

as 字段中返回的文档顺序不保证。

maxDepth
可选。 非负整数,指定最大递归深度。
depthField
可选。 要添加到搜索路径中每个遍历文档的字段名称。此字段的值是文档的递归深度,表示为 NumberLong。递归深度值从零开始,因此第一次查找对应于零深度。
restrictSearchWithMatch

可选。 指定递归搜索的额外条件的文档。语法与 查询过滤器 语法相同。

在此过滤器中,您不能使用任何聚合表达式。例如,您不能使用以下文档来查找其中lastName值与输入文档中lastName值不同的文档

{ lastName: { $ne: "$lastName" } }

您不能在此上下文中使用此文档,因为"$lastName"将作为字符串字面量,而不是字段路径。

从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 集合中的 reportsToname 字段上匹配,返回每个人员的汇报层级

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" } 的遍历路径

起始值

文档的 reportsTo

{ ... reportsTo: "Ron" }
深度 0
{ _id: 3, name: "Ron", reportsTo: "Eliot" }
深度 1
{ _id: 2, name: "Eliot", reportsTo: "Dev" }
深度 2
{ _id: 1, name: "Dev" }

输出生成层级 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,其中起始 airportJFK

起始值

来自 travelers 集合的 nearestAirport

{ ... nearestAirport: "JFK" }
深度 0
{ _id: 0, airport: "JFK", connects: [ "BOS", "ORD" ] }
深度 1
{ _id: 1, airport: "BOS", connects: [ "JFK", "PWM" ] }
{ _id: 2, airport: "ORD", connects: [ "JFK" ] }
深度 2
{ _id: 3, airport: "PWM", connects: [ "BOS", "LHR" ] }

以下示例使用一个包含一组包含人名、他们的朋友列表和他们的爱好数组的文档的集合。一个聚合操作找到一个人,并遍历她的联系网络以找到列出 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"
]
}

网络研讨会:在 MongoDB 中处理图数据

返回

$geoNear