首页 > 解决方案 > Mongoose - How to initialize a default field value on update bulkOp

问题描述

Problem:

Cannot work aggregation in non-existent field.

I have Follow Schema which contains the following fields:

_user_id: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    following: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true,
        default: []
    }],
    followers: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true,
        default: []
    }],

And when a user follows someone, I have this:

const bulk = Follow.collection.initializeUnorderedBulkOp();

bulk.find({ _user_id: req.user._id }).upsert().updateOne({
  $addToSet: {
     following: Types.ObjectId(follow_id)
  }
});

bulk.find({ _user_id: Types.ObjectId(follow_id) }).upsert().updateOne({
   $addToSet: {
     followers: req.user._id
   }
});

bulk.execute((err, doc) => {
  ...
})

And when this successfully executes, it will be saved in MongoDB. Now the problem is when the data is just being saved for the first time, the bulk op above only saves the $addToSet field specified. For example, when I run this:

bulk.find({ _user_id: Types.ObjectId(follow_id) }).upsert().updateOne({
   $addToSet: {
     followers: req.user._id
   }
});

And this will yield to:

{
  _id: ObjectId(544987dfgsld3),
  followers: [ ObjectId(34578778g6d8f7g6) ] // followers gets saved
            // But how do I initialize the following as empty array? 
}

And now I get error in my aggregation because the field is not existent

const result = await Follow.aggregate([
                {
                    $match: { _user_id: user._id }
                },
                {
                    $project: {
                        _id: 0,
                        followingCount: { $size: '$following' }, // I get Error here $size field must be array but it doesnt exist
                        followersCount: { $size: '$followers' },
                        followers: 1
                    }
                },
            ]);

标签: javascriptmongodbmongoose

解决方案


Solved it by checking for non-existent field in my bulkOp query:

bulk
  .find({
     _user_id: Types.ObjectId(follow_id),
     following: { // added this block
        $exists: false
     }
})
.upsert()
.updateOne({
   $set: {  // and use $set operator
     following: []
   }
});

And a much simpler version using aggregate:

{
   $project: {
     following: { $ifNull: ["$following", []] }, // SET DEFAULTS IF NULL OR NON_EXISTENT
     followers: { $ifNull: ["$following", []] },
     followers: 1
   }
},
{
  $project: {
    _id: 0,
    followingCount: { $size: '$following' },
    followersCount: { $size: '$followers' },
    followers: 1
   }
},

More info about $ifNull Operator


推荐阅读