如何基于用户尺码排序商品列表?Mongoose实现方案
解决方案
要实现「匹配尺码的商品前置,非匹配商品后置且不过滤」的需求,不能用普通的find+条件筛选,得靠MongoDB的聚合管道来实现自定义排序逻辑。具体步骤如下:
1. 建立类别-尺码字段映射表
先定义一个映射关系,方便根据当前请求的category快速找到对应用户的尺码字段:
// 类别与用户尺码字段的映射,根据实际业务调整 const categorySizeMap = { 'shorts': 'short_size', 'pants': 'pant_size', 'shoes': 'shoes_size', // 其他类别按需添加 };
2. 使用聚合管道实现排序+筛选+分页
替换原有的Product.find()逻辑,改用聚合管道,核心是通过$addFields新增一个标记字段,标记商品是否匹配用户尺码,再按这个字段排序:
async getProducts(req, res, next) { let { page = 1, brand, category, title, limit = 10 } = req.query; page = parseInt(page); limit = parseInt(limit); try { // 获取用户尺码信息 const { size_interests } = await UserModel.findById(req.session.userId); if (!size_interests) { // 无尺码信息时直接返回普通排序结果 const products = await Product.find({ ...(brand && { brand }), ...(category && { category }), ...(title && { title: { $regex: title, $options: 'i' } }) }) .skip((page - 1) * limit) .limit(limit) .populate("images") .sort({ createdAt: -1 }); // 默认按创建时间排序 return res.json(products); } // 根据类别获取对应的用户尺码字段 const userSizeField = categorySizeMap[category]; const userTargetSize = size_interests[userSizeField]; // 聚合管道 const pipeline = []; // 1. 基础筛选(品牌、类别、标题) const matchStage = {}; if (brand) matchStage.brand = brand; if (category) matchStage.category = category; if (title) matchStage.title = { $regex: title, $options: 'i' }; pipeline.push({ $match: matchStage }); // 2. 添加匹配标记字段:isSizeMatched(1表示匹配,0表示不匹配) if (userTargetSize) { pipeline.push({ $addFields: { isSizeMatched: { $cond: { if: { $eq: ['$size', userTargetSize] }, then: 1, else: 0 } } } }); // 3. 按匹配优先级排序,匹配的排前面,再按其他字段(比如创建时间)排序 pipeline.push({ $sort: { isSizeMatched: -1, createdAt: -1 } }); } else { // 无对应尺码字段时,用默认排序 pipeline.push({ $sort: { createdAt: -1 } }); } // 4. 分页处理 pipeline.push( { $skip: (page - 1) * limit }, { $limit: limit } ); // 5. 替代populate:关联images集合(如果images是单独的Model) pipeline.push({ $lookup: { from: 'images', // 注意这里是images集合的名称(Mongoose默认是模型名小写复数) localField: 'images', foreignField: '_id', as: 'images' } }); // 执行聚合 const products = await Product.aggregate(pipeline); res.json(products); } catch (err) { next(err); } }
关键逻辑说明
- 自定义排序:通过
$addFields新增isSizeMatched字段,用$cond判断商品size是否等于用户对应类别的尺码,匹配则设为1,否则0,再按这个字段倒序排序,就能让匹配的商品排在最前面。 - 不过滤非匹配商品:全程没有在
$match里加尺码筛选条件,所以所有符合基础筛选的商品都会保留,只是排序调整。 - 动态尺码字段:通过
categorySizeMap映射表,根据请求的category自动选择用户的尺码字段,无需硬编码判断。 - 聚合替代populate:因为聚合管道不能直接用Mongoose的
populate,所以用$lookup来关联images集合,效果和populate一致。
内容的提问来源于stack exchange,提问作者David Iss
相关产品推荐
相关产品推荐

