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

MongoDB中如何关联Likes与Posts集合查询用户对帖子的点赞状态

场景说明

我们的应用包含Posts集合和Likes集合,Likes集合存储关联的postId和用户ID,两个集合的Schema定义如下:

// Post Schema 
let postSchema = new Schema({
  user: {type: Schema.Types.ObjectId, ref: "user"},
  text: {type: String},
  likeCount: {type: Number}
})

// Like Schema
let likeSchema = new Schema({
  post: {type: Schema.Types.ObjectId, ref: "post"},
  user: {type: Schema.Types.ObjectId, ref: "user"},
})

需求背景

分页拉取帖子列表时,需要同时返回当前登录用户是否已点赞该帖子的标识,执行查询时可获取到当前用户的userId。服务端使用Node.js开发,Likes集合会配置索引。


问题解答

1. 最优实现方案选择

分场景判断最优方案:

  • 常规业务场景(单页帖子数量≤100,服务端MongoDB版本支持聚合):MongoDB聚合查询是最优解,仅需一次数据库查询即可完成数据拉取和点赞状态拼接,IO次数最少,性能最优。
  • 特殊场景(聚合逻辑过于复杂、老项目代码规范要求避免大聚合、单页拉取帖子数量极大):二次查询+内存映射是最优解,代码可读性更高,维护成本更低,且两次查询均走索引,性能损失可忽略。

2. 聚合查询方案实现

聚合查询是非常合适的方案,只要给Likes集合配置post + user的联合索引,关联查询的效率极高,不会出现性能问题。
具体聚合管道代码如下(基于Mongoose):

const currentUserId = '当前登录用户的ObjectId';
const pageSize = 10;
const pageNum = 1;

const postList = await Post.aggregate([
  // 分页阶段:跳过前面页码的记录
  { $skip: (pageNum - 1) * pageSize },
  // 分页阶段:取当前页的记录数
  { $limit: pageSize },
  // 关联Likes集合,匹配当前用户对该帖子的点赞记录
  {
    $lookup: {
      from: 'likes', // Likes集合在MongoDB中的实际表名,默认是模型名小写加s
      let: { postId: '$_id' },
      pipeline: [
        {
          $match: {
            $expr: {
              $and: [
                { $eq: ['$post', '$$postId'] },
                { $eq: ['$user', mongoose.Types.ObjectId(currentUserId)] }
              ]
            }
          }
        }
      ],
      as: 'currentUserLikeRecords'
    }
  },
  // 新增isLiked字段,判断是否存在点赞记录
  {
    $addFields: {
      isLiked: { $gt: [{ $size: '$currentUserLikeRecords' }, 0] }
    }
  },
  // 可选:删除多余的关联结果字段,精简返回值
  {
    $project: {
      currentUserLikeRecords: 0
    }
  }
])

索引优化建议:给LikeSchema配置联合唯一索引,既避免重复点赞,又加速关联查询:

likeSchema.index({ post: 1, user: 1 }, { unique: true });

3. 非聚合查询方案实现

不使用聚合的情况下,最优方案是「两次索引查询+内存映射」,具体实现如下:

const currentUserId = '当前登录用户的ObjectId';
const pageSize = 10;
const pageNum = 1;

// 第一步:分页查询帖子列表
const postList = await Post.find()
  .skip((pageNum - 1) * pageSize)
  .limit(pageSize)
  .lean(); // 转换为普通JS对象,方便追加自定义字段

// 提取当前页所有帖子的ID数组
const postIdList = postList.map(post => post._id);

// 第二步:查询当前用户给这些帖子的点赞记录,走post+user联合索引
const likedRecords = await Like.find({
  user: mongoose.Types.ObjectId(currentUserId),
  post: { $in: postIdList }
}).select('post').lean();

// 提取点赞过的Post ID集合,加速判断
const likedPostIdSet = new Set(likedRecords.map(record => record.post.toString()));

// 第三步:内存拼接点赞状态
postList.forEach(post => {
  post.isLiked = likedPostIdSet.has(post._id.toString());
});

这种方案的优势是代码逻辑直观,容易调试和扩展额外业务逻辑,在单页拉取数量不超过200的场景下,性能和聚合查询几乎没有差异。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 04:39:02