You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在MongoDB中查询过去7天的文档并补全无数据日期的0值

解决MongoDB查询过去7天数据时补全无工单日期的问题

我明白你现在的困扰:当前的查询只会返回有工单创建的日期,像示例里的05/04/2022这种没有工单的日期直接缺失了,导致返回的数据不完整。下面提供两种解决方案,你可以根据场景选择:

方案一:Node.js代码层面补全日期(适合快速修改现有代码)

思路很简单:先生成过去7天的完整日期列表,再把你统计好的工单数据和这个列表做匹配,缺失的日期就填充numOfTickets: 0

修改后的代码如下:

const companyId = req.query.companyId;
// Last 7 days
const endDate = new Date();
const startDate = new Date(Date.now() - 604800000);

// 1. 生成过去7天的完整日期列表(格式和toLocaleDateString一致)
const generateDateRange = (start, end) => {
  const dates = [];
  const currentDate = new Date(start);
  while (currentDate <= end) {
    // 保持和你现有代码一致的日期格式
    dates.push(currentDate.toLocaleDateString());
    currentDate.setDate(currentDate.getDate() + 1);
  }
  return dates;
};
const fullDateRange = generateDateRange(startDate, endDate);

// 2. 查询并统计有工单的日期
const allCompanyTickets = await ticketModel
  .find({ 
    company_id: companyId, 
    createdAt: { $gte: startDate, $lte: endDate } 
  })
  .sort({ createdAt: 1 });

// 统计日期出现次数
const dateCountMap = allCompanyTickets.reduce((map, ticket) => {
  const dateStr = ticket.createdAt.toLocaleDateString();
  map[dateStr] = (map[dateStr] || 0) + 1;
  return map;
}, {});

// 3. 合并完整日期列表和统计数据,补全0值
const data = fullDateRange.map(date => ({
  date,
  numOfTickets: dateCountMap[date] || 0
}));

res.status(200).json({ 
  datesUsed: { startDate, endDate }, 
  data 
});

这个方法的好处是不需要改动数据库查询逻辑,只需要在现有代码基础上添加日期生成和合并的逻辑,快速解决问题。

方案二:MongoDB聚合管道处理(更高效,适合大数据量)

如果你的工单数据量很大,在Node.js层面处理可能会有性能问题,推荐直接用MongoDB的聚合管道在数据库层面完成统计和补全,减少数据传输量。

核心思路:

  • $dateToStringcreatedAt转成目标格式的日期字符串
  • 按日期分组统计工单数量
  • 生成过去7天的日期范围数组
  • 把统计结果和日期范围做左连接,补全缺失日期的0值

示例聚合代码:

const companyId = req.query.companyId;
const endDate = new Date();
const startDate = new Date(Date.now() - 604800000);

// 计算过去7天的日期天数(用于生成范围)
const daysDiff = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24));

const result = await ticketModel.aggregate([
  // 1. 筛选目标公司和时间范围的工单
  {
    $match: {
      company_id: companyId,
      createdAt: { $gte: startDate, $lte: endDate }
    }
  },
  // 2. 把createdAt转成dd/mm/yyyy格式的日期字符串
  {
    $addFields: {
      dateStr: {
        $dateToString: {
          format: "%d/%m/%Y",
          date: "$createdAt",
          // 如果需要时区,这里可以添加timezone参数,比如"Asia/Shanghai"
        }
      }
    }
  },
  // 3. 按日期分组统计数量
  {
    $group: {
      _id: "$dateStr",
      numOfTickets: { $sum: 1 }
    }
  },
  // 4. 生成过去7天的日期范围数组
  {
    $lookup: {
      from: {
        $expr: {
          $map: {
            input: { $range: [0, daysDiff + 1] },
            as: "day",
            in: {
              dateStr: {
                $dateToString: {
                  format: "%d/%m/%Y",
                  date: { $add: [startDate, { $multiply: ["$$day", 86400000] }] }
                }
              }
            }
          }
        }
      },
      localField: "_id",
      foreignField: "dateStr",
      as: "dateMatch"
    }
  },
  // 5. 合并数据,补全0值
  {
    $replaceRoot: {
      newRoot: {
        $mergeObjects: [
          { $arrayElemAt: ["$dateMatch", 0] },
          { numOfTickets: { $ifNull: ["$numOfTickets", 0] } }
        ]
      }
    }
  },
  // 6. 重命名字段并排序
  {
    $project: {
      _id: 0,
      date: "$dateStr",
      numOfTickets: 1
    }
  },
  { $sort: { date: 1 } }
]);

res.status(200).json({ 
  datesUsed: { startDate, endDate }, 
  data: result 
});

这个方法把所有逻辑放在数据库端处理,返回的数据直接就是完整的日期列表,性能更优,尤其是工单数量较多的时候。

需要注意的是,$dateToString的时区设置要和你之前toLocaleDateString保持一致,避免日期格式出现偏差。

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

火山引擎 最新活动