- Definition 定义
- Syntax 语法
- Considerations 注意事项
- Example 例子
- Perform a Single Equality Join with
$lookup
用$lookup
执行单一相等连接 - Use
$lookup
with an Array 在数组中使用$lookup
- Use
$lookup
with$mergeObjects
将$lookup
和$mergeObjects
一起使用 - Perform Multiple Joins and a Correlated Subquery with
$lookup
用$lookup
执行多个连接和一个关联的子查询 - Perform an Uncorrelated Subquery with
$lookup
用$lookup
执行一个不关联的子查询 - Perform a Concise Correlated Subquery with
$lookup
用$lookup
执行一个简洁的关联子查询
- Perform a Single Equality Join with
- 参考
Definition 定义
对同一数据库中的未分片集合(unsharded collection)执行左外连接(left outer join),从 “连接(joined)” 的 collection 中过滤出文档进行处理。
对于每个输入文档,$lookup
stage 添加一个新的数组字段,其元素(element)是 “连接(joined)” collection 中的匹配文档。$lookup
stage 将这些重新调整的文档传递到下一个 stage。
Syntax 语法
Equality Match with a Single Join Condition 单一连接条件下的相等匹配
为了在输入文档中的 field 和 “joined” collection 中的 field 进行相等匹配,$lookup
stage 有这样的语法:
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
$lookup
采用具有以下字段的文档:
Field | Description |
---|---|
from | 指定同一数据库 中的 collection,以执行 join。from collection 不能是分片(sharded)的。详情请参阅分片集合的限制(Sharded Collection Restrictions)。 |
localField | 指定输入到 $lookup stage 的文档中的字段(输入文档中的字段)。$lookup 从 from collection 的文档中对 localField 和 foreignField 进行相等匹配。如果输入的文档不包含localField ,$lookup 将该字段视为有一个空值(null) 用于达到匹配的目的。 |
foreignField | 指定来自 from collection 中文档的字段。$lookup 对输入文档中的 foreignField 和 localField 进行相等匹配。如果 from collection 中的文档不包含 foreignField ,$lookup 将该值视为空值(null)用于达到匹配的目的。 |
as | 指定新的数组字段的名称,以添加到输入文档中。新的数组字段包含来自 from collection 的匹配文档。如果指定的名称已经存在于输入文档中,那么现有的字段将被覆盖(overwritten) 。 |
该 operation 对应以下的伪 SQL 语句(pseudo-SQL statement) :
select *, <output array field>
from collection
where <output array field> in (
select *
from <collection to join>
where <foreignField> = <collection.localField>
)
请参阅以下示例:
- Perform a Single Equality Join with $lookup 用
$lookup
执行单一相等连接 - Use $lookup with an Array 在数组中使用
$lookup
Use $lookup with $mergeObjects 使用
$lookup
和$mergeObjects
Join Conditions and Subqueries on a Joined Collection 连接集合上的连接条件和子查询
MongoDB 3.6 增加了对以下内容的支持:
Executing a pipeline on a joined collection. 在连接的集合上执行管道
- Multiple join conditions. 多个连接条件
- Correlated and uncorrelated subqueries. 关联和不关联的子查询
在 MongoDB 中,Correlated subquery(关联子查询)是 $lookup
stage 的一个 pipeline,它引用了来自 joined collection 的文档字段。一个 uncorrelated subquery(不关联的子查询)不引用 joined fields。
NOTE 从 MongoDB 5.0 开始,对于包含
[$sample](https://docs.mongodb.com/manual/reference/operator/aggregation/sample/#mongodb-pipeline-pipe.-sample)
stage、[$sampleRate](https://docs.mongodb.com/manual/reference/operator/aggregation/sampleRate/#mongodb-expression-exp.-sampleRate)
operator 或[$rand](https://docs.mongodb.com/manual/reference/operator/aggregation/rand/#mongodb-expression-exp.-rand)
operator 的$lookup
pipeline stage 中的 uncorrelated subquery,如果重复,子查询总是被再次运行。以前,根据子查询的输出大小,要么 subquery 的输出被缓存,要么 subquery 被再次运行。
MongoDB correlated subquery 类似于 SQL correlated subquery,其中内部查询(inner query)引用外部查询值(outer query values)。一个 SQL uncorrelated subquery 不引用外部查询值(outer query values)。
MongoDB 5.0 还支持 concise correlated subqueries(简洁的关联子查询)。
若要对两个 collection 执行 correlated 和 uncorrelated subqueries,并执行除单个相等匹配之外的其他连接条件,请使用以下 $lookup
语法:
{
$lookup:
{
from: <joined collection>,
let: { <var_1>: <expression>, ..., <var_n>: <expression> },
pipeline: [ <pipeline to run on joined collection> ],
as: <output array field>
}
}
$lookup
stage 接受包含以下字段的文档:
Field | Description |
---|---|
from | 指定同一数据库 中的 collection 来执行 join operation。joined collection 不能是分片(sharded)的(参阅 Sharded Collection Restrictions 分片集合限制)。 |
let | 可选的。指定在 pipeline stage 使用的变量。使用 variable expressions 来访问输入到 pipeline 的 joined collection 文档中的字段。> NOTE |
要在 pipeline stage 引用变量,使用语法 “$$
“。 let 变量可以被 pipeline 中的 stage 访问,包括嵌套(nested)在
pipeline
中的额外(additional) $lookup stages。
- 一个 $match stage 需要使用 $expr operator 来访问变量。$expr operator 允许在 $match syntax 内部使用 aggregation expressions。
- 从 MongoDB 5.0 开始,放置在 $expr operator 中的 $eq、$lt、$lte、$gt 和 $gte comparison operators 可以使用 $lookup stage 中引用的
from
collection 上的索引(index)。限制条件:
- 不使用 Multikey indexes(多键索引)
- 在
运算对象为数组
或运算对象类型为 undefined
的情况下,不使用索引进行比较(comparison)。- 索引不用于与多个 field path 运算对象的比较(comparison)。
- pipeline 中的其他(非 $match)stage 不需要 $expr operator 来访问这些变量。
|
| pipeline | 指定在 joined collection 上运行的 pipeline
。pipeline
决定了从 joined collection 中得到的结果文档。要返回所有的文档,请指定一个 empty pipeline``[]
。pipeline
不能包含 $out stage 或者 $merge stage。pipeline
不能直接访问 joined document fields。取而代之的是,使用 let 选项为 joined document fields 定义变量,然后在 pipeline
stage 引用这些变量。> NOTE
要在 pipeline stage 引用变量,使用语法 “$$
“。 let 变量可以被 pipeline 中的 stage 访问,包括嵌套(nested)在
pipeline
中的额外(additional) $lookup stages。
- 一个 $match stage 需要使用 $expr operator 来访问变量。$expr operator 允许在 $match syntax 内部使用 aggregation expressions。
- 从 MongoDB 5.0 开始,放置在 $expr operator 中的 $eq、$lt、$lte、$gt 和 $gte comparison operators 可以使用 $lookup stage 中引用的
from
collection 上的索引(index)。限制条件:
- 不使用 Multikey indexes(多键索引)
- 在
运算对象为数组
或运算对象类型为 undefined
的情况下,不使用索引进行比较(comparison)。- 索引不用于与多个 field path 运算对象的比较(comparison)。
- pipeline 中的其他(非 $match)stage 不需要 $expr operator 来访问这些变量。
| | as | 指定要添加到 joined documents 的新数组字段的名称。 新的数组字段包含来自 joined collection 的匹配文档。 如果指定的名称已存在于 joined document 中,则覆盖(overwritten)现有字段。 |
该 operation 对应以下的 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>
)
请参阅以下示例:
- Perform Multiple Joins and a Correlated Subquery with $lookup 用
$lookup
执行多个连接和一个关联的子查询 - Perform an Uncorrelated Subquery with $lookup 用
$lookup
执行一个不关联的子查询Correlated Subqueries Using Concise Syntax 使用简洁语法的关联子查询
从 MongoDB 5.0 开始,你可以为 correlated subquery 使用一种简洁的语法(concise syntax)。
Correlated subqueries 引用了来自 a joined “foreign” collection(一个连接的外部/外键集合) 和运行 aggregate() 方法的 “local” collection(本地集合)的文档字段。
以下新的 concise syntax 消除了对 $expr operator 内的外部(foreign)和本地(local)字段进行相等匹配的要求:
{
$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>
}
}
该 $lookup 接受一个包含以下字段的文档:
Field | Description |
---|---|
from | 指定同一数据 库中的 foreign collection,以 join 到 local collection 中。foreign collection 不能被分片(sharded)(参阅 Sharded Collction Resctrictions 分片集合限制)。 |
localField | 指定 local document 的 localField 与 foreign document 的 foreignField 进行相等匹配。如果一个 local document 不包含 localField 的值,$lookup 会使用一个空(null)值进行匹配。 |
foreignField | 指定 foreign document 的 foreignField 与 local document 的 localField 进行相等匹配。如果一个 foreign document 不包含 foreignField 的值,$lookup 会使用一个空(null)值进行匹配。 |
let | 可选的。指定在 pipeline stage 使用的变量。使用 variable expressions 来访问输入到 pipeline 中的文档字段。> NOTE |
要在 pipeline stage 引用变量,使用语法 “$$
“。 let 变量可以被 pipeline 中的 stage 访问,包括嵌套(nested)在
pipeline
中的额外(additional) $lookup stages。
- 一个 $match stage 需要使用 $expr operator 来访问变量。$expr operator 允许在 $match syntax 内部使用 aggregation expressions。
- 从 MongoDB 5.0 开始,放置在 $expr operator 中的 $eq、$lt、$lte、$gt 和 $gte comparison operators 可以使用 $lookup stage 中引用的
from
collection 上的索引(index)。限制条件:
- 不使用 Multikey indexes(多键索引)
- 在
运算对象为数组
或运算对象类型为 undefined
的情况下,不使用索引进行比较(comparison)。- 索引不用于与多个 field path 运算对象的比较(comparison)。
- pipeline 中的其他(非 $match)stage 不需要 $expr operator 来访问这些变量。
|
| pipeline | 指定在 foreign collection 上运行的 pipeline
。该 pipeline
从 foreign collection 中返回文档。要返回所有的文档,请指定一个 empty pipeline``[]
。pipeline
不能包括 $out 或 $merge stages。pipeline
不能直接访问文档字段。取而代之的是,使用 let 选项为文档字段定义变量,然后在 pipeline stages 引用这些变量。> NOTE
要在 pipeline stage 引用变量,使用语法 “$$
“。 let 变量可以被 pipeline 中的 stage 访问,包括嵌套(nested)在
pipeline
中的额外(additional) $lookup stages。
- 一个 $match stage 需要使用 $expr operator 来访问变量。$expr operator 允许在 $match syntax 内部使用 aggregation expressions。
- 从 MongoDB 5.0 开始,放置在 $expr operator 中的 $eq、$lt、$lte、$gt 和 $gte comparison operators 可以使用 $lookup stage 中引用的
from
collection 上的索引(index)。限制条件:
- 不使用 Multikey indexes(多键索引)
- 在
运算对象为数组
或运算对象类型为 undefined
的情况下,不使用索引进行比较(comparison)。- 索引不用于与多个 field path 运算对象的比较(comparison)。
- pipeline 中的其他(非 $match)stage 不需要 $expr operator 来访问这些变量。
| | as | 指定新的数组字段的名称,以添加到 foreign document 中。新的数组字段包含了 foreign collection 中的匹配文档。如果指定的名称已经存在于 foreign document 中,那么现有的字段将被覆盖(overwritten)。 |
该 operation 对应以下 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(管道匹配条件)>
)
请参阅以下示例:
Perform a Concise Correlated Subquery with $lookup 用
$lookup
执行一个简洁的关联子查询Considerations 注意事项
Views and Collation 视图和排序规则
如果执行涉及 multiple views 的 aggregation,比如用 $lookup 或 $graphLookup,views 必须有相同的 collation。
Restrictions 限制
在 4.2 版本中更改: 您不能在 $lookup stage 中包含 $out 或 $merge stage。 也就是说,当指定一个 pipeline for the joined collection(连接集合的管道),您不能在
pipeline
字段中包含这两个 stage。{
$lookup:
{
from: <collection to join>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to execute on the joined collection> ], // Cannot include $out or $merge
as: <output array field>
}
}
Sharded Collection Restrictions 分片集合限制
在 $lookup stage,
from
collection 不能被分片(sharded)。然而,你运行 aggregate() 方法的 collection 可以是分片(sharded)的。具体来说,在这个例子中:db.collection.aggregate([
{ $lookup: { from: "fromCollection", ... } }
])
collection
可以分片(can be sharded)。fromCollection
不能分片(cannot be sharded)。
要将一个 sharded collection 与一个 unsharded collection 连接起来,在 sharded collection 上运行 aggregation,然后 lookup unsharded collection。比如说:
db.shardedCollection.aggregate([
{ $lookup: { from: "unshardedCollection", ... } }
])
或者要 join multiple sharded collections(加入多个分片集合),可以考虑:
- 修改客户端应用程序以执行手动查找(manual lookups),而不是使用 $lookup aggregation stage。
- 如果可能的话,使用一个嵌入式(embedded)的数据模型,消除 join collection 的需要。
- 如果可能的话,使用 Atlas Data Lake 的 $lookup pipeline stage 来查询(lookup)一个分片的集合(a sharded collection)。
Example 例子
Perform a Single Equality Join with
用这些文档创建一个$lookup
用$lookup
执行单一相等连接orders
collection:
再用这些文档创建另外一个db.orders.insertMany( [
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
{ "_id" : 3 }
] )
inventory
collection: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 }
] )
orders
collection 上的以下 aggregation operation,使用orders
collection 中的item
字段和inventory
collection 中的sku
字段,将orders
的文档和inventory
collection 的文档连接在一起:
该 operation 返回以下文档:db.orders.aggregate( [
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
] )
该 operation 对应以下的 pseudo-SQL statement(伪 SQL 语句):{
"_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 }
]
}
select *, inventory_docs
from orders
where inventory_docs in (
select *
from inventory
where sku = orders.item
)
Use
如果$lookup
with an Array 在数组中使用$lookup
localField
是一个数组(array),你可以将数组元素(array elements)与标量(scalar)的foreignField
进行匹配,而不需要 $unwind stage。
例如,用以下文档创建一个 classes
collection:
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"] }
] )
再用这些文档创建另外一个 members
collection:
db.members.insertMany( [
{ _id: 1, name: "artie", joined: new Date("2016-05-01"), status: "A" },
{ _id: 2, name: "giraffe", joined: new Date("2017-05-01"), status: "D" },
{ _id: 3, name: "giraffe1", joined: new Date("2017-10-01"), status: "A" },
{ _id: 4, name: "panda", joined: new Date("2018-10-11"), status: "A" },
{ _id: 5, name: "pandabear", joined: new Date("2018-12-01"), status: "A" },
{ _id: 6, name: "giraffe2", joined: new Date("2018-12-01"), status: "D" }
] )
以下 aggregation operation 将 classes
collection 中的文档与 members
colleciton 连接起来,在 members
字段上用 name
字段进行匹配:
db.classes.aggregate( [
{
from: "members",
localField: "enrollmentList",
foreignField: "name",
as: "enrollee_info"
}
] )
该 operation 返回以下内容:
{
"_id" : 1,
"title" : "Reading is ...",
"enrollmentlist" : [ "giraffe2", "pandabear", "artie" ],
"days" : [ "M", "W", "F" ],
"enrollee_info" : [
{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },
{ "_id" : 5, "name" : "pandabear", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "A" },
{ "_id" : 6, "name" : "giraffe2", "joined" : 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", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },
{ "_id" : 3, "name" : "giraffe1", "joined" : ISODate("2017-10-01T00:00:00Z"), "status" : "A" }
]
}
Use $lookup
with $mergeObjects
将 $lookup
和 $mergeObjects
一起使用
$mergeObjects operator 将多个文档合并成一个文档。
用以下文档创建一个 orders
collection:
db.orders.insertMany( [
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
] )
再用这些文档创建另一个 items
collection:
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 }
] )
下面的 operation 首先使用 $lookup stage,通过 item
字段连接两个 collection,然后在 $replaceRoot 中使用 $mergeObjects 合并来自 items
和 orders
的连接文档:
db.orders.aggregate( [
{
$lookup: {
from: "items",
localField: "item",
foreignField: "item",
as: "fromItems"
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
{
$arrayElemAt: [ "$fromItems", 0 ]
},
"$$ROOT"
]
}
}
},
{
$project: { fromItems: 0 }
}
] )
该 operation 返回以下文档:
{
_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
}
Perform Multiple Joins and a Correlated Subquery with $lookup
用 $lookup
执行多个连接和一个关联的子查询
MongoDB 3.6 增加了对在一个 joined collection 上执行 pipeline 的支持,并允许多个 join condition。
join condition 可以引用运行 aggregate() 方法的 local collection 中的字段,并引用 joined collection 中的字段。 这允许两个 collection 之间的 correlated subquery(关联子查询)。
MongoDB 5.0 还支持 consise correlated subqueries(简洁的关联子查询)。
用以下文档创建一个 orders
collection:
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 }
] )
再用这些文档创建另一个 warehouses
collection:
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 }
] )
下面的例子:
- 在
orders.item
和warehouses.stock_item
字段上使用 correlated subquery(关联子查询)进行 join。 确保库存商品的数量(quantity of the item in stock)可以满足订购的数量(ordered quantity)。
db.orders.aggregate( [
{
$lookup:
{
from: "warehouses",
let: { order_item: "$item", order_qty: "$ordered" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$stock_item", "$$order_item" ] },
{ $gte: [ "$instock", "$$order_qty" ] }
]
}
}
},
{ $project: { stock_item: 0, _id: 0 } }
],
as: "stockdata"
}
}
] )
该 operation 返回以下文档:
{
_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 } ]
}
该 operation 对应以下 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
)
从 MongoDB 5.0 开始,放置在 $expr operator 中的 $eq、$lt、$lte、$gt 和 $gte comparison operators 可以使用 $lookup stage 中引用的
from
collection 上的索引(index)。 限制条件:- 在
运算对象为数组
或运算对象类型为 undefined
的情况下,不使用索引进行比较(comparison)。 - 索引不用于与多个 field path 运算对象的比较(comparison)。
例如,如果索引 { stock_item: 1, stock: 1 }
存在于 warehouses
collection 上:
warehouses.stock_item
字段上的相等匹配使用索引。- 对
warehouses.instock
字段的范围查询部分也使用复合索引中的索引字段。
TIP
参阅:
- $expr
- Variables in Aggregation Expressions(聚合表达式中的变量)
Perform an Uncorrelated Subquery with $lookup
用 $lookup
执行一个不关联的子查询
一个 aggregation pipeline $lookup stage 可以在 joined collection 上执行 pipeline,它允许 uncorrelated subquery。 一个 uncorrelated subquery 不引用 joined document 字段。
NOTE 从 MongoDB 5.0 开始,对于包含 $sample stage、$sampleRate operator 或 $rand operator 的 $lookup pipeline stage 中的 uncorrelated subquery,如果重复,subquery 总是被再次运行。 以前,根据 subquery 的输出大小,要么缓存 subquery 的输出,要么再次运行 subquery。
用以下文档创建一个 absences
collection:
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") ] },
] )
再用这些文档创建另一个 holidays
collection:
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") }
] )
以下 operation 将 absences
collection 与 holidays
collection 中的 2018 年假期的信息连接在一起:
db.absences.aggregate( [
{
$lookup:
{
from: "holidays",
pipeline: [
{ $match: { year: 2018 } },
{ $project: { _id: 0, date: { name: "$name", date: "$date" } } },
{ $replaceRoot: { newRoot: "$date" } }
],
as: "holidays"
}
}
] )
该 operation 返回以下内容:
{
_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")
}
]
}
该 operation 对象以下 pseudo-SQL statement(伪 SQL 语句):
select *, holidays
from absences
where holidays in (
select name, date
from holidays
where year = 2018
)
Perform a Concise Correlated Subquery with $lookup
用 $lookup
执行一个简洁的关联子查询
从 MongoDB 5.0 开始,aggregation pipeline $lookup stage 支持 concise correlated subquery syntax(简洁的关联子查询语法),可改进 collection 之间的 join。 新的 concise syntax 消除了对 $match stage 中对 $expr operator 内部的 foreign 和 local 字段进行相等匹配的要求。
创建一个 restaurants
collection:
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" ]
}
] )
创建另一个带有食物和可选饮料的 orders
collection:
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"
}
] )
下面例子的含义:
- 通过匹配 localField
orders.restaurant_name
与 foreignFieldrestaurant.name
来 joinorders
和restaurants
collection。 匹配在 pipeline 运行之前执行。 - 在
orders.drink
和restaurants.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"
}
}
] )
orders.drink
和restaurant.beverages
字段中的soda
值匹配。 这个输出显示了匹配(matches)
数组,包含了所有来自restaurants
collection 的匹配字段:
在引入 concise correlated subqueries 之前,您必须在{
"_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" ]
} ]
}
pipeline
$lookup stage 的 $expr operator 中使用 local field 和 joined field 之间的 $eq 相等匹配,如 Perform Multiple Joins and a Correlated Subquery with $lookup(使用 $lookup 执行多个连接和关联子查询中所示)。
此示例使用 5.0 之前的 MongoDB 版本中较旧的详细语法,并返回与前面的 concise 示例相同的结果:
前面的例子对应这个 pseudo-SQL statement(伪 SQL 语句):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"
}
}
] )
SELECT *, matches
FROM orders
WHERE matches IN (
SELECT *
FROM restaurants
WHERE restaurants.name = orders.restaurant_name
// 这里应该是官方写错了,因为 beverages 是一个数组,而 drink 是一个字符串,应该是 orders.drink in restaurants.beverages
AND restaurants.beverages = orders.drink
);
参考
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup