SylwekFr
SylwekFr

Reputation: 328

Update in function of existing or using mongo operator in updateOne mongoose method

Situation :

I have a like button and I wish that when a user clicks on like the like in the database:

Code:

the controller :

exports.likeIdea = (req,res,next) => {
    const userId = getUserId(req)
    Ideas.updateOne({ _id: req.params.id}, {
      $set: {
        like: { 
            $cond: [ {$in: [userId, "$likedBy"]}, { $inc: { like: +1 } } , { $inc: { like: -1 } } ] 
        },
        likedBy: { 
            $cond: [ {$in: [userId, "$likedBy"]}, { $pull: { likedBy: userId } } , { $push: { likedBy: userId } } ] 
        },
        _id: req.params.id
      }
    })
    .then(() => res.status(200).json({ message: 'Success'}))
    .catch(error => {
      res.status(400).json({ error })
    });
};

the schema

const ideaSchema = mongoose.Schema({  
    name: { type: String, required: true},  
    sumup: { type: String, required: true },  
    description: { type: String, required: true},  
    published: {type: Boolean, required: true},  
    like: {type: Number, required: true},  
    likedBy: {type: [String]},  
    author: {type: String, required: true},  
    dislike: {type: Number, required: true},  
    dislikedBy: {type: [String]},     
    imgUrl: {type: String, required: true} 
});  

the error :

CastError: Cast to Number failed for value "{ '$cond': [ { '$in': [Array] }, { '$inc': [Object] }, { '$inc': [Object] } ] }" at path "like" [...] {messageFormat: undefined, stringValue: '"{
'$cond': [ { '$in': [Array] }, { '$inc': [Object] }, { '$inc': [Object] } ] }"', kind: 'Number', value: {…}, path: 'like', …}

Upvotes: 1

Views: 312

Answers (1)

turivishal
turivishal

Reputation: 36134

The regular update query can not allow to use internal fields and aggregation operators like $cond, so you can't do this operation with regular update query,

You can try with update with aggregation pipeline starting from MongoDB 4.2,

  • instead of $inc you can use $add operator in aggregation update
  • instead of $pull you can use $filter to remove specific user
  • instead of $push you can use $concatArrays operator
exports.likeIdea = (req,res,next) => {
    const userId = getUserId(req)
    Ideas.updateOne({ _id: req.params.id},
      [{
        $set: {
          like: {
            $cond: [
              { $in: [userId, "$likedBy"] },
              { $add: ["$like", 1] },
              { $add: ["$like", -1] }
            ]
          },
          likedBy: {
            $cond: [
              { $in: [userId, "$likedBy"] },
              {
                $filter: {
                  input: "$likedBy",
                  cond: { $ne: ["$$this", userId] }
                }
              },
              { $concatArrays: ["$likedBy", [userId]] }
            ]
          }
        }
      }]
    ).then(() => res.status(200).json({ message: 'Success'}))
    .catch(error => {
      res.status(400).json({ error })
    });
};

Playground

Upvotes: 1

Related Questions