Mongoose实现单用户对单期刊仅发布1条评论的方案咨询
问题解答
关于$addToSet是否可行
无法直接用$addToSet实现你的需求:你的Journal集合中review字段存储的是Review文档的唯一ObjectId,每次新建评论都会生成全新的_id,$addToSet只能校验数组中是否存在完全相等的值,无法识别「同一用户对同一期刊的重复评论」这个业务逻辑,因此不适用。
可行实现方案
方案一:业务逻辑校验 + 数据库唯一索引双重保障(推荐)
第一步:修改Review Schema优化关联逻辑
新增journalId字段关联评论所属期刊,同时添加author + journalId复合唯一索引,从数据库层面强制限制同一用户对同一期刊只能发布一条评论:
const reviewSchema = new mongoose.Schema({ author: {type: mongoose.Schema.Types.String, ref: 'User'}, journalId: {type: mongoose.Schema.Types.ObjectId, ref: 'Journal'}, // 新增字段关联所属期刊 content: String, date: Date, rating: {type: Number, min: 1.0, max: 5.0} }); // 新增复合唯一索引,强制限制同一用户对同一期刊只能有一条评论 reviewSchema.index({author: 1, journalId: 1}, {unique: true}); const Review = mongoose.model("Review", reviewSchema);
第二步:重写评论提交路由逻辑
修复原代码遍历评论就重复新增的逻辑漏洞,用async/await优化嵌套回调,先校验再写入:
app.post("/stats/review", async function(req, res){ // 未登录直接跳转登录页 if(!req.isAuthenticated()) return res.redirect("/login"); try { const { nickname: userNickname } = req.user; const { journalReview: userReview, rating: userRating, journalId } = req.body; // 先校验是否已经提交过评论 const existReview = await Review.findOne({ author: userNickname, journalId: journalId }); if(existReview) { console.log("当前用户已对该期刊提交过评论"); return res.redirect("back"); } // 新建评论并写入数据库 const newReview = await Review.create({ author: userNickname, journalId: journalId, content: userReview, rating: userRating, date: new Date() }); // 关联评论ID到对应期刊 await Journal.findByIdAndUpdate( journalId, { $push: { review: newReview._id } }, { useFindAndModify: false } ); res.redirect("back"); } catch (err) { // 捕获唯一索引冲突等异常 console.log(err); res.redirect("back"); } });
方案二:不修改现有Schema的兼容方案
如果你暂时不想调整Review的结构,可以直接优化现有查询逻辑,先判断该用户是否已经在期刊的评论列表中存在,再决定是否新建:
app.post("/stats/review", async function(req, res){ if(!req.isAuthenticated()) return res.redirect("/login"); try { const { nickname: userNickname } = req.user; const { journalReview: userReview, rating: userRating, journalId } = req.body; // 关联查询期刊下的所有评论,判断是否存在当前用户的评论 const targetJournal = await Journal.findById(journalId).populate('review'); const hasReviewed = targetJournal.review.some(review => review.author === userNickname); if(hasReviewed) { console.log("当前用户已对该期刊提交过评论"); return res.redirect("back"); } // 新建评论流程和原有逻辑一致 const newReview = await Review.create({ author: userNickname, content: userReview, rating: userRating, date: new Date() }); await Journal.findByIdAndUpdate( journalId, { $push: { review: newReview._id } }, { useFindAndModify: false } ); res.redirect("back"); } catch (err) { console.log(err); res.redirect("back"); } });
注:该方案不需要调整Schema结构,但每次校验都需要关联查询期刊下的所有评论,评论量大时性能比方案一低很多,仅适合临时兼容使用。
内容的提问来源于stack exchange,提问作者indee423
相关产品推荐
相关产品推荐

