基于日期统计下单商品数量的Node.js+MongoDB+Express API开发
按年月统计已下单商品数量的API实现方案
前提假设
先基于常见的订单Schema结构展开(如果你的Schema字段名不同,对应调整即可):
const mongoose = require('mongoose'); const OrderSchema = new mongoose.Schema({ customer: { type: mongoose.Schema.Types.ObjectId, ref: 'Customer', required: true }, orderDate: { type: Date, required: true, default: Date.now }, products: [ { product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true }, quantity: { type: Number, required: true, min: 1 } } ] }); module.exports = mongoose.model('Order', OrderSchema);
1. 核心聚合查询逻辑
MongoDB的聚合管道是实现这类统计的最优方案,分两种常见需求场景:
场景1:按年月统计所有商品下单总数量
返回结构示例:
[ { "yearMonth": "2024-04", "totalQuantity": 120 }, { "yearMonth": "2024-05", "totalQuantity": 180 } ]
聚合代码:
const monthlyTotalStats = await Order.aggregate([ // 展开订单中的商品数组,让每个商品单独成为一条文档 { $unwind: '$products' }, // 从下单日期中提取"YYYY-MM"格式的年月字符串 { $addFields: { yearMonth: { $dateToString: { format: "%Y-%m", date: "$orderDate" } } } }, // 按年月分组,累加所有商品的数量 { $group: { _id: "$yearMonth", totalQuantity: { $sum: "$products.quantity" } } }, // 按年月升序排序 { $sort: { _id: 1 } }, // 重命名字段,让返回结构更直观 { $project: { _id: 0, yearMonth: "$_id", totalQuantity: 1 } } ]);
场景2:按年月+商品维度统计(每个年月下各商品的下单数量)
返回结构示例:
[ { "yearMonth": "2024-05", "totalQuantity": 180, "products": [ { "productId": "60d21b4667d0d8992e610c85", "quantity": 90 }, { "productId": "60d21b4667d0d8992e610c86", "quantity": 90 } ] } ]
聚合代码:
const monthlyProductStats = await Order.aggregate([ { $unwind: '$products' }, { $addFields: { yearMonth: { $dateToString: { format: "%Y-%m", date: "$orderDate" } }, productId: "$products.product" } }, // 先按年月+商品ID分组,计算单商品当月下单量 { $group: { _id: { yearMonth: "$yearMonth", productId: "$productId" }, quantity: { $sum: "$products.quantity" } } }, // 再按年月分组,合并同月份的所有商品统计数据 { $group: { _id: "$_id.yearMonth", totalQuantity: { $sum: "$quantity" }, products: { $push: { productId: "$_id.productId", quantity: "$quantity" } } } }, { $sort: { _id: 1 } }, { $project: { _id: 0, yearMonth: "$_id", totalQuantity: 1, products: 1 } } ]);
2. Express控制器实现
把上面的聚合逻辑封装成API控制器:
const Order = require('../models/Order'); // 按年月统计商品下单数量(这里用场景1的逻辑,按需替换) exports.getMonthlyOrderStats = async (req, res) => { try { const stats = await Order.aggregate([ { $unwind: '$products' }, { $addFields: { yearMonth: { $dateToString: { format: "%Y-%m", date: "$orderDate" } } } }, { $group: { _id: "$yearMonth", totalQuantity: { $sum: "$products.quantity" } } }, { $sort: { _id: 1 } }, { $project: { _id: 0, yearMonth: "$_id", totalQuantity: 1 } } ]); res.status(200).json({ success: true, data: stats }); } catch (error) { console.error('统计查询失败:', error); res.status(500).json({ success: false, message: '获取统计数据失败,请稍后重试' }); } };
对应的路由配置:
const express = require('express'); const router = express.Router(); const orderController = require('../controllers/orderController'); router.get('/orders/stats/monthly', orderController.getMonthlyOrderStats); module.exports = router;
3. 优化建议
- 索引优化:给
orderDate字段添加索引,大幅提升聚合查询速度:OrderSchema.index({ orderDate: 1 }); - 时区处理:如果需要指定时区(比如北京时间),在
$dateToString中添加timezone参数:yearMonth: { $dateToString: { format: "%Y-%m", date: "$orderDate", timezone: "Asia/Shanghai" } } - 关联商品详情:如果需要返回商品名称等信息,可在聚合管道末尾添加
$lookup关联Product集合:// 放在$project之前 { $lookup: { from: 'products', // MongoDB中的实际集合名(通常是模型名小写复数) localField: 'products.productId', foreignField: '_id', as: 'productDetails' } }
内容的提问来源于stack exchange,提问作者velvj
相关产品推荐
相关产品推荐

