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

基于Express+MongoDB实现多颜色多尺码商品筛选API改造

实现多颜色/多尺码的商品筛选功能(Express+MongoDB)

现有基于Express和MongoDB搭建的服装商城后端,getAllProducts接口当前无法支持多颜色(如请求参数color=green,blue)和多尺码(如size=26,28)的筛选需求,以下是改造后的完整实现方案:

原问题代码回顾

原getAllProducts接口

const getAllProducts = async (req: Request, res: Response) => {
  const { page, limit, search, color, size, category, brand } = req?.query;

  let currentPage = Number(page) || 0;
  let docLimit = Number(limit) || 10;
  let searchDoc = search || '';

  try {
    const products = await Product.aggregate([
      {
        $lookup: {
          from: 'stores',
          localField: 'store',
          foreignField: '_id',
          as: 'store',
        },
      },
      {
        $match: {
          $or: [
            {
              title: { $regex: searchDoc, $options: 'i' },
            },
            {
              'store.name': { $regex: searchDoc, $options: 'i' },
            },
          ],
          ...(color ? { color: { $regex: color, $options: 'i' } } : {}),
          ...(brand ? { 'store.name': { $regex: brand, $options: 'i' } } : {}),
          ...(size
            ? {
                size: {
                  $in: [Number(size)],
                },
              }
            : {}),
          ...(category ? { $expr: { $eq: ['$category', category] } } : {}),
        },
      },
      {
        $skip: currentPage * docLimit,
      },
      {
        $limit: docLimit,
      },
    ]);

    const total = await Product.find().countDocuments();

    const data = {
      products,
      total,
    };

    res.status(200).json({
      statusCode: 200,
      success: true,
      message: 'fetch successfully',
      data,
    });
  } catch (error) {
    if (error instanceof Error) {
      res.status(500).json({
        statusCode: 500,
        success: false,
        message: error.message,
        error,
      });
    }
  }
};

Product Schema

const productSchema = new mongoose.Schema<IProduct>(
  {
    title: {
      type: String,
      required: true,
    },
    description: {
      type: String,
    },
    color: {
      type: [String],
      required: true,
      enum: [
        'black',
        'white',
        'blue',
        'brown',
        'copper',
        'gold',
        'green',
        'grey',
        'navy',
        'pink',
        'orange',
      ],
    },
    price: {
      type: Number,
      required: true,
    },
    discount: {
      type: Number,
      default: 0,
    },
    rating: {
      type: Number,
      default: 0,
    },
    category: {
      type: String,
      enum: [
        'shirts',
        'jeans',
        'jacket',
        'top',
        'skirts',
        'pants',
        'dresses',
        't-shirts',
        'hats',
        'socks',
      ],
    },
    store: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'stores',
    },
    size: {
      type: [Number],
    },
    quantity: {
      type: Number,
      default:5
    },
    productImg: [
      {
        src: {
          type: String,
        },
      },
    ],
  },
  { timestamps: true }
);

改造后的实现代码

const getAllProducts = async (req: Request, res: Response) => {
  const { page, limit, search, color, size, category, brand } = req?.query;

  let currentPage = Number(page) || 0;
  let docLimit = Number(limit) || 10;
  let searchDoc = search || '';

  // 构建统一的筛选条件对象
  const matchConditions: any = {
    $or: [
      { title: { $regex: searchDoc, $options: 'i' } },
      { 'store.name': { $regex: searchDoc, $options: 'i' } }
    ]
  };

  // 处理多颜色筛选:分割参数为数组,用$in匹配
  if (color) {
    const colorArray = (color as string).split(',').map(c => c.trim().toLowerCase());
    matchConditions.color = { $in: colorArray };
  }

  // 处理多尺码筛选:分割参数为数字数组,用$in匹配
  if (size) {
    const sizeArray = (size as string).split(',').map(s => Number(s.trim())).filter(num => !isNaN(num));
    matchConditions.size = { $in: sizeArray };
  }

  // 品牌筛选
  if (brand) {
    matchConditions['store.name'] = { $regex: brand as string, $options: 'i' };
  }

  // 分类筛选
  if (category) {
    matchConditions.category = category as string;
  }

  try {
    const products = await Product.aggregate([
      {
        $lookup: {
          from: 'stores',
          localField: 'store',
          foreignField: '_id',
          as: 'store',
        },
      },
      { $match: matchConditions },
      { $skip: currentPage * docLimit },
      { $limit: docLimit },
    ]);

    // 复用筛选条件计算准确的总条数
    const total = await Product.aggregate([
      { $lookup: { from: 'stores', localField: 'store', foreignField: '_id', as: 'store' } },
      { $match: matchConditions },
      { $count: 'total' }
    ]).then(result => result[0]?.total || 0);

    const data = {
      products,
      total,
    };

    res.status(200).json({
      statusCode: 200,
      success: true,
      message: '获取成功',
      data,
    });
  } catch (error) {
    if (error instanceof Error) {
      res.status(500).json({
        statusCode: 500,
        success: false,
        message: error.message,
        error,
      });
    }
  }
};

关键改动说明

  1. 多颜色参数处理

    • 将逗号分隔的color参数分割为数组,统一转小写匹配Schema枚举值,使用MongoDB的$in操作符匹配商品颜色数组中包含任一指定颜色的文档,替换原低效的正则匹配。
  2. 多尺码参数处理

    • 分割size参数为数组,转换为数字类型并过滤无效值,用$in操作符匹配商品尺码数组中包含任一指定尺码的文档,修复原仅支持单个尺码的问题。
  3. 总条数计算修复

    • 原代码直接统计所有商品总数,与筛选后分页数据不匹配。改造后复用聚合管道的筛选条件,通过$count获取准确的筛选后总条数。
  4. 筛选条件重构

    • 将分散的筛选逻辑整合到matchConditions对象中,代码更清晰易维护。

内容的提问来源于stack exchange,提问作者Marchant Khalid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:55:57