多表关联($lookup)
多表关联($lookup)
MongoDB作为文档型数据库,通常以嵌套文档的形式存储关联数据。但在实际业务中,数据关联关系复杂时,仍需要类似SQL中的JOIN操作。$lookup
阶段提供了跨集合关联查询的能力,允许在聚合管道中将其他集合的文档引入当前处理流。
$lookup基本语法
$lookup
操作符的基本语法结构如下:
{
$lookup: {
from: "<目标集合>",
localField: "<输入文档的字段>",
foreignField: "<目标集合的字段>",
as: "<输出数组字段>"
}
}
典型示例:关联订单和产品集合
db.orders.aggregate([
{
$lookup: {
from: "products",
localField: "product_id",
foreignField: "_id",
as: "product_details"
}
}
])
关联条件进阶
多字段关联
当需要多个字段组合作为关联条件时,可以使用let
和pipeline
参数:
db.orders.aggregate([
{
$lookup: {
from: "inventory",
let: {
productId: "$product_id",
warehouse: "$warehouse"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$product", "$$productId"] },
{ $eq: ["$location", "$$warehouse"] }
]
}
}
}
],
as: "inventory_items"
}
}
])
嵌套文档关联
处理嵌套文档中的字段关联:
db.users.aggregate([
{
$lookup: {
from: "departments",
localField: "employment.department_id",
foreignField: "_id",
as: "department_info"
}
}
])
关联结果处理
展开关联数组
使用$unwind
展开关联结果:
db.orders.aggregate([
{ $lookup: { ... } },
{ $unwind: "$product_details" }
])
关联结果过滤
在管道中对关联结果进行二次筛选:
db.orders.aggregate([
{
$lookup: {
from: "products",
let: { pid: "$product_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$_id", "$$pid"] } } },
{ $project: { name: 1, price: 1 } }
],
as: "product_info"
}
}
])
性能优化策略
索引设计
确保关联字段已建立索引:
db.products.createIndex({ _id: 1 })
db.orders.createIndex({ product_id: 1 })
分片集合关联
跨分片集合关联时需考虑:
db.orders.aggregate([
{
$lookup: {
from: "products",
localField: "product_id",
foreignField: "_id",
as: "matched_products",
shardedCollection: true // 显式声明目标为分片集合
}
}
])
复杂关联场景
多级关联查询
实现三级关联(用户→订单→产品):
db.users.aggregate([
{
$lookup: {
from: "orders",
localField: "_id",
foreignField: "user_id",
as: "user_orders"
}
},
{ $unwind: "$user_orders" },
{
$lookup: {
from: "products",
localField: "user_orders.product_id",
foreignField: "_id",
as: "ordered_products"
}
}
])
自引用关联
处理树形结构数据:
db.categories.aggregate([
{
$lookup: {
from: "categories",
localField: "_id",
foreignField: "parent_id",
as: "children"
}
}
])
替代方案比较
应用层JOIN
在Node.js中实现类似功能:
async function getOrdersWithProducts() {
const orders = await db.collection('orders').find().toArray();
const productIds = orders.map(o => o.product_id);
const products = await db.collection('products')
.find({ _id: { $in: productIds } })
.toArray();
return orders.map(order => ({
...order,
product: products.find(p => p._id.equals(order.product_id))
}));
}
引用设计模式
预存储关联数据示例:
// 订单文档设计
{
_id: ObjectId("..."),
user_id: ObjectId("..."),
products: [
{
product_id: ObjectId("..."),
name: "手机",
price: 3999
}
]
}
实际案例分析
电商平台订单查询实现:
db.orders.aggregate([
{ $match: { status: "completed" } },
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user_info"
}
},
{ $unwind: "$user_info" },
{
$lookup: {
from: "products",
localField: "items.product_id",
foreignField: "_id",
as: "product_list"
}
},
{
$project: {
order_id: 1,
total: 1,
"user_info.name": 1,
"user_info.email": 1,
products: {
$map: {
input: "$items",
as: "item",
in: {
$mergeObjects: [
"$$item",
{
$arrayElemAt: [
{
$filter: {
input: "$product_list",
cond: { $eq: ["$$this._id", "$$item.product_id"] }
}
},
0
]
}
]
}
}
}
}
}
])
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn