You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于用户尺码排序商品列表?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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 21:50:51