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

MongoDB数组条件更新:实现单帖用户仅可点赞或点踩一次

问题根源

你的查询条件失效核心是对数组字段错误使用了$nin操作符。
$nin的语义是匹配字段值不在给定候选列表中的文档,作用在数组字段上时,它的匹配逻辑是「数组没有任何元素完全匹配候选列表值」,这个逻辑在数组存在多元素、类型隐式转换场景下很容易出现非预期匹配,直接导致投票拦截逻辑失效。
另外你当前的逻辑只覆盖了未投票用户的首次投票场景,既没有对更新结果做校验,也没有兼容多数论坛常见的「切换投票」需求。


修复方案

判断数组中是否不存在指定元素,直接使用$ne操作符即可,这是Mongo官方明确推荐的数组元素查询写法,行为稳定无歧义,且不需要额外包裹$and(Mongo顶层多条件默认就是且逻辑)。

基础版:仅允许单次投票,不可切换

完全符合你当前的业务规则:每个用户只能选点赞/点踩其中一种,不可重复投、不可同时投。

// 点赞操作
const vote_result = await Poll.updateOne(
  {
    _id: poll_id,
    likes: { $ne: MY_ID },
    dislikes: { $ne: MY_ID }
  },
  {
    $addToSet: {
      likes: MY_ID
    }
  }
)

// 必须校验匹配条数,没匹配到说明用户已经投过票
if (vote_result.matchedCount === 0) {
  throw new Error("不可重复投票")
}

点踩操作只需要把$addToSet的字段从likes换成dislikes即可,查询条件保持不变。

进阶版:支持切换投票(可选)

如果需要兼容用户从点赞改点踩、点踩改点赞的常见需求,可以用原子更新一步完成,不需要先查后改,避免并发问题:

// 切换为点赞
const vote_result = await Poll.updateOne(
  {
    _id: poll_id,
    likes: { $ne: MY_ID } // 防止重复点赞
  },
  {
    // 先从点踩列表移除当前用户(存在才删,不存在无影响)
    $pull: { dislikes: MY_ID },
    // 加入点赞列表
    $addToSet: { likes: MY_ID }
  }
)

点踩逻辑只需要互换$pull和$addToSet对应的字段即可。


场景验证

修复后可以覆盖所有测试场景:

  • 初始状态likes: []、dislikes: []:点赞、点踩均可正常执行
  • 已点赞状态likes: [MY_ID]、dislikes: []:重复点赞直接被拦截,若使用进阶版逻辑,发起点踩会自动将用户从点赞列表移到点踩列表
  • 已点踩状态likes: []、dislikes: [MY_ID]:重复点踩直接被拦截,点赞逻辑同理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 09:06:27