基于Mongoose findOne的用户通知分页后端实现问题求助
解决Mongoose嵌套数组分页获取及总条数统计问题
原代码问题分析
dbResponse.notifications.totalLength是无效属性,切片后的数组只有length属性,但仅代表当前返回的条数,并非总通知数- 分页逻辑仅处理了第1页的情况,页码>1时未计算正确的偏移量
- 内存中反转数组效率较低,建议在数据库层面处理排序
方案一:两次查询(简单直接)
先获取总通知数,再查询对应分页的数据:
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
相关产品推荐
相关产品推荐

