如何实现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
相关产品推荐
相关产品推荐

