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

基于Mongoose findOne的用户通知分页后端实现问题求助

解决Mongoose嵌套数组分页获取及总条数统计问题

原代码问题分析

  1. dbResponse.notifications.totalLength是无效属性,切片后的数组只有length属性,但仅代表当前返回的条数,并非总通知数
  2. 分页逻辑仅处理了第1页的情况,页码>1时未计算正确的偏移量
  3. 内存中反转数组效率较低,建议在数据库层面处理排序

方案一:两次查询(简单直接)

先获取总通知数,再查询对应分页的数据:

const { page = 1, limit = 10 } = req.query;
const pageNum = parseInt(page);
const limitNum = parseInt(limit);

// 1. 获取总通知条数
const countDoc = await Notification.findOne({ userId: res.locals.user._id }, { notifications: 1 });
if (!countDoc) {
  return res.status(HTTPCodes.NOT_FOUND).send({ message: "Notifications not found" });
}
const totalNotifications = countDoc.notifications.length;

// 2. 计算分页切片参数:page=2时取倒数20到倒数11条
const skip = pageNum * limitNum;
const sliceArgs = [-skip, limitNum];

// 3. 获取分页数据
const dbResponse = await Notification.findOne(
  { userId: res.locals.user._id },
  { notifications: { $slice: sliceArgs } }
);

// 反转数组让最新通知排在前面
const paginatedNotifications = dbResponse.notifications.reverse();

const response = {
  notificationsPerPage: limitNum,
  notifications: paginatedNotifications,
  currentPage: pageNum,
  totalNotifications,
  totalPages: Math.ceil(totalNotifications / limitNum) // 可选:返回总页数
};
return res.status(HTTPCodes.OK).send(response);

方案二:聚合管道一次查询(更高效)

通过MongoDB聚合管道,一次请求同时获取总条数和分页数据:

const { page = 1, limit = 10 } = req.query;
const pageNum = parseInt(page);
const limitNum = parseInt(limit);

const [result] = await Notification.aggregate([
  // 匹配目标用户
  { $match: { userId: res.locals.user._id.toString() } },
  // 添加总通知数字段
  { $addFields: { totalNotifications: { $size: "$notifications" } } },
  // 截取分页数据并反转,让最新通知在前
  {
    $project: {
      userId: 1,
      totalNotifications: 1,
      notifications: {
        $reverseArray: {
          $slice: ["$notifications", -(pageNum * limitNum), limitNum]
        }
      }
    }
  }
]);

if (!result) {
  return res.status(HTTPCodes.NOT_FOUND).send({ message: "Notifications not found" });
}

const response = {
  notificationsPerPage: limitNum,
  notifications: result.notifications,
  currentPage: pageNum,
  totalNotifications: result.totalNotifications,
  totalPages: Math.ceil(result.totalNotifications / limitNum) // 可选:返回总页数
};
return res.status(HTTPCodes.OK).send(response);

优化建议

如果通知有时间戳,建议修改Schema存储时间字段,避免依赖数组顺序:

const NotificationSchema = new Schema({
  userId: String,
  notifications: [{
    content: String,
    read: { type: Boolean, default: false },
    createdAt: { type: Date, default: Date.now }
    // 其他业务字段
  }]
});

此时可通过聚合管道的$unwind、$sort、$group实现更可靠的分页排序,避免数组顺序变化导致的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 12:15:32