2

Consider the following code:

findAndModify({id: id}, undefined, {$inc: {counter: 1}, {$max: {counter: 10})

This fails with an error because both $inc and $max try to update the same field.

But what if I want to set the counter to 10 if its value is less than 10 and if not, increment its value by 1? How do I do that in one atomic operation?

Is there a way to apply the updates sequentially in a single operation?

Soggiorno
  • 760
  • 9
  • 17

1 Answers1

2

I don't think that can be done in a single operation. However, you can create an aggregate query with the conditional statements that you then use in your update.

(async () => {
    try {
        const results = await Model.aggregate([
            { '$match': { 'id': id } },
            { '$project': { 
                'counter': { 
                    '$cond': [
                        { '$lt': ['$counter', 10 ] },
                        10,
                        { '$add': ['$counter', 1 ] }
                    ] 
                }
            } }
        ]).exec();

        const data = results[0];
        const { counter } = data;

        const doc = await Model.findOneAndUpdate({ id },
            { '$set': { counter } },
            { new: true }
        ).exec();

        console.log(doc);
    } 
    catch (err) {
        console.error(err);
    }
})()

For an atomic update, include a query that restricts the increment for documents that only have counter less than the given ceiling value of 10:

findAndModify(
    {
        'id': id,
        'counter': { '$lt': 10 } 
    }, undefined, 
    { '$inc': { 'counter': 1 } },
    callback
)
chridam
  • 100,957
  • 23
  • 236
  • 235
  • 1
    @Soggiorno chridam's original answer was right. Any other updates to `counter` should also prevent it from going over 10. – JohnnyHK Aug 13 '18 at 14:06
  • @JohnnyHK Yes, his original answer was right. I made a mistake and didn't formulate the question properly though. The edit makes the solution quite a different beast. – Soggiorno Aug 13 '18 at 14:18
  • @chridam Thanks! Do you have any suggestion on how to make the aggregate and update queries atomic as to avoid race conditions in concurrent environments? – Soggiorno Aug 13 '18 at 14:19
  • @Soggiorno Refer to this [answer](https://stackoverflow.com/a/48107980/122005) for more info – chridam Aug 13 '18 at 14:23