Database Manual / Reference / Query Language / Aggregation Stages

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

Definition定义

$lookup

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

Performs a left outer join to a collection in the same database to filter in documents from the foreign collection for processing. 对同一数据库中的集合执行左外联接,以从外部集合中筛选文档进行处理。The $lookup stage adds a new array field to each input document. The new array field contains the matching documents from the foreign collection. $lookup阶段为每个输入文档添加一个新的数组字段。新的数组字段包含来自外部集合的匹配文档。The $lookup stage passes these reshaped documents to the next stage.$lookup阶段将这些重塑的文档传递到下一阶段。

Starting in MongoDB 5.1, you can use $lookup with sharded collections.从MongoDB 5.1开始,您可以在分片集合中使用$lookup

To combine elements from two different collections, use the $unionWith pipeline stage.要组合来自两个不同集合的元素,请使用$unionWith管道阶段。

Compatibility兼容性

You can use $lookup for deployments hosted in the following environments:您可以将$lookup用于在以下环境中托管的部署:

  • MongoDB Atlas: The fully managed service for MongoDB deployments in the cloud:云中MongoDB部署的完全托管服务
  • MongoDB Enterprise: The subscription-based, self-managed version of MongoDB:MongoDB的基于订阅的自我管理版本
  • MongoDB Community: The source-available, free-to-use, and self-managed version of MongoDB:MongoDB的源代码可用、免费使用和自我管理版本

Syntax语法

The $lookup stage syntax:$lookup阶段语法:

{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to run> ],
as: <output array field>
}
}

The $lookup accepts a document with these fields:$lookup接受具有以下字段的文档:

Field字段Necessity必要性Description描述
fromRequired必需

Specifies the foreign collection in the same database to join to the local collection.指定同一数据库中要加入本地集合的外部集合。

It is possible in some edge cases to subsitute from with pipeline with $documents as the first stage. 在某些边缘情况下,对于from,第一阶段可以用$documents代替管道。For an example, see Use a $documents Stage in a $lookup Stage.例如,请参阅$lookup阶段中使用$documents阶段

Starting in MongoDB 5.1, the from collection can be sharded.从MongoDB 5.1开始,from集合可以分片。

localFieldOptional 可选。if pipeline is specified如果指定了pipeline

Specifies the field from the documents input to the $lookup stage. $lookup performs an equality match on the localField to the foreignField from the documents of the from collection. 指定从文档输入到$lookup阶段的字段。$lookup对from集合的文档中的localFieldforeignField执行相等匹配。If an input document does not contain the localField, the $lookup treats the field as having a value of null for matching purposes.如果输入文档不包含localField,则$lookup会将该字段视为具有null值以进行匹配。

foreignFieldOptional 可选。if pipeline is specified如果指定了pipeline

Specifies the foreign documents' foreignField to perform an equality match with the local documents' localField.指定外来文档的foreignField以执行与本地文档的localField的相等匹配。

If a foreign document does not contain a foreignField value, the $lookup uses a null value for the match.如果外部文档不包含foreignField值,则$lookup将使用null值进行匹配。

letOptional可选

Specifies variables to use in the pipeline stages. Use the variable expressions to access the fields from the local collection's documents that are input to the pipeline.指定要在pipeline阶段中使用的变量。使用变量表达式访问本地集合文档中输入到pipeline的字段。

To reference variables in pipeline stages, use the "$$<variable>" syntax.要引用pipeline阶段中的变量,请使用"$$<variable>"语法。

The let variables can be accessed by the stages in the pipeline, including additional $lookup stages nested in the pipeline.let变量可以由pipeline中的阶段访问,包括嵌套在pipeline中的其他$lookup阶段。

  • A $match stage requires the use of an $expr operator to access the variables. $match阶段需要使用$expr运算符来访问变量。The $expr operator allows the use of aggregation expressions inside of the $match syntax.$expr运算符允许在$match语法中使用聚合表达式。

    The $eq, $lt, $lte, $gt, and $gte comparison operators placed in an $expr operator can use an index on the from collection referenced in a $lookup stage. Limitations:放置在$expr运算符中的$eq$lt$lte$gt$gte比较运算符可以在$lookup阶段引用的from集合上使用索引。限制:

    • Indexes can only be used for comparisons between fields and constants, so the let operand must resolve to a constant.索引只能用于字段和常量之间的比较,因此let操作数必须解析为常量。

      For example, a comparison between $a and a constant value can use an index, but a comparison between $a and $b cannot.例如,$a和常量值之间的比较可以使用索引,但$a$b之间的比较不能。

    • Indexes are not used for comparisons where the let operand resolves to an empty or missing value.let操作数解析为空值或缺失值时,索引不用于比较。
    • Multikey indexes are not used.不使用多键索引
  • Other (non-$match) stages in the pipeline do not require an $expr operator to access the variables.pipeline中的其他(非$match)阶段不需要$expr运算符来访问变量。
pipelineOptional 可选。if localField and foreignField are specified如果指定了localFieldforeignField

Specifies the pipeline to run on the foreign collection. The pipeline returns documents from the foreign collection. To return all documents, specify an empty pipeline: [].指定要在外部集合上运行的pipeline。该pipeline从国外集合文件。要返回所有文档,请指定一个空pipeline: []

The pipeline cannot include the $out or $merge stages. pipeline不能包含$out$merge阶段。Starting in v6.0, the pipeline can contain the MongoDB Search $search stage as the first stage inside the pipeline. 从v6.0开始,pipeline可以包含MongoDB Search$Search阶段作为管道内的第一阶段。To learn more, see MongoDB Search Support.要了解更多信息,请参阅MongoDB搜索支持

The pipeline cannot access fields from input documents. Instead, define variables for the document fields using the let option and then reference the variables in the pipeline stages.pipeline无法访问输入文档中的字段。相反,使用let选项为文档字段定义变量,然后在管道阶段引用变量。

To reference variables in pipeline stages, use the "$$<variable>" syntax.要引用pipeline阶段中的变量,请使用"$$<variable>"语法。

The let variables can be accessed by the stages in the pipeline, including additional $lookup stages nested in the pipeline.let变量可以由pipeline中的阶段访问,包括嵌套在pipeline中的其他$lookup阶段。

  • A $match stage requires the use of an $expr operator to access the variables. $match阶段需要使用$expr运算符来访问变量。The $expr operator allows the use of aggregation expressions inside of the $match syntax.$expr运算符允许在$match语法中使用聚合表达式。

    The $eq, $lt, $lte, $gt, and $gte comparison operators placed in an $expr operator can use an index on the from collection referenced in a $lookup stage. Limitations:放置在$expr运算符中的$eq$lt$lte$gt$gte比较运算符可以在$lookup阶段引用的from集合上使用索引。限制:

    • Indexes can only be used for comparisons between fields and constants, so the let operand must resolve to a constant.索引只能用于字段和常量之间的比较,因此let操作数必须解析为常量。

      For example, a comparison between $a and a constant value can use an index, but a comparison between $a and $b cannot.例如,$a和常量值之间的比较可以使用索引,但$a$b之间的比较不能。

    • Indexes are not used for comparisons where the let operand resolves to an empty or missing value.let操作数解析为空值或缺失值时,索引不用于比较。
    • Multikey indexes are not used.不使用多键索引
  • Other (non-$match) stages in the pipeline do not require an $expr operator to access the variables.pipeline中的其他(非$match)阶段不需要$expr运算符来访问变量。
asRequired必需

Specifies the name of the new array field to add to the input documents. The new array field contains the matching documents from the from collection. If the specified name already exists in the input document, the existing field is overwritten.指定要添加到输入文档中的新数组字段的名称。新的数组字段包含from集合中的匹配文档。如果指定的名称已存在于输入文档中,则现有字段将被覆盖

Equality Match with a Single Join Condition具有单一连接条件的相等匹配

To perform an equality match between a field from the input documents with a field from the documents of the foreign collection, the $lookup stage has this syntax:要在输入文档的字段与外部集合文档的字段之间执行相等匹配,$lookup阶段有以下语法:

{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
pipeline: [ <pipeline to run> ],
as: <output array field>
}
}

Note

In this example, pipeline is optional and runs after the local and foreign equality stage.在这个例子中,pipeline是可选的,在本地和外部相等阶段之后运行。

The operation corresponds to this pseudo-SQL statement:该操作对应于此伪SQL语句:

SELECT *, (
SELECT ARRAY_AGG(*)
FROM <collection to join>
WHERE <foreignField> = <collection.localField>
) AS <output array field>
FROM collection;

Note

The SQL statements on this page are included for comparison to the MongoDB aggregation pipeline syntax. The SQL statements aren't runnable.此页面上的SQL语句用于与MongoDB聚合管道语法进行比较。SQL语句无法运行。

For MongoDB examples, see these pages:有关MongoDB示例,请参阅以下页面:

Join Conditions and Subqueries on a Foreign Collection外国托收的加入条件和子查询

MongoDB supports:支持:

  • Executing a pipeline on a foreign collection.在外部集合上执行管道。
  • Multiple join conditions.多种加入条件。
  • Correlated and uncorrelated subqueries.相关和不相关的子查询。

In MongoDB, an uncorrelated subquery means that every input document will return the same result. A correlated subquery is a pipeline in a $lookup stage that uses the local or input collection's fields to return results correlated to each incoming document.在MongoDB中,不相关的子查询意味着每个输入文档将返回相同的结果。相关子查询是$lookup阶段的一个管道,它使用本地或input集合的字段返回与每个传入文档相关的结果。

Note

Starting in MongoDB 5.0, for an uncorrelated subquery in a $lookup pipeline stage containing a $sample stage, the $sampleRate operator, or the $rand operator, the subquery is always run again if repeated. Previously, depending on the subquery output size, either the subquery output was cached or the subquery was run again.从MongoDB 5.0开始,对于包含$sample阶段、$sampleRate运算符或$rand运算符的$lookup管道阶段中的不相关子查询,如果重复,子查询总是会再次运行。以前,根据子查询输出的大小,要么缓存子查询输出,要么再次运行子查询。

MongoDB correlated subqueries are comparable to SQL correlated subqueries, where the inner query references outer query values. An SQL uncorrelated subquery does not reference outer query values.MongoDB相关子查询与SQL相关子查询类似,其中内部查询引用外部查询值。SQL不相关的子查询不引用外部查询值。

MongoDB 5.0 also supports concise correlated subqueries.MongoDB 5.0还支持简洁的相关子查询

To perform correlated and uncorrelated subqueries with two collections, and perform other join conditions besides a single equality match, use this $lookup syntax:要对两个集合执行相关和不相关的子查询,并执行除单个相等匹配之外的其他连接条件,请使用以下$lookup语法:

{
$lookup:
{
from: <foreign collection>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to run on foreign collection> ],
as: <output array field>
}
}

The operation corresponds to this pseudo-SQL statement:该操作对应于此伪SQL语句:

SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (
SELECT <documents as determined from the pipeline>
FROM <collection to join>
WHERE <pipeline>
);

See the following examples:请参阅以下示例:

Correlated Subqueries Using Concise Syntax使用简洁语法的相关子查询

New in version 5.0.在版本5.0中新增。

Starting in MongoDB 5.0, you can use a concise syntax for a correlated subquery. Correlated subqueries reference document fields from a foreign collection and the "local" collection on which the aggregate() method was run.从MongoDB 5.0开始,您可以对相关子查询使用简洁的语法。相关子查询引用来自外部集合和运行aggregate()方法的"local"集合的文档字段。

The following new concise syntax removes the requirement for an equality match on the foreign and local fields inside of an $expr operator:以下新的简洁语法消除了对$expr运算符内的外部和本地字段进行相等匹配的要求:

{
$lookup:
{
from: <foreign collection>,
localField: <field from local collection's documents>,
foreignField: <field from foreign collection's documents>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to run> ],
as: <output array field>
}
}

The operation corresponds to this pseudo-SQL statement:该操作对应于此伪SQL语句:

SELECT *, <output array field>
FROM localCollection
WHERE <output array field> IN (
SELECT <documents as determined from the pipeline>
FROM <foreignCollection>
WHERE <foreignCollection.foreignField> = <localCollection.localField>
AND <pipeline match condition>
);

See this example:请看这个例子:

Behavior行为

Encrypted Collections加密集合

Starting in MongoDB 8.1, you can reference multiple encrypted collections in a $lookup stage. However, $lookup does not support:从MongoDB 8.1开始,您可以在$lookup阶段引用多个加密集合。但是,$lookup不支持:

  • Using an encrypted field as the join field in the localField or foreignField.使用加密字段作为localFieldforeignField中的联接字段。

    Note

    For drivers using Client-Side Field Level Encryption, you can use an encrypted field as a join field only if you are performing a self-join operation.对于使用客户端字段级加密的驱动程序,只有在执行自连接操作时,才能将加密字段用作连接字段。

  • Using any field in an encrypted array. An array is considered as encrypted if it contains any encrypted elements.使用加密数组中的任何字段。如果一个数组包含任何加密元素,则认为它是加密的。

    • For example, you can't use any field within the resulting as array of the $lookup operation, unless you're using Client-Side Field Level Encryption and $unwind the as field.例如,您不能在$lookup操作的结果数组中使用任何字段,除非您使用客户端字段级加密和$unwind as字段。

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,则这些视图必须具有相同的排序规则

Restrictions限制

You cannot include the $out or the $merge stage in the $lookup stage. 您不能在$lookup阶段中包含$out$merge阶段。That is, when specifying a pipeline for the foreign collection, you cannot include either stage in the pipeline field.也就是说,在为外部集合指定管道时,您不能在管道字段中包含任何阶段。

{
$lookup:
{
from: <collection to join>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to execute on the foreign collection> ], // Cannot include $out or $merge
as: <output array field>
}
}

MongoDB Search SupportMongoDB搜索支持

Starting in MongoDB 6.0, you can specify the MongoDB Search $search or $searchMeta stage in the $lookup pipeline to search collections on the Atlas cluster. 从MongoDB 6.0开始,您可以在$lookup管道中指定MongoDB Search$Search$searchMeta阶段,以搜索Atlas集群上的集合。The $search or the $searchMeta stage must be the first stage inside the $lookup pipeline.$search$searchMeta阶段必须是$lookup管道中的第一阶段。

For example, when you Join Conditions and Subqueries on a Foreign Collection or run Correlated Subqueries Using Concise Syntax, you can specify $search or $searchMeta inside the pipeline as shown below:例如,当您在外部集合上连接条件和子查询使用简明语法运行相关子查询时,您可以在管道内指定$search$searchMeta,如下所示:

$search
[{
"$lookup": {
"from": <foreign collection>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
"as": <output array field>,
"pipeline": [{
"$search": {
"<operator>": {
<operator-specification>
}
},
...
}]
}
}]
$searchMeta
[{
"$lookup": {
"from": <foreign collection>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
"as": <output array field>,
"pipeline": [{
"$searchMeta": {
"<collector>": {
<collector-specification>
}
},
...
}]
}
}]

To see an example of $lookup with $search, see the MongoDB Search tutorial Run a MongoDB Search $search Query Using $lookup.要查看使用$search$lookup示例,请参阅MongoDB搜索教程使用$lookup运行MongoDB搜索$search查询。

Sharded Collections分片化集合

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

Starting in MongoDB 8.0, you can use the $lookup stage within a transaction while targeting a sharded collection.从MongoDB 8.0开始,您可以在事务中使用$lookup阶段,同时针对分片集合。

Slot-Based Query Execution Engine基于插槽的查询执行引擎

Starting in version 6.0, MongoDB can use the slot-based execution query engine to execute $lookup stages if all preceding stages in the pipeline can also be executed by the slot-based execution engine and none of the following conditions are true:从6.0版本开始,如果管道中的所有前面阶段也可以由基于槽的执行引擎执行,并且以下条件均不成立,MongoDB可以使用基于槽的运行查询引擎来执行$lookup阶段:

  • The $lookup operation executes a pipeline on a foreign collection. To see an example of this kind of operation, see Join Conditions and Subqueries on a Foreign Collection.$lookup操作在外部集合上执行管道。要查看此类操作的示例,请参阅外部集合上的联接条件和子查询
  • The $lookup's localField or foreignField specify numeric components. For example: { localField: "restaurant.0.review" }.$lookuplocalFieldforeignField指定了数字组件。例如:{ localField: "restaurant.0.review" }
  • The from field of any $lookup in the pipeline specifies a view or sharded collection.任何$lookup管道中的from字段都指定了一个视图或分片集合。

For more information, see $lookup Optimization.有关更多信息,请参阅$lookup优化

Performance Considerations性能考量

$lookup performance depends on the type of operation performed. Refer to the following table for performance considerations for different $lookup operations.$lookup性能取决于执行的操作类型。有关不同$lookup操作的性能考虑,请参阅下表。

$lookup Operation操作Performance Considerations性能考量
Equality Match with a Single Join通过单一连接进行平等匹配
  • $lookup operations that perform equality matches with a single join perform better when the foreign collection contains an index on the foreignField.当外部集合包含foreignField上的索引时,使用单个联接执行相等匹配的$lookup操作性能更好。

    IMPORTANT:重要: If a supporting index on the foreignField does not exist, a $lookup operation that performs an equality match with a single join will likely have poor performance.如果foreignField上不存在支持索引,则与单个联接执行相等匹配的$lookup操作的性能可能会很差。

Uncorrelated Subqueries不相关的子查询
  • $lookup operations that contain uncorrelated subqueries perform better when the inner pipeline can reference an index of the foreign collection.当内部管道可以引用外部集合的索引时,包含不相关子查询的$lookup操作性能更好。
  • MongoDB only needs to run the $lookup subquery once before caching the query because there is no relationship between the source and foreign collections. MongoDB只需要在缓存查询之前运行一次$lookup子查询,因为源集合和外部集合之间没有关系。The subquery is not based on any value in the source collection. This behavior improves performance for subsequent executions of the $lookup operation.子查询不基于源集合中的任何值。此行为提高了$lookup操作后续执行的性能。
Correlated Subqueries关联子查询
  • $lookup operations that contain correlated subqueries perform better when the following conditions apply:当满足以下条件时,包含相关子查询的$lookup操作表现更好:

    • The foreign collection contains an index on the foreignField.外国集合包含foreignField的索引。
    • The foreign collection contains an index that references the inner pipline.外部集合包含一个引用内部管线的索引。
  • If your pipeline passes a large number of documents to the $lookup query, the following strategies may improve performance:如果您的管道将大量文档传递给$lookup查询,以下策略可能会提高性能:

    • Reduce the number of documents that MongoDB passes to the $lookup query. For example, set a stricter filter during the $match stage.减少MongoDB传递给$lookup查询的文档数量。例如,在$match阶段设置一个更严格的筛选器。
    • Run the inner pipeline of the $lookup subquery as a separate query and use $out to create a temporary collection. Then, run an equality match with a single join.$lookup子查询的内部管道作为单独的查询运行,并使用$out创建临时集合。然后,用一个连接运行一个相等匹配
    • Reconsider the data's schema to ensure it is optimal for the use case.重新考虑数据的模式,以确保它对用例是最优的。

For general performance strategies, see Indexing Strategies and Query Optimization.有关一般性能策略,请参阅索引策略查询优化

Important

Excessive use of $lookup within a query may slow down performance. 在查询中过度使用$lookup可能会降低性能。To avoid multiple $lookup stages, consider an embedded data model to optimize query performance.为了避免多个$lookup阶段,可以考虑使用嵌入式数据模型来优化查询性能。

Examples示例

MongoDB Shell

Perform a Single Equality Join with $lookup使用$lookup执行单个相等连接

Create a collection orders with these documents:使用以下文档创建集合orders

db.orders.insertMany( [
{ _id: 1, item: "almonds", price: 12, quantity: 2 },
{ _id: 2, item: "pecans", price: 20, quantity: 1 },
{ _id: 3 }
] )

Create another collection inventory with these documents:使用以下文档创建另一个集合inventory

db.inventory.insertMany( [
{ _id: 1, sku: "almonds", description: "product 1", instock: 120 },
{ _id: 2, sku: "bread", description: "product 2", instock: 80 },
{ _id: 3, sku: "cashews", description: "product 3", instock: 60 },
{ _id: 4, sku: "pecans", description: "product 4", instock: 70 },
{ _id: 5, sku: null, description: "Incomplete" },
{ _id: 6 }
] )

The following aggregation operation on the orders collection joins the documents from orders with the documents from the inventory collection using the fields item from the orders collection and the sku field from the inventory collection:orders集合的以下聚合操作使用orders集合的字段项和inventory集合的sku字段将订单中的文档与inventory集合中的文档连接起来:

db.orders.aggregate( [
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
] )

The operation returns these documents:该操作返回以下文档:

{
_id: 1,
item: "almonds",
price: 12,
quantity: 2,
inventory_docs: [
{ _id: 1, sku: "almonds", description: "product 1", instock: 120 }
]
}
{
_id: 2,
item: "pecans",
price: 20,
quantity: 1,
inventory_docs: [
{ _id: 4, sku: "pecans", description: "product 4", instock: 70 }
]
}
{
_id: 3,
inventory_docs: [
{ _id: 5, sku: null, description: "Incomplete" },
{ _id: 6 }
]
}

The operation corresponds to this pseudo-SQL statement:该操作对应于此伪SQL语句:

SELECT *, inventory_docs
FROM orders
WHERE inventory_docs IN (
SELECT *
FROM inventory
WHERE sku = orders.item
);

For more information, see Equality Match Performance Considerations.有关详细信息,请参阅平等匹配性能考虑因素

Use $lookup with an Array$lookup与数组一起使用

If the localField is an array, you can match the array elements against a scalar foreignField without an $unwind stage.如果localField是一个数组,则可以将数组元素与标量foreignField进行匹配,而无需$unwind阶段。

For example, create an example collection classes with these documents:例如,使用以下文档创建一个示例集合classes

db.classes.insertMany( [
{ _id: 1, title: "Reading is ...", enrollmentlist: [ "giraffe2", "pandabear", "artie" ], days: ["M", "W", "F"] },
{ _id: 2, title: "But Writing ...", enrollmentlist: [ "giraffe1", "artie" ], days: ["T", "F"] }
] )

Create another collection members with these documents:使用以下文档创建另一个集合members

db.members.insertMany( [
{ _id: 1, name: "artie", foreign: new Date("2016-05-01"), status: "A" },
{ _id: 2, name: "giraffe", foreign: new Date("2017-05-01"), status: "D" },
{ _id: 3, name: "giraffe1", foreign: new Date("2017-10-01"), status: "A" },
{ _id: 4, name: "panda", foreign: new Date("2018-10-11"), status: "A" },
{ _id: 5, name: "pandabear", foreign: new Date("2018-12-01"), status: "A" },
{ _id: 6, name: "giraffe2", foreign: new Date("2018-12-01"), status: "D" }
] )

The following aggregation operation joins documents in the classes collection with the members collection, matching on the enrollmentlist field to the name field:以下聚合操作将classes集合中的文档与members集合连接起来,在enrollmentlist(注册列表)字段上与名称字段匹配:

db.classes.aggregate( [
{
$lookup:
{
from: "members",
localField: "enrollmentlist",
foreignField: "name",
as: "enrollee_info"
}
}
] )

The operation returns the following:该操作返回以下内容:

{
_id: 1,
title: "Reading is ...",
enrollmentlist: [ "giraffe2", "pandabear", "artie" ],
days: [ "M", "W", "F" ],
enrollee_info: [
{ _id: 1, name: "artie", foreign: ISODate("2016-05-01T00:00:00Z"), status: "A" },
{ _id: 5, name: "pandabear", foreign: ISODate("2018-12-01T00:00:00Z"), status: "A" },
{ _id: 6, name: "giraffe2", foreign: ISODate("2018-12-01T00:00:00Z"), status: "D" }
]
}
{
_id: 2,
title: "But Writing ...",
enrollmentlist: [ "giraffe1", "artie" ],
days: [ "T", "F" ],
enrollee_info: [
{ _id: 1, name: "artie", foreign: ISODate("2016-05-01T00:00:00Z"), status: "A" },
{ _id: 3, name: "giraffe1", foreign: ISODate("2017-10-01T00:00:00Z"), status: "A" }
]
}

Use $lookup with $mergeObjects$lookup$mergeObjects一起使用

The $mergeObjects operator combines multiple documents into a single document.$mergeObjects运算符将多个文档合并为一个文档。

Create a collection orders with these documents:使用以下文档创建集合orders

db.orders.insertMany( [
{ _id: 1, item: "almonds", price: 12, quantity: 2 },
{ _id: 2, item: "pecans", price: 20, quantity: 1 }
] )

Create another collection items with these documents:使用这些文档创建另一个集合items

db.items.insertMany( [
{ _id: 1, item: "almonds", description: "almond clusters", instock: 120 },
{ _id: 2, item: "bread", description: "raisin and nut bread", instock: 80 },
{ _id: 3, item: "pecans", description: "candied pecans", instock: 60 }
] )

The following operation first uses the $lookup stage to join the two collections by the item fields and then uses $mergeObjects in the $replaceRoot to merge the foreign documents from items and orders:以下操作首先使用$lookup阶段按item字段连接两个集合,然后使用$replaceRoot中的$mergeObjects合并itemsorders中的外部文档:

db.orders.aggregate( [
{
$lookup: {
from: "items",
localField: "item", // field in the orders collection
foreignField: "item", // field in the items collection
as: "fromItems"
}
},
{
$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$fromItems", 0 ] }, "$$ROOT" ] } }
},
{ $project: { fromItems: 0 } }
] )

The operation returns these documents:该操作返回以下文档:

{
_id: 1,
item: 'almonds',
description: 'almond clusters',
instock: 120,
price: 12,
quantity: 2
},
{
_id: 2,
item: 'pecans',
description: 'candied pecans',
instock: 60,
price: 20,
quantity: 1
}

Use Multiple Join Conditions and a Correlated Subquery使用多个连接条件和相关子查询

Pipelines can execute on a foreign collection and include multiple join conditions. 管道可以在外部集合上执行,并包含多个连接条件。The $expr operator enables more complex join conditions including conjunctions and non-equality matches.$expr运算符支持更复杂的连接条件,包括连词和非等式匹配。

A join condition can reference a field in the local collection on which the aggregate() method was run and reference a field in the foreign collection. This allows a correlated subquery between the two collections.连接条件可以引用本地集合中运行aggregate()方法的字段,也可以引用外部集合中的字段。这允许在两个集合之间进行相关子查询。

MongoDB 5.0 supports concise correlated subqueries.MongoDB 5.0支持简洁的相关子查询

Create a collection orders with these documents:使用以下文档创建集合orders

db.orders.insertMany( [
{ _id: 1, item: "almonds", price: 12, ordered: 2 },
{ _id: 2, item: "pecans", price: 20, ordered: 1 },
{ _id: 3, item: "cookies", price: 10, ordered: 60 }
] )

Create another collection warehouses with these documents:使用以下文档创建另一个集合warehouses

db.warehouses.insertMany( [
{ _id: 1, stock_item: "almonds", warehouse: "A", instock: 120 },
{ _id: 2, stock_item: "pecans", warehouse: "A", instock: 80 },
{ _id: 3, stock_item: "almonds", warehouse: "B", instock: 60 },
{ _id: 4, stock_item: "cookies", warehouse: "B", instock: 40 },
{ _id: 5, stock_item: "cookies", warehouse: "A", instock: 80 }
] )

The following example:以下示例:

  • Uses a correlated subquery with a join on the orders.item and warehouse.stock_item fields.orders.itemwarehouse.stock_item字段上使用具有联接的相关子查询。
  • Ensures the quantity of the item in stock can fulfill the ordered quantity.确保inventory商品的数量能够满足订购数量。
db.orders.aggregate( [
{
$lookup:
{
from : "warehouses",
localField : "item",
foreignField : "stock_item",
let : { order_qty: "$ordered" },
pipeline : [
{ $match :
{ $expr :
{ $gte: [ "$instock", "$$order_qty" ] }
}
},
{ $project : { stock_item: 0, _id: 0 } }
],
as : "stockdata"
}
}
] )

The operation returns these documents:该操作返回以下文档:

{
_id: 1,
item: 'almonds',
price: 12,
ordered: 2,
stockdata: [
{ warehouse: 'A', instock: 120 },
{ warehouse: 'B', instock: 60 }
]
},
{
_id: 2,
item: 'pecans',
price: 20,
ordered: 1,
stockdata: [ { warehouse: 'A', instock: 80 } ]
},
{
_id: 3,
item: 'cookies',
price: 10,
ordered: 60,
stockdata: [ { warehouse: 'A', instock: 80 } ]
}

The operation corresponds to this pseudo-SQL statement:该操作对应于此伪SQL语句:

SELECT *, stockdata
FROM orders
WHERE stockdata IN (
SELECT warehouse, instock
FROM warehouses
WHERE stock_item = orders.item
AND instock >= orders.ordered
);

The $eq, $lt, $lte, $gt, and $gte comparison operators placed in an $expr operator can use an index on the from collection referenced in a $lookup stage. Limitations:放置在$expr运算符中的$eq$lt$lte$gt$gte比较运算符可以在$lookup阶段引用的from集合上使用索引。限制:

  • Indexes can only be used for comparisons between fields and constants, so the let operand must resolve to a constant.索引只能用于字段和常量之间的比较,因此let操作数必须解析为常量。

    For example, a comparison between $a and a constant value can use an index, but a comparison between $a and $b cannot.例如,$a和常量值之间的比较可以使用索引,但$a$b之间的比较不能。

  • Indexes are not used for comparisons where the let operand resolves to an empty or missing value.当let操作数解析为空值或缺失值时,索引不用于比较。
  • Multikey indexes are not used.不使用多键索引

For example, if the index { stock_item: 1, instock: 1 } exists on the warehouses collection:例如,如果warehouses(仓库)集合上存在索引{ stock_item: 1, instock: 1 }

  • The equality match on the warehouses.stock_item field uses the index.warehouses.stock_item字段上的相等匹配使用索引。
  • The range part of the query on the warehouses.instock field also uses the indexed field in the compound index.warehouses.instock字段查询的范围部分也使用复合索引中的索引字段。

Perform an Uncorrelated Subquery with $lookup使用$lookup执行不相关的子查询

An aggregation pipeline $lookup stage can execute a pipeline on the foreign collection, which allows uncorrelated subqueries. An uncorrelated subquery does not reference the local document fields.聚合管道$lookup阶段可以在外部集合上执行管道,这允许不相关的子查询。不相关的子查询不引用本地文档字段。

Note

Starting in MongoDB 5.0, for an uncorrelated subquery in a $lookup pipeline stage containing a $sample stage, the $sampleRate operator, or the $rand operator, the subquery is always run again if repeated. 从MongoDB 5.0开始,对于包含$sample阶段、$sampleRate运算符或$rand运算符的$lookup管道阶段中的不相关子查询,如果重复,子查询总是会再次运行。Previously, depending on the subquery output size, either the subquery output was cached or the subquery was run again.以前,根据子查询输出的大小,要么缓存子查询输出,要么再次运行子查询。

Create a collection absences with these documents:使用以下文档创建集合absences

db.absences.insertMany( [
{ _id: 1, student: "Ann Aardvark", sickdays: [ new Date ("2018-05-01"),new Date ("2018-08-23") ] },
{ _id: 2, student: "Zoe Zebra", sickdays: [ new Date ("2018-02-01"),new Date ("2018-05-23") ] },
] )

Create another collection holidays with these documents:使用这些文档创建另一个集合holidays

db.holidays.insertMany( [
{ _id: 1, year: 2018, name: "New Years", date: new Date("2018-01-01") },
{ _id: 2, year: 2018, name: "Pi Day", date: new Date("2018-03-14") },
{ _id: 3, year: 2018, name: "Ice Cream Day", date: new Date("2018-07-15") },
{ _id: 4, year: 2017, name: "New Years", date: new Date("2017-01-01") },
{ _id: 5, year: 2017, name: "Ice Cream Day", date: new Date("2017-07-16") }
] )

The following operation joins the absences collection with 2018 holiday information from the holidays collection:以下操作将absences(缺勤)集合与holidays(假期)集合中的2018年假期信息合并:

db.absences.aggregate( [
{
$lookup:
{
from: "holidays",
pipeline: [
{ $match: { year: 2018 } },
{ $project: { _id: 0, date: { name: "$name", date: "$date" } } },
{ $replaceRoot: { newRoot: "$date" } }
],
as: "holidays"
}
}
] )

The operation returns the following:该操作返回以下内容:

{
_id: 1,
student: 'Ann Aardvark',
sickdays: [
ISODate("2018-05-01T00:00:00.000Z"),
ISODate("2018-08-23T00:00:00.000Z")
],
holidays: [
{ name: 'New Years', date: ISODate("2018-01-01T00:00:00.000Z") },
{ name: 'Pi Day', date: ISODate("2018-03-14T00:00:00.000Z") },
{ name: 'Ice Cream Day', date: ISODate("2018-07-15T00:00:00.000Z")
}
]
},
{
_id: 2,
student: 'Zoe Zebra',
sickdays: [
ISODate("2018-02-01T00:00:00.000Z"),
ISODate("2018-05-23T00:00:00.000Z")
],
holidays: [
{ name: 'New Years', date: ISODate("2018-01-01T00:00:00.000Z") },
{ name: 'Pi Day', date: ISODate("2018-03-14T00:00:00.000Z") },
{ name: 'Ice Cream Day', date: ISODate("2018-07-15T00:00:00.000Z")
}
]
}

The operation corresponds to this pseudo-SQL statement:该操作对应于此伪SQL语句:

SELECT *, holidays
FROM absences
WHERE holidays IN (
SELECT name, date
FROM holidays
WHERE year = 2018
);

For more information, see Uncorrelated Subquery Performance Considerations.有关更多信息,请参阅不相关的子查询性能考虑因素

Perform a Concise Correlated Subquery with $lookup使用$lookup执行简洁的相关子查询

New in version 5.0.在版本5.0中新增。

Starting in MongoDB 5.0, an aggregation pipeline $lookup stage supports a concise correlated subquery syntax that improves joins between collections. 从MongoDB 5.0开始,聚合管道$lookup阶段支持简洁的相关子查询语法,以改进集合之间的连接。The new concise syntax removes the requirement for an equality match on the foreign and local fields inside of an $expr operator in a $match stage.新的简洁语法消除了在$match阶段对$expr运算符内的外部和本地字段进行相等匹配的要求。

Create a collection restaurants:创建集合restaurants(餐厅):

db.restaurants.insertMany( [
{
_id: 1,
name: "American Steak House",
food: [ "filet", "sirloin" ],
beverages: [ "beer", "wine" ]
},
{
_id: 2,
name: "Honest John Pizza",
food: [ "cheese pizza", "pepperoni pizza" ],
beverages: [ "soda" ]
}
] )

Create another collection orders with food and optional drink orders:创建另一个包含食物和可选饮料订单的集合orders

db.orders.insertMany( [
{
_id: 1,
item: "filet",
restaurant_name: "American Steak House"
},
{
_id: 2,
item: "cheese pizza",
restaurant_name: "Honest John Pizza",
drink: "lemonade"
},
{
_id: 3,
item: "cheese pizza",
restaurant_name: "Honest John Pizza",
drink: "soda"
}
] )

The following example:以下示例:

  • Joins the orders and restaurants collections by matching the orders.restaurant_name localField with the restaurants.name foreignField. The match is performed before the pipeline is run.通过将orders.restaurant_name localFieldrestaurants.name foreignField匹配来合并ordersrestaurants集合。匹配在管道运行之前执行。
  • Performs an $in array match between the orders.drink and restaurants.beverages fields that are accessed using $$orders_drink and $beverages respectively.orders.drinkrestaurants.beverages字段之间执行$in数组匹配,这些字段分别使用$$orders_drink$beverages(饮料)访问。
db.orders.aggregate( [
{
$lookup: {
from: "restaurants",
localField: "restaurant_name",
foreignField: "name",
let: { orders_drink: "$drink" },
pipeline: [ {
$match: {
$expr: { $in: [ "$$orders_drink", "$beverages" ] }
}
} ],
as: "matches"
}
}
] )

There is a match for the soda value in the orders.drink and restaurants.beverages fields. This output shows the matches array and contains all foreign fields from the restaurants collection for the match:orders.drinkrestaurants.beverages字段中,soda(苏打水)的值是匹配的。此输出显示matches数组,并包含匹配restaurants集合中的所有外来字段:

{
_id: 1, item: "filet",
restaurant_name: "American Steak House",
matches: [ ]
}
{
_id: 2, item: "cheese pizza",
restaurant_name: "Honest John Pizza",
drink: "lemonade",
matches: [ ]
}
{
_id: 3, item: "cheese pizza",
restaurant_name: "Honest John Pizza",
drink: "soda",
matches: [ {
_id: 2, name": "Honest John Pizza",
food: [ "cheese pizza", "pepperoni pizza" ],
beverages: [ "soda" ]
} ]
}

This example uses the older verbose syntax from MongoDB versions before 5.0 and returns the same results as the previous concise example:此示例使用MongoDB 5.0之前版本的旧详细语法,并返回与前面简洁示例相同的结果:

db.orders.aggregate( [
{
$lookup: {
from: "restaurants",
let: { orders_restaurant_name: "$restaurant_name",
orders_drink: "$drink" },
pipeline: [ {
$match: {
$expr: {
$and: [
{ $eq: [ "$$orders_restaurant_name", "$name" ] },
{ $in: [ "$$orders_drink", "$beverages" ] }
]
}
}
} ],
as: "matches"
}
}
] )

The previous examples correspond to this pseudo-SQL statement:前面的例子对应于这个伪SQL语句:

SELECT *, matches
FROM orders
WHERE matches IN (
SELECT *
FROM restaurants
WHERE restaurants.name = orders.restaurant_name
AND restaurants.beverages = orders.drink
);

For more information, see Correlated Subquery Performance Considerations.有关更多信息,请参阅相关子查询性能考虑因素

Namespaces in Subpipelines子管道中的命名空间

Starting in MongoDB 8.0, namespaces in subpipelines within $lookup and $unionWith are validated to ensure the correct use of from and coll fields:从MongoDB 8.0开始,$lookup$unionWith子管道中的命名空间经过验证,以确保fromcoll字段的正确使用:

  • For $lookup, omit the from field if you use a subpipeline with a stage which doesn't require a specified collection. For example, a $documents stage.对于$lookup,如果您使用的子管道具有不需要指定集合的阶段,请省略from字段。例如,$documents阶段。
  • Similarly, for $unionWith, omit the coll field.同样,对于$unionWith,省略coll字段。

Unchanged behavior:行为不变:

  • For a $lookup that starts with a stage for a collection, for example a $match or $collStats subpipeline, you must include the from field and specify the collection.对于以集合的阶段开始的$lookup,例如$match$collStats子管道,您必须包含from字段并指定集合。
  • Similarly, for $unionWith, include the coll field and specify the collection.同样,对于$unionWith,包含coll字段并指定集合。

The following scenario shows an example.以下场景显示了一个示例。

Create a collection cakeFlavors:创建一个集合cakeFlavors(蛋糕口味):

db.cakeFlavors.insertMany( [
{ _id: 1, flavor: "chocolate" },
{ _id: 2, flavor: "strawberry" },
{ _id: 3, flavor: "cherry" }
] )

Starting in MongoDB 8.0, the following example returns an error because it contains an invalid from field:从MongoDB 8.0开始,以下示例返回错误,因为它包含无效的from字段:

db.cakeFlavors.aggregate( [ {
$lookup: {
from: "cakeFlavors",
pipeline: [ { $documents: [ {} ] } ],
as: "test"
}
} ] )

In MongoDB versions before 8.0, the previous example runs.在MongoDB 8.0之前的版本中,前面的示例运行。

For an example with a valid from field, see Perform a Single Equality Join with $lookup.有关有效from字段的示例,请参阅使用$lookup执行单个相等连接

C#

The C# examples on this page use the sample_mflix database from the Atlas sample datasets. 本页上的C#示例使用Atlas示例数据集中的sample_mflix数据库。To learn how to create a free MongoDB Atlas cluster and load the sample datasets, see Get Started in the MongoDB .NET/C# Driver documentation.要了解如何创建免费的MongoDB Atlas集群并加载示例数据集,请参阅MongoDB .NET/C#驱动程序文档中的入门

The following Movie class models the documents in the sample_mflix.movies collection:以下Movie类对sample_mflix.movies集合中的文档进行建模:

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

public int Runtime { get; set; }

public string Title { get; set; }

public string Rated { get; set; }

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

public string Plot { get; set; }

public ImdbData Imdb { get; set; }

public int Year { get; set; }

public int Index { get; set; }

public string[] Comments { get; set; }

[BsonElement("lastupdated")]
public DateTime LastUpdated { get; set; }
}

Note

ConventionPack for Pascal CasePascal大小写的约定包

The C# classes on this page use Pascal case for their property names, but the field names in the MongoDB collection use camel case. To account for this difference, you can use the following code to register a ConventionPack when your application starts:此页面上的C#类使用Pascal大小写作为其属性名,但MongoDB集合中的字段名使用驼峰大小写。为了解释这种差异,您可以在应用程序启动时使用以下代码注册ConventionPack

var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

The following Comment class models the documents in the sample_mflix.comments collection:以下Comment类对sample_mflix.comments集合中的文档进行建模:

public class Comment
{
public Guid Id { get; set; }

[BsonElement("movie_id")]
public Guid MovieId { get; set; }

public string Text { get; set; }
}

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

The following example creates a pipeline stage that performs a left outer join between the movies and comments collections. The code joins the Id field from each Movie document to the MovieId field in the Comment documents. The comments for each movie are stored in a field named Comments in each Movie document.以下示例创建了一个管道阶段,该阶段在moviescomments集合之间执行左外连接。该代码将每个Movie文档的Id字段连接到Comment文档中的MovieId字段。每部电影的评论都存储在每个Movie文档中名为Comments 的字段中。

var commentCollection = client
.GetDatabase("aggregation_examples")
.GetCollection<Comment>("comments");

var pipeline = new EmptyPipelineDefinition<Movie>()
.Lookup<Movie, Movie, Comment, Movie>(
foreignCollection: commentCollection,
localField: m => m.Id,
foreignField: c => c.MovieId,
@as: m => m.Comments);
Node.js

The Node.js examples on this page use the sample_mflix database from the Atlas sample datasets. 本页上的Node.js示例使用Atlas示例数据集中的sample_mflix数据库。To learn how to create a free MongoDB Atlas cluster and load the sample datasets, see Get Started in the MongoDB Node.js driver documentation.要了解如何创建免费的MongoDB Atlas集群并加载示例数据集,请参阅MongoDB Node.js驱动程序文档中的入门

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

The following example creates a pipeline stage that performs a left outer join between the movies and comments collections. 以下示例创建了一个管道阶段,该阶段在moviescomments集合之间执行左外连接。The code joins the _id field from each movie document to the movie_id field in the comment documents. 该代码将每个movie文档的_id字段与评论文档中的movie_id字段连接起来。The comments field stores the comments for each movie in each movie document. The example then runs the aggregation pipeline:comments字段存储每个movie文档中每个电影的评论。然后,该示例运行聚合管道:

const pipeline = [
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "movie_id",
as: "comments"
}
}
];

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