Database Manual / Reference / Query Language / Aggregation Stages

$graphLookup (aggregation stage)(聚合阶段)

Definition定义

$graphLookup

Changed in version 5.1.在版本5.1中的更改。

Performs a recursive search on a collection, with options for restricting the search by recursion depth and query filter.对集合执行递归搜索,并提供按递归深度和查询筛选器限制搜索的选项。

The $graphLookup search process is summarized below:$graphLookup搜索过程总结如下:

  1. Input documents flow into the $graphLookup stage of an aggregation operation.输入文档流入聚合操作的$graphLookup阶段。
  2. $graphLookup targets the search to the collection designated by the from parameter (see below for full list of search parameters).将搜索定向到from参数指定的集合(有关搜索参数的完整列表,请参阅下文)。
  3. For each input document, the search begins with the value designated by startWith.对于每个输入文档,搜索从startWith指定的值开始。
  4. $graphLookup matches the startWith value against the field designated by connectToField in other documents in the from collection.startWith值与from集合中其他文档中connectToField指定的字段进行匹配。
  5. For each matching document, $graphLookup takes the value of the connectFromField and checks every document in the from collection for a matching connectToField value. 对于每个匹配的文档,$graphLookup获取connectFromField的值,并检查from集合中的每个文档是否有匹配的connectToField值。For each match, $graphLookup adds the matching document in the from collection to an array field named by the as parameter.对于每个匹配,$graphLookupfrom集合中的匹配文档添加到由as参数命名的数组字段中。

    This step continues recursively until no more matching documents are found, or until the operation reaches a recursion depth specified by the maxDepth parameter. 此步骤递归进行,直到找不到更多匹配的文档,或者直到操作达到maxDepth参数指定的递归深度。$graphLookup then appends the array field to the input document. 然后将数组字段附加到输入文档中。$graphLookup returns results after completing its search on all input documents.在完成对所有输入文档的搜索后返回结果。

$graphLookup has the following prototype form:具有以下原型形式:

{
$graphLookup: {
from: <collection>,
startWith: <expression>,
connectFromField: <string>,
connectToField: <string>,
as: <string>,
maxDepth: <number>,
depthField: <string>,
restrictSearchWithMatch: <document>
}
}

$graphLookup takes a document with the following fields:获取具有以下字段的文档:

Field字段Description描述
from

Target collection for the $graphLookup operation to search, recursively matching the connectFromField to the connectToField. The from collection must be in the same database as any other collections used in the operation.$graphLookup操作要搜索的目标集合,递归匹配connectFromFieldconnectToFieldfrom集合必须与操作中使用的任何其他集合位于同一数据库中。

Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded.从MongoDB 5.1开始,from参数中指定的集合可以被分片。

startWithExpression that specifies the value of the connectFromField with which to start the recursive search. 指定用于开始递归搜索的connectFromField值的表达式If startWith evaluates to an array, $graphLookup performs the search simultaneously from all array elements.如果startWith计算结果为数组,则$graphLookup会同时从所有数组元素中执行搜索。
connectFromFieldField name whose value $graphLookup uses to recursively match against the connectToField of other documents in the collection. 字段名,其值$graphLookup用于递归匹配集合中其他文档的connectToFieldIf the value is an array, each element is individually followed through the traversal process.如果该值是一个数组,则遍历过程中每个元素都会被单独跟踪。
connectToFieldField name in other documents against which to match the value of the field specified by the connectFromField parameter.connectFromField参数指定的字段值匹配的其他文档中的字段名称。
as

Name of the array field added to each output document. Contains the documents traversed in the $graphLookup stage to reach the document.添加到每个输出文档的数组字段的名称。包含在$graphLookup阶段遍历以访问文档的文档。

Documents returned in the as field are not guaranteed to be in any order.as字段中返回的文件不保证按任何顺序排列。

maxDepthOptional.可选的。 Non-negative integral number specifying the maximum recursion depth.指定最大递归深度的非负整数。
depthFieldOptional.可选的。 Name of the field to add to each traversed document in the search path. 要添加到搜索路径中每个遍历文档的字段名称。The value of this field is the recursion depth for the document, represented as a NumberLong. 此字段的值是文档的递归深度,表示为NumberLongRecursion depth value starts at zero, so the first lookup corresponds to zero depth.递归深度值从零开始,因此第一次查找对应于零深度。
restrictSearchWithMatch

Optional.可选的。 A document specifying additional conditions for the recursive search. The syntax is identical to query filter syntax.为递归搜索指定附加条件的文档。语法与查询筛选器语法相同。

You cannot use any aggregation expression in this filter. For example, you can't use the following document to find documents in which the lastName value is different from the lastName value of the input document:您不能在此筛选器中使用任何聚合表达式。例如,您不能使用以下文档来查找lastName值与输入文档的lastName值不同的文档:

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

You can't use the document in this context, because "$lastName" will act as a string literal, not a field path.您不能在此上下文中使用该文档,因为"$lastName"将充当字符串文字,而不是字段路径。

Considerations注意事项

Sharded Collections分片化集合

Starting in MongoDB 5.1, you can specify sharded collections in the from parameter of $graphLookup stages.从MongoDB 5.1开始,您可以在$graphLookup阶段的from参数中指定分片集合

You cannot use the $graphLookup stage within a transaction while targeting a sharded collection.在针对分片集合时,您不能在事务中使用$graphLookup阶段。

Max Depth最大深度

Setting the maxDepth field to 0 is equivalent to a non-recursive $graphLookup search stage.maxDepth字段设置为0相当于非递归的$graphLookup搜索阶段。

Memory内存

If the $graphLookup stage consumes more than 100 megabytes of memory, it automatically writes temporary files to disk. You can see when $graphLookup uses disk through the serverStatus command and view an explanation of $graphLookup disk usage through the explain() command in executionStats verbosity mode.如果$graphLookup阶段消耗的内存超过100兆字节,它会自动将临时文件写入磁盘。您可以通过serverStatus命令查看$graphLookup何时使用磁盘,并在executionStats详细模式下通过explain()命令查看$graphLookup磁盘使用情况的说明。

If the $graphLookup stage exceeds 100 megabytes of memory and the allowDiskUse option is set to false, $graphLookup returns an error.如果$graphLookup阶段超过100兆字节的内存,并且allowDiskUse选项设置为false,则$graphLookup将返回错误。

See aggregration pipeline limitations for more information.有关更多信息,请参阅聚合管道限制

Unsorted Results未排序的结果

The $graphLookup stage does not return sorted results. To sort your results, use the $sortArray operator.$graphLookup阶段不返回排序结果。要对结果进行排序,请使用$sortArray运算符。

Views and Collation视图和排序

If performing an aggregation that involves multiple views, such as with $lookup or $graphLookup, the views must have the same collation.如果执行涉及多个视图的聚合,例如使用$lookup$graphLookup,则这些视图必须具有相同的排序规则

Examples示例

MongoDB Shell

Within a Single Collection在单一集合中

A collection named employees has the following documents:名为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" }
] )

The following $graphLookup operation recursively matches on the reportsTo and name fields in the employees collection, returning the reporting hierarchy for each person:以下$graphLookup操作递归匹配employees集合中的reportsToname字段,返回每个人的报告层次结构:

db.employees.aggregate( [
{
$graphLookup: {
from: "employees",
startWith: "$reportsTo",
connectFromField: "reportsTo",
connectToField: "name",
as: "reportingHierarchy"
}
}
] )

The output resembles the following results:输出类似于以下结果:

{
_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" }
]
}

The following table provides a traversal path for the document { "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" }:下表提供了文档{ "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" }的遍历路径:

Start value起始值

The reportsTo value of the document:

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

The output generates the hierarchy Asya -> Ron -> Eliot -> Dev.输出生成层次结构Asya -> Ron -> Eliot -> Dev

Across Multiple Collections跨越多个集合

Like $lookup, $graphLookup can access another collection in the same database.$lookup一样,$graphLookup可以访问同一数据库中的另一个集合。

For example, create a database with two collections:例如,创建一个包含两个集合的数据库:

  • An airports collection with the following documents: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" ] }
    ] )
  • A travelers collection with the following documents:包含以下文件的travelers集合:

    db.travelers.insertMany( [
    { _id: 1, name: "Dev", nearestAirport: "JFK" },
    { _id: 2, name: "Eliot", nearestAirport: "JFK" },
    { _id: 3, name: "Jeff", nearestAirport: "BOS" }
    ] )

For each document in the travelers collection, the following aggregation operation looks up the nearestAirport value in the airports collection and recursively matches the connects field to the airport field. The operation specifies a maximum recursion depth of 2.对于travelers集合中的每个文档,以下聚合操作会查找airports集合中nearestAirport(最近的机场)值,并递归地将connects字段与airport字段进行匹配。该操作指定最大递归深度为2

db.travelers.aggregate( [
{
$graphLookup: {
from: "airports",
startWith: "$nearestAirport",
connectFromField: "connects",
connectToField: "airport",
maxDepth: 2,
depthField: "numConnections",
as: "destinations"
}
}
] )

The output resembles the following results:输出类似于以下结果:

{
_id: 1,
name: "Dev",
nearestAirport: "JFK",
destinations: [
{ _id: 3,
airport: "PWM",
connects: [ "BOS", "LHR" ],
numConnections: Long(2) },
{ _id: 2,
airport: "ORD",
connects: [ "JFK" ],
numConnections: Long(1) },
{ _id: 1,
airport: "BOS",
connects: [ "JFK", "PWM" ],
numConnections: Long(1) },
{ _id: 0,
airport: "JFK",
connects: [ "BOS", "ORD" ],
numConnections: Long(0) }
]
}
{
_id: 2,
name: "Eliot",
nearestAirport: "JFK",
destinations: [
{ _id: 3,
airport: "PWM",
connects: [ "BOS", "LHR" ],
numConnections: Long(2) },
{ _id: 2,
airport: "ORD",
connects: [ "JFK" ],
numConnections: Long(1) },
{ _id: 1,
airport: "BOS",
connects: [ "JFK", "PWM" ],
numConnections: Long(1) },
{ _id: 0,
airport: "JFK",
connects: [ "BOS", "ORD" ],
numConnections: Long(0) } ]
}
{
"_id" : 3,
name: "Jeff",
nearestAirport: "BOS",
destinations: [
{ _id: 2,
airport: "ORD",
connects: [ "JFK" ],
numConnections: Long(2) },
{ _id: 3,
airport: "PWM",
connects: [ "BOS", "LHR" ],
numConnections: Long(1) },
{ _id: 4,
airport: "LHR",
connects: [ "PWM" ],
numConnections: Long(2) },
{ _id:: 0,
airport: "JFK",
connects: [ "BOS", "ORD" ],
numConnections: Long(1) },
{ _id:: 1,
airport: "BOS",
connects: [ "JFK", "PWM" ],
numConnections: Long(0) }
]
}

The following table provides a traversal path for the recursive search, up to depth 2, where the starting airport is JFK:下表提供了递归搜索的遍历路径,深度为2,其中起始airport(机场)为JFK(肯尼迪机场):

Start value起始值

The nearestAirport value from the travelers collection: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" ] }

With a Query Filter使用查询筛选器

The following example uses a collection with a set of documents containing names of people along with arrays of their friends and their hobbies. An aggregation operation finds one particular person and traverses her network of connections to find people who list golf among their hobbies.以下示例使用了一个包含一组文档的集合,其中包含人名、朋友数组和爱好。聚合操作找到一个特定的人,并遍历她的连接网络,以找到将高尔夫列为爱好的人。

A collection named people contains the following documents:名为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" ]
}
] )

The following aggregation operation uses three stages:以下聚合操作使用三个阶段:

  • $match matches on documents with a name field containing the string "Tanya Jordan". Returns one output document.匹配name字段包含字符串"Tanya Jordan"的文档。返回一个输出文档。
  • $graphLookup connects the output document's friends field with the name field of other documents in the collection to traverse Tanya Jordan's network of connections. 将输出文档的friends字段与集合中其他文档的name字段连接起来,以遍历Tanya Jordan's的连接网络。This stage uses the restrictSearchWithMatch parameter to find only documents in which the hobbies array contains golf. Returns one output document.此阶段使用restrictSearchWithMatch参数仅查找hobbies(爱好)数组中包含golf的文档。返回一个输出文档。
  • $project shapes the output document. The names listed in connections who play golf are taken from the name field of the documents listed in the input document's golfers array.塑造输出文档的形状。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"
}
}
] )

The operation returns the following document:该操作返回以下文档:

{
_id: 1,
name: "Tanya Jordan",
friends: [
"Shirley Soto",
"Terry Hawkins",
"Carole Hale"
],
'connections who play golf': [
"Joseph Dennis",
"Tanya Jordan",
"Angelo Ward",
"Carole Hale"
]
}
C#

A collection named employees has the following documents:名为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" }

The following Employee class models documents in the employees collection:employees集合中的以下Employee类模型文档:

public class Employee
{
public ObjectId Id { get; set; }

public string Name { get; set; }

public Employee ReportsTo { get; set; }

public List<Employee> ReportingHierarchy { get; set; }

public List<string> Hobbies { get; set; }
}

To use the MongoDB .NET/C# driver to add a $graphLookup stage to an aggregation pipeline, call the GraphLookup() method on a PipelineDefinition object.要使用MongoDB NET/C#驱动程序将$graphLookup阶段添加到聚合管道中,请在PipelineDefinition对象上调用GraphLookup()方法。

The following example creates a pipeline stage that recursively matches on the ReportsTo and Name fields in the employees collection, returning the reporting hierarchy for each person:以下示例创建了一个管道阶段,该阶段递归匹配employees集合中的ReportsToName字段,返回每个人的报告层次结构:

var pipeline = new EmptyPipelineDefinition<Employee>()
.GraphLookup<Employee, Employee, Employee, Employee, string, Employee, List<Employee>, Employee>(
from: employeeCollection,
connectFromField: e => e.ReportsTo,
connectToField: e => e.Name,
startWith: e => e.ReportsTo,
@as: e => e.ReportingHierarchy);

You can use an AggregateGraphLookupOptions object to specify the depth to recurse and name of the depth field. 您可以使用AggregateGraphLookupOptions对象指定要递归的深度和深度字段的名称。The following code example performs the same $graphLookup operation as the previous example, but specifies a maximum recursion depth of 1:以下代码示例执行与前一个示例相同的$graphLookup操作,但指定的最大递归深度为1

var employeeCollection = client.GetDatabase("aggregation_examples").GetCollection<Employee>("employees");

var pipeline = new EmptyPipelineDefinition<Employee>()
.GraphLookup<Employee, Employee, Employee, Employee, string, Employee, List<Employee>, Employee>(
from: employeeCollection,
connectFromField: e => e.ReportsTo,
connectToField: e => e.Name,
startWith: e => e.ReportsTo,
@as: e => e.ReportingHierarchy,
new AggregateGraphLookupOptions<Employee, Employee, Employee>
{
MaxDepth = 1
});

You can also use an AggregateGraphLookupOptions object to specify a filter that documents must match in order for MongoDB to include them in your search. 您还可以使用AggregateGraphLookupOptions对象指定文档必须匹配的筛选器,以便MongoDB将其包含在您的搜索中。The following code example performs the same $graphLookup operation as the previous examples, but includes only Employee documents where the Hobbies field contains "golf":下面的代码示例执行与前面的示例相同的$graphLookup操作,但仅包括Hobbies字段包含"golf"Employee(员工)文档:

var employeeCollection = client.GetDatabase("aggregation_examples").GetCollection<Employee>("employees");
var pipeline = new EmptyPipelineDefinition<Employee>()
.GraphLookup<Employee, Employee, Employee, Employee, string, Employee, List<Employee>, Employee>(
from: employeeCollection,
connectFromField: e => e.ReportsTo,
connectToField: e => e.Name,
startWith: e => e.ReportsTo,
@as: e => e.ReportingHierarchy,
new AggregateGraphLookupOptions<Employee, Employee, Employee>
{
MaxDepth = 1,
RestrictSearchWithMatch = Builders<Employee>.Filter.AnyEq(e => e.Hobbies, "golf")
});
Node.js

A collection named employees has the following documents:名为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" }
]);

To use the MongoDB Node.js driver to add a $graphLookup stage to an aggregation pipeline, use the $graphLookup operator in a pipeline object.要使用MongoDB Node.js驱动程序将$graphLookup阶段添加到聚合管道中,请在管道对象中使用$graphLookup运算符。

The following example creates a pipeline stage that recursively matches the reportsTo fields to the name fields in the employees collection, returning the reporting hierarchy for each person in a new field named reportingHierarchy. The example then runs the aggregation pipeline:以下示例创建了一个管道阶段,该阶段递归地将reportsTo字段与employees集合中的name字段匹配,在名为reportingHierarchy的新字段中返回每个人的报告层次结构。然后,该示例运行聚合管道:

const pipeline = [
{
$graphLookup: {
from: "employees",
connectFromField: "reportsTo",
connectToField: "name",
startWith: "$reportsTo",
as: "reportingHierarchy"
}
}
];

const cursor = collection.aggregate(pipeline);
return cursor;

To specify the depth of the recursion, use the maxDepth field. The following code example performs the same $graphLookup operation as the previous example, but specifies a maximum recursion depth of 1:要指定递归的深度,请使用maxDepth字段。以下代码示例执行与前一个示例相同的$graphLookup操作,但指定的最大递归深度为1

const pipeline = [
{
$graphLookup: {
from: "employees",
connectFromField: "reportsTo",
connectToField: "name",
startWith: "$reportsTo",
as: "reportingHierarchy",
maxDepth: 1
}
}
];

const cursor = collection.aggregate(pipeline);
return cursor;

To specify a filter that documents must match for the operation to include them in the search results, use the restrictSearchWithMatch field. 要指定文档必须匹配的筛选器,以便操作将其包含在搜索结果中,请使用restrictSearchWithMatch字段。The following code example performs the same $graphLookup operation as the previous examples, but includes only employee documents where the hobbies field contains "golf":以下代码示例执行与前面示例相同的$traphLookup操作,但仅包括hobbies字段包含"golf"employee文档:

const pipeline = [
{
$graphLookup: {
from: "employees",
connectFromField: "reportsTo",
connectToField: "name",
startWith: "$reportsTo",
as: "reportingHierarchy",
maxDepth: 1,
restrictSearchWithMatch: { hobbies: "golf" }
}
}
];

const cursor = collection.aggregate(pipeline);
return cursor;

Learn More了解更多

To learn more about how to use $graphLookup, see Webinar: Working with Graph Data in MongoDB.要了解有关如何使用$graphLookup的更多信息,请参阅网络研讨会:在MongoDB中使用图形数据