mongodbmongoosemongodb-querymongodb-update

MongoDB - Update array with $addToSet and set array's length with { '$size': '$array' } not working


I am working on MongoDB, which has a model as below:

const appJobSchema = new mongoose.Schema({
    data: [
      { type: Schema.Types.Mixed }
    ],
    stat: {
      dataCount: { type: Number, default: 0 },
    }
})

What I need to do is to update a few records in the array field, at the same time update the latest array length to the stat.dataCount field.

export async function updateDataById(id: string, records: any[]) {
  return await model.updateOne({ '_id': id }, {  
    '$addToSet': { 'data': { '$each': records } } ,  
    '$set': { 
      'stat.dataCount': { $size: "$data" }  
    } 
  });
}

But I got an error saying:

UncaughtException: Cast to Number failed for value "{ '$size': '$data' }" (type Object) at path "stat.dataCount"

Any idea how I would do it?

Update

First try on the pipeline:

return await model.updateOne({'_id':id}, 
  [
    {
      "$set": {
        "data": {
          $concatArrays: [ "$data",  records ]
        }
      }
    },
    { 
      '$set': { 
        'stat.dataCount': { $size: "$data" } }  
    }
  ]);

It is working, but the problem is that solely adding values to the data array would cause duplicate values, and that is why I have to use $addToSet in order to remove the duplicates.

Second try on the pipeline:

Model.updateOne({'_id':id},
  [
      { '$addToSet': { 'data': { '$each': records } }, '$inc':{ 'runCount': 1  } , 
      { 
        '$set': { 
          'stat.dataCount': { $size: "$data" } }  
      }
  ]);

It throws out:

UncaughtException: Invalid update pipeline operator: "$addToSet".

Same for $inc.

Finally get it working by $setUnion with pipeline,

await Model.updateOne({'_id':id},
  [   
    {
      "$set": {
        "data": {
          $setUnion: [ "$data",  records  ]
        }
      }
    },
    { 
      '$set': { 
        'stat.dataCount': { $size: "$data" }  }  
    },
  ]);

But what if I need to use $inc? It still seems a problem.


Solution

  • As you are switching to the update pipeline to use the aggregation operator, the update operator is not supported. You need to switch to all aggregation operators, such as $sum to replace the $inc.

    await Model.updateOne({'_id':id},
      [   
        {
          "$set": {
            "data": {
              $setUnion: [ "$data",  records  ]
            },
            "runCount": { 
              $sum: [ "$runCount", 1 ]  
            }
          }
        }
      },
      { 
        "$set": { 
          "stat.dataCount": { $size: "$data" }  
        }
      },
    ]);