如何在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
相关产品推荐
相关产品推荐

