Mongoose 社交平台关注者帖子查询及无限滚动分页最优实现方案
方案说明
你当前全量拉取所有关注者帖子再拆分的方案完全不可行,用户关注量、帖子量上来之后会直接打垮服务器内存和查询性能,不需要采用。你可以直接通过Mongoose单查询满足需求,无需遍历关注列表多次查库。
实现逻辑
- 先提取当前用户所有关注的账号ID数组
- 用MongoDB的
$in操作符一次匹配所有关注者发布的帖子,同时叠加年龄分级过滤条件 - 直接在数据库层面完成排序、分页操作,仅返回当前页需要的15条数据
代码实现
这里提供两种分页方案,你可以根据业务场景选择:
方案1:skip + limit 分页(适合中小数据量场景)
router.get("/following", auth, async (req, res) => { try { // 前端传当前分页偏移量,首次请求传0,后续每次加15 const pageSkip = Number(req.query.skip) || 0; const PAGE_SIZE = 15; const user = await User.findOne({ firebaseUID: req.authId }); // 提取关注用户ID数组 const followingIds = user.following.map(item => item.user); // 计算用户年龄 const age = dayjs().diff(dayjs(user.dob), "year"); // 一次查询完成过滤、排序、分页 const posts = await Post.find({ userID: { $in: followingIds }, age: { $lte: age } }) .sort({ date: -1 }) // 按发布时间倒序,不用自己再手动排序 .skip(pageSkip) .limit(PAGE_SIZE); res.json(posts); } catch (err) { console.error(err.message); res.status(500).json({ msg: "Server Error" }); } });
方案2:游标分页(推荐用于无限滚动,大数据量下性能更优)
skip分页在偏移量过大时,MongoDB需要跳过大量已排序文档,性能会明显下降,无限滚动场景更推荐用时间戳作为游标分页:
router.get("/following", auth, async (req, res) => { try { // 前端传上一次请求返回的最后一条帖子的发布时间,首次请求不传 const lastPostDate = req.query.lastDate ? new Date(req.query.lastDate) : new Date(); const PAGE_SIZE = 15; const user = await User.findOne({ firebaseUID: req.authId }); const followingIds = user.following.map(item => item.user); const age = dayjs().diff(dayjs(user.dob), "year"); const posts = await Post.find({ userID: { $in: followingIds }, age: { $lte: age }, date: { $lt: lastPostDate } // 只查比上次最后一条更早的帖子 }) .sort({ date: -1 }) .limit(PAGE_SIZE); res.json(posts); } catch (err) { console.error(err.message); res.status(500).json({ msg: "Server Error" }); } });
性能优化建议
给Post集合加联合索引,可以大幅提升查询速度:
// 在Post的Schema定义文件中添加索引,匹配查询字段和排序规则 PostSchema.index({ userID: 1, age: 1, date: -1 });
内容的提问来源于stack exchange,提问作者Thomas Pritchard
相关产品推荐
相关产品推荐

