skip to Main Content

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"} }}
    } 
}
);

2

Answers


  1. $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

    Login or Signup to reply.
  2. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search