Search code examples
mongodbaggregation

Mongo 5 findOneAndUpdate aggregation pipeline for default counter value?


Theres a several old questions (2015/2016) about doing an upsert with $inc that has a default value. The consensus seemed to be do multiple DB calls. I'm hoping there is a more modern approach to this now. I'm using Mongo 5.0.9.

I found this example that states you can now use $cond in update aggregation pipelines. I tried the following: https://stackoverflow.com/a/62819768/1795570

Trying to convert it to my problem I came up with:

const result = await context.params.tenantDb.collection('counters').findOneAndUpdate({
    // Filter
    name: options.name,
  }, {
    // Data
    $set: {
      value: {
        $cond: {
          if: {$eq:[{$type:"$value"} ,  "missing"]},
          then: 1000,   // it's the upsert case
          else: { // it's the update case
             $inc: {
               value: 1
             }
          }    
       }
      },
    },
  }, {
    // Mongo Options
    returnDocument: 'after',
    name: options.name,
    upsert: true,
  });

This however just adds the entire $cond as the value to my document.

 value: {
    _id: new ObjectId("62ab8829d8ea984d839e10f3"),
    name: 'my_counter',
    value: { '$cond': [Array] }
  },

Is it possible to do this?


Solution

  • You will be able to achieve that with this, you're missing [] that will help you to use the aggregation framework, then you should have this

    You will be able to achieve that with this, you 're missing [] that will help you to use the aggregation framework, then you should have this

    const result = await context.params.tenantDb.collection('counters').findOneAndUpdate({
        // Filter
        name: options.name,
    }, [{
        $set: {
            value: {
                $cond: {
                    if: {
                        $eq: [{
                                $type: "$value"
                            },
                            "missing"
                        ]
                    },
                    then: 1000,
                    // it's the upsert case
                    else: {
                        $add: [
                            "$value",
                            1
                        ]
                    }
                }
            },
    
        }
    }], {
        // Mongo Options
        returnDocument: 'after',
        name: options.name,
        upsert: true,
        multi: true
    });