MongoDB - Comment Upvoting/Downvoting with Aggregation Pipeline

拜拜、爱过 提交于 2020-05-09 06:12:09

问题


I'm trying to implement an upvote/downvote mechanism for comments (similar to the upvoting/downvoting mechanism found on reddit). I have a separate collection called commentReputation and the documents inside can look like this:

{
    "_id" : ObjectId("5e5acb6d6034a879655c8819"),
    "commentId" : ObjectId("5e5983102328a83d1a4b541f"),
    "creationDate" : ISODate("2020-02-29T20:37:01.509Z"),
    "upvotes" : [
        ObjectId("5e5983102328a83d1a4b53e7"),
        ObjectId("5e5983102328a83d1a4b53e4")
    ],
    "downvotes" : [
       ObjectId("5e5983102328a83d1a4b53e5")
    ]
}

In short: every comment will eventually have it's own CommentReputation document (the CommentReputation document should be created as soon as someone upvotes/downvotes a comment)

There are 2 case scenarios:

  1. The collection is empty meaning that I need to create my very first CommentReputation document with a given commentId x. In some other part of the project I was using $setOnInsert with { upsert: true } but it seems (looking at the documentation) that the aggregation pipeline does not support $setOnInsert as for now. Is there another way to deal with this problem?

  2. The document is there and the actuall upvoting should occur.

    a) Both upvotes and downvotes arrays do not contain the userId that is trying to upvote thus it gets added to the upvotes array without any further actions

    b) The upvotes array contains the userId that is trying to upvote the comment as a result the userId should be REMOVED from the upvotes array. (the user already had this comment upvoted and clicked a second time the upvote button which cancels out the upvote)

    c) The downvotes array contains the userId. In this case the userId should be removed from downvotes and added to upvotes

I'm trying to accomplish the above logic with the updateOne method and a aggreagtion pipeline however I'm not sure if this is even possible.

What I currently have is returning a "Unrecognized pipeline stage name: '$cond'"

const updateUpvotes = {
  $cond: {
    if: { $elemMatch: { upvotes: ObjectID(userId) } },
    then: { $pull: { upvotes: ObjectID(userId) } },
    else: { $addToSet: { upvotes: ObjectID(userId) } }
  }
};

db.collection(collectionName).updateOne({
   commentId: ObjectID('5e5983102328a83d1a4b541f')
}, [updateUpvotes])

Am I overthinking the whole feature? I guess the 1. problem can be solved by simply creating a CommentReputation document (with empty upvotes and downvotes at the same time the Comment document is being created.

Is there a better way of doing this? I would love to have it working inside a single query request. Maybe someone of You guys implemented a similar feature and can give me some hints on this one.


回答1:


you can do it with the following pipeline update but it requires that the upvotes and downvotes arrays exist. even if it's just empty.

var comment_id = ObjectId("5e5983102328a83d1a4b541f");
var user_id = ObjectId("5e5983102328a83d1a4b53e5");

db.commentReputation.update(
    {
        commentId: comment_id
    },
    [
        {
            $set: {
                upvotes: {
                    $cond: [
                        { $in: [user_id, '$upvotes'] },
                        { $setDifference: ['$upvotes', [user_id]] },
                        { $setUnion: ['$upvotes', [user_id]] }
                    ]
                }
            }
        },
        {
            $set: {
                downvotes: {
                    $cond: [
                        { $in: [user_id, '$downvotes'] },
                        { $setDifference: ['$downvotes', [user_id]] },
                        '$downvotes'
                    ]
                }
            }
        }
    ]
);


来源:https://stackoverflow.com/questions/60469900/mongodb-comment-upvoting-downvoting-with-aggregation-pipeline

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!