skip to Main Content

I’m using MongoDB and I have a document where I want to increment a count field. However, I need to ensure that this count does not go below zero. For example, if inc is -2 and the current count is 1, applying $inc will result in -1, which I want to avoid.

Here’s the current update operation I’m using:

db.collection.updateMany(
  { '_id': {'$in': object_ids} },
  { 
    '$inc': { 'count': inc },
    '$addToSet': {...},
    '$set': {...},
  }
)

I’ve tried these 2 , but as ‘$addToSet’ can not be added as a pipeline stage I can not use sth like this:

db.collection.updateMany({ '_id': {'$in': object_ids} }, [ { $set: { count: { $cond: { if: { $gte: [{ $add: ['$count', inc] }, 0] }, then: { $add: ['$count', inc] }, else: 0 } } } } ] )

I also thought of using

'$expr': { '$gte': ['$count', 0] }

but this doesn’t prevent decrementing to negative!

3

Answers


  1. One possible solution is to use a two-step process:

    • use updateMany with $inc to increment the count fields of the documents.

      db.collection.updateMany(
              { '_id': { '$in': object_ids } },
              { 
                '$inc': { 'count': inc },
                '$addToSet': {...},
                '$set': {...},
              }
            )
      
    • use another updateMany operation with $max to set any negative count fields to zero.

      db.collection.updateMany(
          { '_id': { '$in': object_ids }, 'count': { '$lt': 0 } },
          { 
            '$set': { 'count': 0 }
          }
        )
      

    Note: this is not an atomic operation, you might need to use a transaction

    Login or Signup to reply.
  2. Please see if the below query is useful, it uses $cond and $expr.

    db.test.find();
    [ { a: 1 } ]
    
    // the value to update
    const updateval = -1;
    
    // the new value after update
    // this is used to build an expression
    test> const newval = { $cond: { if: { $eq:[1,1]}, then:{$add:["$a",updateval]}, else:{$add:["$a",0]}}}
    
    // update query using an expression
    db.test.updateMany({$expr:{$gte:[newval,0]}},{$inc:{a:updateval}})
    
    db.test.find();
    [ { a: 0 } ]
    
    // the same update query executed again 
    test> db.test.updateMany({$expr:{$gte:[newval,0]}},{$inc:{a:updateval}})
    
    // did not go beyond zero
    db.test.find();
    [ { a: 0 } ]
    
    Login or Signup to reply.
  3. It is possible to complete all operations in one shot with aggregation in an update pipeline.

    1. $inc: chain up $add result with $max with 0 to bound it to zero if it is negative
    2. $addToSet: put them in a $setUnion to perform the same behaviour
    3. $set: just put the other entries in the $set stage
    db.collection.update({},
    [
      {
        $set: {
          count: {
            $max: [
              0,
              {
                $add: [
                  "$count",
                  -2// increment
                  
                ]
              }
            ]
          },
          arr: {
            "$setUnion": [
              "$arr",
              // your input in $addToSet
              [
                "AAA",
                "BBB",
                "CCC"
              ]
            ]
          }
        }
      }
    ],
    {
      multi: true
    })
    

    Mongo Playground

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