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

如何实现MongoDB数组中对象存在判断及点赞/取消点赞功能

点赞/取消点赞API解决方案

问题背景

开发简单API,每个帖子对应一个点赞文档,文档包含存储点赞对象的likes数组。需求:若点赞对象不存在则添加至likes数组,若已存在则从中移除。尝试使用$elemMatch运算符未生效,寻求解决方案。

原代码问题分析

原代码存在以下问题:

  • 嵌套查询导致逻辑冗余,多次查询数据库降低效率
  • updateOne中使用错误的匹配条件_Id(拼写错误且应使用postId匹配帖子)
  • 不必要使用$elemMatch(单条件匹配数组元素时无需该运算符)
  • 回调嵌套易导致逻辑混乱,错误处理分散
  • 创建文档时userId和isNotified变量未明确从请求体获取,存在未定义风险

优化方案一:基于查询+内存判断的实现

使用async/await简化逻辑,减少数据库查询次数:

router.post("/like", async (req, res) => {
  try {
    // 解构请求体参数,给isNotified设置默认值
    const { postId, userId, isNotified = true } = req.body;
    
    // 查询当前帖子的点赞文档
    const likeDoc = await likeModel.findOne({ postId });

    if (!likeDoc) {
      // 不存在则创建新文档并保存
      const newLike = new likeModel({
        postId,
        likes: [{ userId, isNotified }]
      });
      const savedDoc = await newLike.save();
      return res.json(savedDoc);
    }

    // 内存中判断用户是否已点赞
    const hasLiked = likeDoc.likes.some(item => item.userId === userId);

    if (hasLiked) {
      // 已点赞则移除该用户的点赞记录
      const updatedDoc = await likeModel.updateOne(
        { postId },
        { $pull: { likes: { userId } } }
      );
      res.json(updatedDoc);
    } else {
      // 未点赞则添加新的点赞记录
      const updatedDoc = await likeModel.updateOne(
        { postId },
        { $push: { likes: { userId, isNotified } } }
      );
      res.json(updatedDoc);
    }
  } catch (err) {
    // 统一错误处理
    res.json({ message: err.message });
  }
});

优化方案二:原子操作实现(更高效)

通过数据库原子操作,最多两次请求完成逻辑,避免并发问题:

router.post("/like", async (req, res) => {
  try {
    const { postId, userId, isNotified = true } = req.body;
    
    // 先尝试移除用户的点赞记录
    const pullResult = await likeModel.updateOne(
      { postId, "likes.userId": userId },
      { $pull: { likes: { userId } } }
    );

    if (pullResult.modifiedCount === 0) {
      // 没有修改记录,说明用户未点赞,添加点赞(不存在则创建文档)
      const result = await likeModel.findOneAndUpdate(
        { postId },
        { $push: { likes: { userId, isNotified } } },
        { upsert: true, new: true } // upsert:不存在则创建;new:返回更新后的文档
      );
      res.json(result);
    } else {
      // 成功移除,返回移除结果
      res.json(pullResult);
    }
  } catch (err) {
    res.json({ message: err.message });
  }
});

原文档结构

[
    {
        "_id": "62f8556861c9b1029e7a07b8",
        "postId": "62f5fb7d74033aeac1e06399",
        "likes": [
            {
                "userId": "62f5d6bee2310e3c71e10e36",
                "isNotified": true,
                "_id": "62f8556861c9b1029e7a07b9"
            },
            {
                "userId": "62f5d9c2b30ecc8ca84c0242",
                "isNotified": true,
                "_id": "62f865ed2a055ef17214a6ea"
            },
            {
                "userId": "62f5d9c2b30ecc8ca84c0242",
                "isNotified": true,
                "_id": "62f866192a055ef17214a6f1"
            }
        ],
        "__v": 0
    },
    {
        "_id": "62f866192a055ef17214a6ed",
        "postId": "62f5fb7e74033aeac1e0639d",
        "likes": [
            {
                "userId": "62f5d9c2b30ecc8ca84c0242",
                "isNotified": true,
                "_id": "62f866192a055ef17214a6ee"
            }
        ],
        "__v": 0
    }
]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 11:06:48