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

如何在Mongoose中按自定义价格规则对产品文档升序排序?

需求说明

我有一个产品模型,其中price是可选字段——因为产品可能存在变体,此时价格会在变体中指定。需要实现产品按价格升序排序,规则如下:

  • 若产品存在变体,则取变体中的最低价格参与比较
  • 若无变体,则使用产品自身的price字段值参与比较

产品Schema代码

interface IProductVariation extends IMongooseId { 
  price: number;
  //... 其他属性
}

interface IProduct extends IMongooseId {
  /* price是可选的,因为如果存在变体,价格会在变体中指定 */
  price?: number;
  variations: IProductVariation[];
  //...其他属性  
}

const variationSchema = new Schema<IProductVariation>({
  price: { type: Number, required: true },
  // ...
});

const productSchema = new Schema<IProduct>({
  price: Number,
  variations: [variationSchema],
  // ...
});

待实现排序逻辑的查询方法

public async getPaginatedData(
  page: number,
  perPage: number,
  query: FilterQuery<IProduct> = {},
  sort?: FilterQuery<IProduct>
) {
  return this.model
    .find(query)
    .limit(perPage)
    .skip((page - 1) * perPage)
    .sort(sort);
}

解决方案

普通的sort方法无法动态计算每个产品的有效排序价格,需要通过MongoDB聚合管道实现需求:

基础实现(仅支持价格升序/降序排序)

直接修改查询方法为聚合方式,动态计算每个产品的有效价格并排序:

public async getPaginatedData(
  page: number,
  perPage: number,
  query: FilterQuery<IProduct> = {},
  sortDirection: 1 | -1 = 1
) {
  const pipeline = [
    // 匹配查询条件
    { $match: query },
    // 计算用于排序的有效价格
    {
      $addFields: {
        effectivePrice: {
          $cond: {
            // 判断变体数组是否非空
            if: { $gt: [{ $size: "$variations" }, 0] },
            // 取变体中的最低价格
            then: { $min: "$variations.price" },
            // 无变体则用自身price字段
            else: "$price"
          }
        }
      }
    },
    // 按有效价格排序
    { $sort: { effectivePrice: sortDirection } },
    // 分页处理
    { $skip: (page - 1) * perPage },
    { $limit: perPage },
    // 移除临时计算的有效价格字段(可选)
    { $unset: "effectivePrice" }
  ];

  return this.model.aggregate(pipeline);
}

兼容原有sort参数的实现

如果需要保留原方法的sort参数,兼容其他字段的排序需求,可以做分支处理:

public async getPaginatedData(
  page: number,
  perPage: number,
  query: FilterQuery<IProduct> = {},
  sort?: FilterQuery<IProduct>
) {
  // 判断是否是按price字段排序的需求
  const isPriceSort = sort && Object.keys(sort).includes('price');
  
  if (isPriceSort) {
    const sortDir = sort.price === -1 ? -1 : 1;
    const pipeline = [
      { $match: query },
      {
        $addFields: {
          effectivePrice: {
            $cond: {
              if: { $gt: [{ $size: "$variations" }, 0] },
              then: { $min: "$variations.price" },
              else: "$price"
            }
          }
        }
      },
      { $sort: { effectivePrice: sortDir } },
      { $skip: (page - 1) * perPage },
      { $limit: perPage },
      { $unset: "effectivePrice" }
    ];
    return this.model.aggregate(pipeline);
  } else {
    // 其他排序需求走原有逻辑
    return this.model
      .find(query)
      .limit(perPage)
      .skip((page - 1) * perPage)
      .sort(sort);
  }
}

关键逻辑说明

  • $addFields:新增临时字段effectivePrice存储每个产品的排序依据价格
  • $cond:根据变体数组的长度做条件判断,选择排序用的价格来源
  • $min: "$variations.price":MongoDB支持直接从数组的子字段中提取最小值,无需额外遍历

内容的提问来源于stack exchange,提问作者Dima Kambalin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:56:26