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

MongoDB更新用户投注文档时遇Cannot increment with non-numeric argument错误

解决MongoDB更新操作中的$inc聚合表达式错误

需求说明

需要定时执行User文档集合的更新操作:

  • 将符合条件的bets数组项标记为已支付(isPaidOut: true)和获胜(didWin: true)
  • 同时将用户balance字段递增该bets项的amount * multiplier的向上取整值

示例文档更新后,bets[1].isPaidOut和bets[1].didWin需设为true,balance需从100递增89(ceil(1.27 * 70) = 89)。

示例User文档

{
  "_id": {
    "$oid": "63c9ca0217b00eaef7d6237f"
  },
  "guildId": "1061387401743831121",
  "userId": "307884715677974530",
  "userName": "Iron Man",
  "balance": {
    "$numberDouble": "100.0"
  },
  "lastClaimedAt": {
    "$date": {
      "$numberLong": "1674168834621"
    }
  },
  "bets": [
    {
      "sport": "NFL",
      "eventWeek": {
        "$numberInt": "2"
      },
      "team": "New York Giants",
      "opponentTeam": "Philadelphia Eagles",
      "teamId": {
        "$numberInt": "19"
      },
      "opponentTeamId": {
        "$numberInt": "21"
      },
      "eventId": "401438004",
      "amount": {
        "$numberInt": "20"
      },
      "multiplier": {
        "$numberDouble": "3.85"
      },
      "isPaidOut": true,
      "didWin": false
    },
    {
      "sport": "NFL",
      "eventWeek": {
        "$numberInt": "2"
      },
      "team": "Philadelphia Eagles",
      "opponentTeam": "New York Giants",
      "teamId": {
        "$numberInt": "21"
      },
      "opponentTeamId": {
        "$numberInt": "19"
      },
      "eventId": "401438004",
      "amount": {
        "$numberInt": "70"
      },
      "multiplier": {
        "$numberDouble": "1.27"
      },
      "isPaidOut": false
    },
    {
      "sport": "NFL",
      "eventWeek": {
        "$numberInt": "2"
      },
      "team": "San Francisco 49ers",
      "opponentTeam": "Dallas Cowboys",
      "teamId": {
        "$numberInt": "25"
      },
      "opponentTeamId": {
        "$numberInt": "6"
      },
      "eventId": "401438006",
      "amount": {
        "$numberInt": "200"
      },
      "multiplier": {
        "$numberDouble": "1.49"
      },
      "isPaidOut": false
    }
  ],
  "createdAt": {
    "$date": {
      "$numberLong": "1674168834633"
    }
  },
  "updatedAt": {
    "$date": {
      "$numberLong": "1674338378566"
    }
  },
  "__v": {
    "$numberInt": "3"
  }
}

错误代码及报错信息

原更新代码

const winnerId = 21;
const eventId = "401438004";

usersCollection.updateMany(
{ 
   bets: {
     "$elemMatch": {
       eventId: eventId,
       isPaidOut: false,
       teamId: winnerId
     }
   }
},
{ 
  $set: { "bets.$.isPaidOut" : true, "bets.$.didWin": true }, 
    $inc: { 
      balance: {$ceil: {$mul: ["bets.$.amount", "bets.$.multiplier"]} }
    } 
}
);

报错信息

uncaught promise rejection: write exception: write errors: [Cannot increment with non-numeric argument: {balance: { $ceil: { $mul: [ "bets.$.amount", "bets.$.multiplier" ] } }}]

问题分析

$inc操作符仅接受纯数值作为增量参数,不支持嵌套聚合表达式(如$ceil、$mul)。原代码直接将聚合表达式传给$inc,导致MongoDB无法解析为合法的数值增量,从而抛出错误。

正确解决方案

使用MongoDB的**更新管道(Update Pipeline)**实现,更新管道允许在更新操作中使用聚合表达式。修改后的代码如下:

const winnerId = 21;
const eventId = "401438004";

usersCollection.updateMany(
  {
    bets: {
      $elemMatch: {
        eventId: eventId,
        isPaidOut: false,
        teamId: winnerId
      }
    }
  },
  [
    // 更新bets数组中符合条件的项
    {
      $set: {
        bets: {
          $map: {
            input: "$bets",
            as: "bet",
            in: {
              $cond: [
                {
                  $and: [
                    { $eq: ["$$bet.eventId", eventId] },
                    { $eq: ["$$bet.teamId", winnerId] },
                    { $eq: ["$$bet.isPaidOut", false] }
                  ]
                },
                // 匹配成功:合并原数据与更新状态
                { $mergeObjects: ["$$bet", { isPaidOut: true, didWin: true }] },
                // 匹配失败:保持原数据
                "$$bet"
              ]
            }
          }
        }
      }
    },
    // 计算并更新balance
    {
      $set: {
        balance: {
          $add: [
            "$balance",
            {
              $ceil: {
                $mul: [
                  // 取出目标bet的amount
                  { $arrayElemAt: [
                      { $filter: {
                          input: "$bets",
                          cond: {
                            $and: [
                              { $eq: ["$$this.eventId", eventId] },
                              { $eq: ["$$this.teamId", winnerId] },
                              { $eq: ["$$this.isPaidOut", true] }
                            ]
                          }
                        }
                      }, 0
                  ] }.amount,
                  // 取出目标bet的multiplier
                  { $arrayElemAt: [
                      { $filter: {
                          input: "$bets",
                          cond: {
                            $and: [
                              { $eq: ["$$this.eventId", eventId] },
                              { $eq: ["$$this.teamId", winnerId] },
                              { $eq: ["$$this.isPaidOut", true] }
                            ]
                          }
                        }
                      }, 0
                  ] }.multiplier
                ]
              }
            }
          ]
        }
      }
    },
    // 可选:更新文档修改时间
    { $set: { updatedAt: new Date() } }
  ]
);

代码说明

  1. 更新bets数组:通过$map遍历数组,用$cond判断是否为目标投注项,匹配时用$mergeObjects合并原数据与更新字段,确保其他字段不受影响。
  2. 计算balance增量:
    • 用$filter从bets数组中筛选出已更新状态的目标项
    • 用$arrayElemAt取出筛选后的第一个元素(查询条件已确保每个文档至少有一个匹配项)
    • 通过$mul计算金额与倍率的乘积,$ceil向上取整后,用$add加到原balance上
  3. 更新时间戳:添加updatedAt字段更新,保证文档的修改时间准确

执行后,示例文档的balance会从100变为189,bets[1]的isPaidOut和didWin均设为true,完全符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 01:50:15