如何在 MongoDB 聚合管道中执行嵌套 "joins"(加入 3 个或更多集合)?
How can I perform nested "joins" (joining 3 or more collections) in a MongoDB aggregation pipeline?
假设我们在 MongoDB 中有 3 个假设的集合:customers
、orders
和 orderItems
。
每个客户有多个订单,每个订单有多个订单商品。
以下是这 3 个集合的一些样本数据:
客户
[
{
customer_id: 1,
name: "Jim Smith",
email: "jim.smith@example.com"
},
{
customer_id: 2,
name: "Bob Jones",
email: "bob.jones@example.com"
}
]
订单
[
{
order_id: 1,
customer_id: 1
},
{
order_id: 2,
customer_id: 1
}
]
订单商品
[
{
order_item_id: 1,
name: "Foo",
price: 4.99,
order_id: 1
},
{
order_item_id: 2,
name: "Bar",
price: 17.99,
order_id: 1
},
{
order_item_id: 3,
name: "baz",
price: 24.99,
order_id: 2
}
]
想要的结果
如何编写我的聚合管道以使返回的结果看起来像这样?
[
{
customer_id: 1,
name: "Jim Smith",
email: "jim.smith@example.com"
orders: [
{
order_id: 1,
items: [
{
name: "Foo",
price: 4.99
},
{
name: "Bar",
price: 17.99
}
]
},
{
order_id: 2,
items: [
{
name: "baz",
price: 24.99
}
]
}
]
},
{
customer_id: 2,
name: "Bob Jones",
email: "bob.jones@example.com"
orders: []
}
]
进行嵌套查找
$lookup
与 orders
collection,
let
,定义来自主 collection 的变量 customer_id
,使用 $$
像 $$customer_id
,
pipeline
可以像我们在根级管道中一样添加管道阶段
$expr
每当我们匹配内部字段时它需要表达式匹配条件,所以 $$customer_id
是 parent collection 在 let
和 [= 中声明的字段21=]是childcollection's/currentcollection的字段
$lookup
与 orderitems
collection
db.customers.aggregate([
{
$lookup: {
from: "orders",
let: { customer_id: "$customer_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$$customer_id", "$customer_id"] } } },
{
$lookup: {
from: "orderitems",
localField: "order_id",
foreignField: "order_id",
as: "items"
}
}
],
as: "orders"
}
}
])
Tip:
Several joins considered as bad practice in NoSQL, I would suggest if you could add your order items in orders collection as array, you can save one join process for orderitems, see improved version in playground
假设我们在 MongoDB 中有 3 个假设的集合:customers
、orders
和 orderItems
。
每个客户有多个订单,每个订单有多个订单商品。
以下是这 3 个集合的一些样本数据:
客户
[
{
customer_id: 1,
name: "Jim Smith",
email: "jim.smith@example.com"
},
{
customer_id: 2,
name: "Bob Jones",
email: "bob.jones@example.com"
}
]
订单
[
{
order_id: 1,
customer_id: 1
},
{
order_id: 2,
customer_id: 1
}
]
订单商品
[
{
order_item_id: 1,
name: "Foo",
price: 4.99,
order_id: 1
},
{
order_item_id: 2,
name: "Bar",
price: 17.99,
order_id: 1
},
{
order_item_id: 3,
name: "baz",
price: 24.99,
order_id: 2
}
]
想要的结果
如何编写我的聚合管道以使返回的结果看起来像这样?
[
{
customer_id: 1,
name: "Jim Smith",
email: "jim.smith@example.com"
orders: [
{
order_id: 1,
items: [
{
name: "Foo",
price: 4.99
},
{
name: "Bar",
price: 17.99
}
]
},
{
order_id: 2,
items: [
{
name: "baz",
price: 24.99
}
]
}
]
},
{
customer_id: 2,
name: "Bob Jones",
email: "bob.jones@example.com"
orders: []
}
]
$lookup
与orders
collection,let
,定义来自主 collection 的变量customer_id
,使用$$
像$$customer_id
,pipeline
可以像我们在根级管道中一样添加管道阶段$expr
每当我们匹配内部字段时它需要表达式匹配条件,所以$$customer_id
是 parent collection 在let
和 [= 中声明的字段21=]是childcollection's/currentcollection的字段
$lookup
与orderitems
collection
db.customers.aggregate([
{
$lookup: {
from: "orders",
let: { customer_id: "$customer_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$$customer_id", "$customer_id"] } } },
{
$lookup: {
from: "orderitems",
localField: "order_id",
foreignField: "order_id",
as: "items"
}
}
],
as: "orders"
}
}
])
Tip:
Several joins considered as bad practice in NoSQL, I would suggest if you could add your order items in orders collection as array, you can save one join process for orderitems, see improved version in playground