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

如何在MongoDB中按物品被添加到库存的次数排序?

按物品被添加到库存的次数排序(无计数器字段方案)

问题背景

我正在开发一款库存管理应用,物品与库存为多对多关系,Mongoose Schema如下:

const itemSchema = mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  emoji: {
    type: String,
    required: true,
  },
  createdBy: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    default: null,
  },
  category: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Category',
  },
});

const inventorySchema = mongoose.Schema({
  owner: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
  },
  access: {
    type: String,
    enum: ['PRIVATE', 'PUBLIC'],
    default: 'PRIVATE',
  },
  category: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Category',
  },
  items: [
    {
      item: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Item',
      },
      quantity: {
        type: Number,
        default: 0,
      },
    },
  ],
  sharedWith: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Group',
    },
  ],
});

需求:将所有物品按被添加到所有库存的次数从多到少排序,且不想在Item模型中添加计数器字段。


解决方案

通过MongoDB的聚合管道直接从Inventory集合统计物品出现次数,无需修改Item模型。核心思路是拆分数组、分组统计、关联详情、排序:

完整Mongoose代码实现

const Item = mongoose.model('Item', itemSchema);
const Inventory = mongoose.model('Inventory', inventorySchema);

async function getItemsSortedByInventoryCount() {
  const sortedItems = await Inventory.aggregate([
    // 1. 展开items数组,每个库存中的物品项转为独立文档
    { $unwind: '$items' },
    // 2. 按物品ID分组,统计被添加到库存的次数
    {
      $group: {
        _id: '$items.item',
        inventoryCount: { $sum: 1 }
      }
    },
    // 3. 关联Item集合,获取物品完整信息
    {
      $lookup: {
        from: 'items', // 注意:这里是MongoDB实际集合名,通常为模型名小写复数
        localField: '_id',
        foreignField: '_id',
        as: 'itemInfo'
      }
    },
    // 4. 将关联得到的数组转为单个对象(每个分组对应唯一物品)
    { $unwind: '$itemInfo' },
    // 5. 按统计次数降序排序
    { $sort: { inventoryCount: -1 } },
    // 6. 可选:调整返回字段结构,只保留需要的信息
    {
      $project: {
        _id: 0,
        itemId: '$_id',
        name: '$itemInfo.name',
        emoji: '$itemInfo.emoji',
        category: '$itemInfo.category',
        createdBy: '$itemInfo.createdBy',
        inventoryCount: 1
      }
    }
  ]);

  return sortedItems;
}

// 使用示例
getItemsSortedByInventoryCount()
  .then(result => console.log('按库存次数排序的物品:', result))
  .catch(err => console.error('查询失败:', err));

关键说明

  • 集合名称注意:$lookup中的from参数要和MongoDB中实际的集合名称一致,Mongoose默认会把模型名转为小写复数(比如Item模型对应items集合)。
  • 过滤特定库存:如果需要只统计特定用户、公开/私有库存,可以在聚合开头添加$match阶段,例如:
    { $match: { access: 'PUBLIC', owner: userId } }
    
  • 性能考量:如果数据量极大,建议给items.item字段添加索引,或者定期将统计结果缓存到Redis等工具中,避免频繁执行聚合查询。

内容的提问来源于stack exchange,提问作者Ibrahim Farooq

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 15:40:23