MongoDB - Комментарии Upvoting / Downvoting с агрегации конвейера - PullRequest
2 голосов
/ 01 марта 2020

Я пытаюсь реализовать механизм upvote / downvote для комментариев (аналогично механизму upvote / downvoting, найденному в reddit). У меня есть отдельная коллекция с именем commentReputation, и документы внутри могут выглядеть так:

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

Короче говоря: каждый комментарий в конечном итоге будет иметь свой собственный документ CommentReputation (документ CommentReputation должен быть создан как только кто-то одобряет / понижает комментарий)

Есть 2 сценария ios:

  1. Коллекция пуста, что означает, что мне нужно создать самый первый CommentReputation документ с заданным комментарием ID x. В какой-то другой части проекта я использовал $setOnInsert с { upsert: true }, но кажется (глядя на документацию), что конвейер агрегации не поддерживает $setOnInsert на данный момент. Есть ли другой способ решения этой проблемы?

  2. Документ находится там, и должно произойти фактическое голосование.

    a) Оба массива upvotes и downvotes не содержат userId, который пытается поднять голос, поэтому он добавляется в массив upvotes без каких-либо дальнейших действий

    b) Массив upvotes содержит userId, который пытается поднять голос комментарий в результате userId должен быть УДАЛЕН из массива upvotes. (пользователь уже проголосовал за этот комментарий и во второй раз нажал кнопку upvote, которая отменяет это голосование)

    c) Массив downvotes содержит userId. В этом случае userId следует удалить из downvotes и добавить к upvotes

Я пытаюсь выполнить sh вышеуказанную логику c с помощью * Метод 1045 * и конвейер агрегации, однако я не уверен, возможно ли это вообще.

То, что у меня сейчас есть, возвращает "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])

Я переосмыслил всю функцию? Я предполагаю, что проблему 1. можно решить, просто создав документ CommentReputation (при пустых upvotes и downvotes в то же время создается документ Comment.

Есть ли лучший способ сделать это? Мне бы хотелось, чтобы он работал внутри одного запроса. Может быть, кто-то из вас, ребята, реализовал подобную функцию и может дать мне несколько советов по этому вопросу.

1 Ответ

2 голосов
/ 01 марта 2020

Вы можете сделать это с помощью следующего обновления конвейера, но для этого необходимо наличие массивов повышающих и понижающих голосов. даже если он просто пуст.

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'
                    ]
                }
            }
        }
    ]
);
...