1

I have a collection of User documents, containing several fields and an array of Bets. I am trying to set up an update that I will run on a schedule. In the User documents, the Balance field needs to be incremented by (Bets[index].multiplier * Bets[index].amount), and the Bets[index] needs to be marked as paid out and whether the bet was successful.

An example of a User document I'm trying to update. After running the update, User.bets[1].isPaidOut should be true, User.bets[1].didWin should be true, and User.balance should be incremented by 89 (that is, because ceiling(1.27 * 70) = 89).

{
  "_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"
  }
}

This is what I have for my Update. When this is run, I receive this error: uncaught promise rejection: write exception: write errors: [Cannot increment with non-numeric argument: {balance: { $ceil: { $mul: [ "bets.$.amount", "bets.$.multiplier" ] } }}]. I thought this could have been because of mismatched types, but all documents have the same as the one above.

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"} }}
    } 
}
);
Noel
  • 10,152
  • 30
  • 45
  • 67
batson
  • 11
  • 3

2 Answers2

0

$ceil is an aggregation operator, not an update operator, so you can only use it with an update if you use the update with aggregation pipeline

Joe
  • 25,000
  • 3
  • 22
  • 44
0

You will have to use pipelined update. But then, you won't be able to use $ field names. Here's a solution using $map and $reduce.

db.collection.updateMany(
bets: {
"$elemMatch": {
  eventId: "401438004",isPaidOut: false,teamId: 21
    }
},
[
  {
    "$set": {
      "balance": {                         //set the updated balance
        $reduce: {
          input: "$bets",                  //iterate through the bets array
          initialValue: "$balance",        //start with the existing balance
          in: {
            $add: [
              "$$value",
              {
                $cond: [                  //check the condition
                  {$and: [
                      {$eq: ["$$this.eventId","401438004"]},
                      {$eq: ["$$this.teamId",21]},
                      {$eq: ["$$this.isPaidOut",false]}
                  ]},
                  {$ceil: {$multiply: ["$$this.amount","$$this.multiplier"]}},  //if true, perform the arithmetic operation andd add it to existing $$value
                  0                                                             //if false, add 0
                ]
              }
            ]
          }
        }
      },
      "bets": {                           //set the updated bets array
        $map: {                           //iterate through the bets array
          input: "$bets",
          in: {
            $cond: [                      //check the condition
              {$and: [
                  {$eq: ["$$this.eventId","401438004"]},
                  {$eq: ["$$this.teamId",21]},
                  {$eq: ["$$this.isPaidOut",false]}
              ]},
              {
                "$mergeObjects": [                    //if true, merge the array element with updated fields
                  "$$this",
                  {"didWin": true,"isPaidOut": true}
                ]
              },
              {
                "$mergeObjects": [                    //if false, keep the array element as it is
                  "$$this"
                ]
              }
            ]
          }
        }
      }
    }
  }
])

Demo

Noel
  • 10,152
  • 30
  • 45
  • 67