MongoDB findOneAndUpdate返回null问题及评论投票功能实现求助
评论投票功能问题排查与解决
问题概述
实现评论投票功能时,要求登录用户不能给自己的评论投票,但调用Comment.findOneAndUpdate后返回null;同时不清楚如何通过nModified和ok属性判断评论是否更新成功。
原控制器代码
exports.voteComment = (req, res) => { const { commentId } = req.params; if (!ObjectId.isValid(commentId)) { return res.status(400).send({ error: "Comment ID not valid", }); } console.log(typeof req.auth._id); Comment.findOneAndUpdate( { commentId, "comment.postedBy": { $ne: req.auth._id } }, { $push: { votes: req.auth._id } }, { new: true } // { omitUndefined: true } // { returnNewDocument: true } ).exec((err, result) => { if (err) { return res.status(400).json({ error: "Could not vote on that comment", }); } console.log(result); res.json(result); // if (result.nModified) { // console.log("test"); // } }); };
Comment模型代码
const mongoose = require("mongoose"); const { ObjectId } = mongoose.Schema; const commentSchema = new mongoose.Schema( { content: { type: String, required: true, trim: true, max: 200, }, postedBy: { type: ObjectId, ref: "User", required: true, }, feature: { type: ObjectId, ref: "Feature", required: true, }, votes: [{ type: ObjectId, ref: "User" }], replies: [ { content: { type: String, required: true, trim: true, max: 200, }, postedBy: { type: ObjectId, ref: "User", }, created: { type: Date, default: Date.now, }, votes: [ { postedBy: { type: ObjectId, ref: "User", }, created: { type: Date, default: Date.now, }, }, ], }, ], }, { timestamps: true } ); module.exports = mongoose.model("Comment", commentSchema);
问题原因分析
- 查找字段错误:
- Mongoose默认文档ID字段是
_id,原代码用自定义的commentId作为查询条件,无法匹配到目标文档。 - 模型中
postedBy是一级字段,原代码错误使用"comment.postedBy"嵌套路径,导致权限判断条件失效。
- Mongoose默认文档ID字段是
- ID类型不匹配:
req.auth._id可能是字符串类型,而模型中postedBy是ObjectId类型,类型不匹配会导致$ne判断逻辑失效。 - 缺少重复投票拦截:未判断用户是否已投票,可能导致同一个用户ID被重复添加到
votes数组。
修正后的代码与实现
优化后的控制器方法(使用async/await更易维护)
const mongoose = require("mongoose"); const { ObjectId } = mongoose.Schema; const Comment = require("../models/Comment"); // 根据实际文件路径调整 exports.voteComment = async (req, res) => { const { commentId } = req.params; // 验证评论ID合法性 if (!ObjectId.isValid(commentId)) { return res.status(400).json({ error: "评论ID无效" }); } try { // 统一用户ID类型为ObjectId const userId = typeof req.auth._id === "string" ? mongoose.Types.ObjectId(req.auth._id) : req.auth._id; // 开启rawResult获取MongoDB原始响应,包含nModified等元数据 const updateResult = await Comment.findOneAndUpdate( { _id: commentId, postedBy: { $ne: userId }, // 排除自己发布的评论 votes: { $ne: userId } // 防止重复投票 }, { $push: { votes: userId } }, { new: true, rawResult: true } ); // 无符合条件的文档(评论不存在/是自己的评论/已投过票) if (!updateResult.value) { return res.status(403).json({ error: "无法投票:评论不存在、是您自己的评论或已投过票" }); } // 检查是否成功更新 if (updateResult.lastErrorObject.nModified === 0) { return res.status(400).json({ error: "投票操作未生效" }); } // 返回更新后的评论数据 res.json(updateResult.value); } catch (err) { console.error(err); res.status(400).json({ error: "投票失败,请稍后重试" }); } };
关于nModified和ok的使用说明
- 启用
rawResult: true选项后,findOneAndUpdate会返回包含以下核心字段的对象:value:更新后的文档(对应new: true的配置结果)lastErrorObject:包含nModified(实际更新的文档数量)、ok(操作是否成功,1为成功)等元数据
- 如果坚持使用回调方式,同样可以通过
rawResult: true获取这些字段:
Comment.findOneAndUpdate( // 查询条件 { _id: commentId, postedBy: { $ne: userId }, votes: { $ne: userId } }, // 更新操作 { $push: { votes: userId } }, // 选项 { new: true, rawResult: true } ).exec((err, result) => { if (err) { /* 错误处理逻辑 */ } console.log("更新的文档数:", result.lastErrorObject.nModified); console.log("操作是否成功:", result.lastErrorObject.ok); });
内容的提问来源于stack exchange,提问作者Rohan Srivastava
相关产品推荐
相关产品推荐

