Validating transactions: Prevent negative numbers

Assuming a have a property “balance” of type Number.
I am adding/subtracting this property by using the inc operator.
Is there a way to validate transactions so that queries that would result in the property being negative are rejected?

1 Like

You do not need transaction to do that.

Start with document

{ _id : 5 , balance : 300 }

So rather than doing an update such as:

collection.updateOne( { _id : 5 } ,
  { $inc : { balance : -400} } )

that would result into a new balance of -100. You simply do:

collection.updateOne( { _id : 5 , balance : { $gte : 400 } } ,
  { $inc : { balance : -400} } )

With this query the update will not occur.

3 Likes

Above solution is fine. But what if I want to check multiple fields. eg
collection.updateOne( { _id : 5 } ,
{ $inc : { balance : -400, savings: 200, cost: 100} } )
the query should only update the values if they will be positive after updation. In above case, query should not update ‘balance’, but should increment ‘savings’ and ‘cost’

@Sudarshan_Dhatrak1 , you can apply the @steevej’s solution above to your new requirements:

db.balance.updateOne(
  // filter params
  {
    _id: 5, 
    savings: { $gte: 400 }, // s1
    costs: { $gte: 250 }, // c1
  },
  // update params
  {
    $inc: {
      savings: -400, // s2
      costs: -250 // c2
    }
  }
);

If you decrease values using $inc, make sure its absolute value in update params is not greater, then the value in filter params:
|s1| must be >= |s2]
|c1| must be >= |c2]

How to make condition that if it would go negative, then set it to be 0, else execute the $inc as it would otherwise?

Use $cond with an update with aggregation.

1 Like

@Balint_Tamas

Hi, you can also use $max operator like this:

db.collection.update({
  _id: 1
},
[
  {
    "$set": {
      "balance": {
        "$max": [
          {
            "$subtract": [
              "$balance",
              400
            ]
          },
          0
        ]
      }
    }
  }
])

So, if the balance after deduction is less than 0, it will be set to 0. Otherwise, it will be set to the amount after deduction.

Working example

1 Like