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() } } ] );
代码说明
- 更新bets数组:通过
$map遍历数组,用$cond判断是否为目标投注项,匹配时用$mergeObjects合并原数据与更新字段,确保其他字段不受影响。 - 计算balance增量:
- 用
$filter从bets数组中筛选出已更新状态的目标项 - 用
$arrayElemAt取出筛选后的第一个元素(查询条件已确保每个文档至少有一个匹配项) - 通过
$mul计算金额与倍率的乘积,$ceil向上取整后,用$add加到原balance上
- 用
- 更新时间戳:添加
updatedAt字段更新,保证文档的修改时间准确
执行后,示例文档的balance会从100变为189,bets[1]的isPaidOut和didWin均设为true,完全符合需求。
内容的提问来源于stack exchange,提问作者batson
相关产品推荐
相关产品推荐

