基于TypeScript的Mongoose如何用$lookup关联用户购物袋商品ID并投影数据
问题修复与正确实现
原有代码核心问题
- 关联逻辑错误:两次
$lookup的localField均错误使用用户_id,未正确关联inBagProducts字段存储的商品ID数组,且重复的$lookup逻辑完全冗余 - 字段读取路径错误:
$group阶段直接读取$category等字段,实际该类字段嵌套在products对象下,无法直接取到值 - 类型定义错误:声明的
product类型错误引用了用户表字段,未匹配商品表结构 - 存在冗余聚合阶段,无需多余的字段分组逻辑
正确实现代码
类型定义
import mongoose, { Types } from 'mongoose'; const ObjectId = mongoose.Types.ObjectId; // 定义商品返回字段的类型,匹配需要投影的字段 interface IBagProduct { _id: Types.ObjectId; category: number; gender: number; title: string; description: string; price: number; imageFileName: string; } // 聚合返回结果类型 interface IAggregateResult { products: IBagProduct[]; productsCounter: number; }
聚合查询代码(保留inBagProducts中重复的商品记录)
如果需要保留用户购物袋中重复添加的同一款商品,使用以下写法:
const result = await UserDB.aggregate<IAggregateResult>([ // 匹配目标用户 { $match: { _id: ObjectId(req.userId) }, }, // 展开商品ID数组,保留重复ID { $unwind: '$inBagProducts', }, // 关联商品表,匹配每个商品ID对应的详情 { $lookup: { from: 'products', localField: 'inBagProducts', foreignField: '_id', as: 'productDetail', }, }, // 展开关联得到的商品详情($lookup返回数组,每个ID只会匹配一个商品所以直接展开) { $unwind: '$productDetail', }, // 按商品创建时间倒序排列 { $sort: { 'productDetail.createdAt': -1 }, }, // 投影需要的商品字段 { $project: { _id: 0, product: { _id: '$productDetail._id', category: '$productDetail.category', gender: '$productDetail.gender', title: '$productDetail.title', description: '$productDetail.description', price: '$productDetail.price', imageFileName: '$productDetail.imageFileName', }, }, }, // 把所有商品合并为数组,统计总数 { $group: { _id: null, products: { $push: '$product' }, productsCounter: { $sum: 1 }, }, }, ]); // 最终拿到的商品数组与总数 const bagProducts = result[0]?.products ?? []; const total = result[0]?.productsCounter ?? 0;
聚合查询代码(自动去重重复商品)
如果不需要保留重复添加的同一款商品,可以使用简化写法:
const result = await UserDB.aggregate<IAggregateResult>([ { $match: { _id: ObjectId(req.userId) }, }, // 直接关联商品ID数组,自动去重匹配 { $lookup: { from: 'products', localField: 'inBagProducts', foreignField: '_id', as: 'products', }, }, // 展开商品数组排序 { $unwind: '$products' }, { $sort: { 'products.createdAt': -1 } }, // 投影指定字段 { $project: { _id: 0, 'products._id': 1, 'products.category': 1, 'products.gender': 1, 'products.title': 1, 'products.description': 1, 'products.price': 1, 'products.imageFileName': 1, }, }, // 合并为数组,统计总数 { $group: { _id: null, products: { $push: '$products' }, productsCounter: { $sum: 1 }, }, }, ]);
内容的提问来源于stack exchange,提问作者user16657995
相关产品推荐
相关产品推荐

