0
let bulk = Card.collection.initializeOrderedBulkOp();
// if user doesn't exist in array
bulk.find({
    '_id': mongoose.Types.ObjectId(card_id),
    'likedBy': { '$ne': mongoose.Types.ObjectId(user_id) }
}).updateOne({
    '$inc': { 'likes': 1 },
    '$push': { 'likedBy': mongoose.Types.ObjectId(user_id) }
});
// if user exists in array
bulk.find({
    "_id": mongoose.Types.ObjectId(card_id),
    "likedBy": mongoose.Types.ObjectId(user_id)
}).updateOne({
    "$inc": { "likes": -1 },
    "$pull": { "likedBy": mongoose.Types.ObjectId(user_id) }
});

bulk.execute(function(response) {
    console.log(response);

    return res.json({
        'state': true,
        'msg': 'Successful',
    })
});

The above is supposed to behave by incrementing or decrementing the likes field if the user id exists in the likedBy array.

However, both functions run, thus the last of the bulk gets to be the last action done. In the above, the end result is always zero.

I suspect the query matches a document always, thus the .updateOne() parts run on all.

Here's the schema:

var CardSchema = new Schema({
  slug: {
    type: String,
    lowercase: true,
    index: true
  },
  title: String,
  content: String,
  createdAt: {
    type: Date,
    default: Date.now,
  },
  updatedAt: {
    type: Date,
  },
  options: [],
  likedBy: [],
  likes: Number,
  createdBy: String,
  featured: Boolean,
});

Is there a better mongo way to do the like/dislike thing?

KhoPhi
  • 9,660
  • 17
  • 77
  • 128
  • I think you are misinterpreting [How to Model a “likes” voting system with MongoDB](https://stackoverflow.com/q/28006521/2313887). The actions that you say are "cancelling each other out" is because the first one is the "like" and the second one is the "dislike". You're not meant to submit "both" operations at once. They are meant to be independent for each action. – Neil Lunn Sep 06 '17 at 12:27
  • In my UI, I have a single button, which the intended purpose was to have it alternate between like/dislike. Thus first time click, like. Next click, dislike and so on. Checking the link. – KhoPhi Sep 06 '17 at 12:43

1 Answers1

0

Going with this for now. Too verbose, but works. I've created a like and dislike button separately in the UI, which calls two independent functions, but to same endpoint, with endpoint rigged like this:

let like = req.body.like;
// if request is a like
if (like) {
    Card.update({
        '_id': mongoose.Types.ObjectId(card_id),
        'likedBy': { '$ne': mongoose.Types.ObjectId(user_id) }
    }, {
        '$inc': { 'likes': 1 },
        '$push': { 'likedBy': mongoose.Types.ObjectId(user_id) }
    }, function(err) {
        if (err) {
          console.log(err);
           return res.json({
            'state': false,
            'msg': err
           })
        }

        return res.json({
            'state': true,
            'msg': 'Liked',
        })
    })
} else if (!like) { // if request is dislike
    Card.update({
        '_id': mongoose.Types.ObjectId(card_id),
        'likedBy': mongoose.Types.ObjectId(user_id) 
    }, {
        '$inc': { 'likes': -1 },
        '$pull': { 'likedBy': mongoose.Types.ObjectId(user_id) }
    }, function(err,) {
        if (err) {
           console.log(err);
           return res.json({
            'state': false,
            'msg': err
           })
        }

        return res.json({
            'state': true,
            'msg': 'Disliked',
        })
    })
}

Then something like this makes the request,

likeCard(card_id: string, like: boolean) {
let param = {
  card_id: card_id,
  like: like
};

return this.http.post(AppSettings.API_ENDPOINT + '/card/like', JSON.stringify(param), { headers: this.headers })
  .map((res) => {
    return res
  })
}
KhoPhi
  • 9,660
  • 17
  • 77
  • 128
  • how do you then know if the user liked ? Of course one way is to likedBy.indexOf(user_id), but its a bad idea to send the whole array of likedBy (for every post) just to check that.. is there any way how to "transform" that array into a flag: liked: true/false directly in mongo? I wouldnt even want to iterate them on the Backend (iterating is blocking operation and im using nodejs) – Denko Mancheski Dec 26 '18 at 11:46