Вложенный документ проекта в агрегат mongodb - PullRequest
0 голосов
/ 12 сентября 2018

У меня есть вложенный вложенный документ, который выглядит примерно так. В каждом сообщении есть n-комментариев, а в каждом комментарии есть данные пользователя с именем и идентификатором электронной почты.

Я хочу проецировать только имя прокомментированного пользователя в список

{
    "PostId":"Post001",
    "Comments":[
         {"_id": "001",
          "CommentedBy":{
            "_id":"User001",
            "Name":"UserName001",
            "email":"user001@eg.com"
            }
         },
         {"_id": "002",
           "CommentedBy":{
            "_id":"User002",
            "Name":"UserName002",
            "email":"user001@eg.com"
            }
         },
         {"_id": "003",
          "CommentedBy":{
            "_id":"User003",
            "Name":"UserName003",
            "email":"user001@eg.com"
            }
         }
    ]
}

И я хочу превратиться в нечто, похожее на это, используя конвейер агрегации mongodb.

{
    "PostId":"Post001"
    "Comments":[
         {"_id": "001",
          "CommentedBy":"UserName001",
         },
         {"_id": "002",
           "CommentedBy": "UserName002"
         },
         {"_id": "003",
          "CommentedBy": "UserName003"
         }
    ]
}

Использование проекционного запроса Монго предоставляет список CommentedBy со всеми именами в нем. Как мне этого добиться, используя агрегатный запрос Монго. Есть ли способ сделать это без использования $unwind.

Запрос, который я пробовал, и результат, который я получил.

db.getCollection('post').aggregate([
{$project:{"Comments.CommentedBy":"$Comments.CommentedBy.Name"}}
])

{
    "_id" : ObjectId("5b98b4cc3bb8c65aeacabd78"),
    "Comments" : [ 
        {
            "CommentedBy" : [ 
                "UserName001", 
                "UserName002", 
                "UserName003"
            ]
        }, 
        {
            "CommentedBy" : [ 
                "UserName001", 
                "UserName002", 
                "UserName003"
            ]
        }, 
        {
            "CommentedBy" : [ 
                "UserName001", 
                "UserName002", 
                "UserName003"
            ]
        }
    ]
}

Ответы [ 3 ]

0 голосов
/ 12 сентября 2018

unwind комментарии, затем project это и group это по PostId путем отправки всех комментариев в массив.

db.getCollection('test').aggregate([
{"$unwind" : "$Comments"},
{$project:{
    "PostId":"$PostId",
    "Comments.CommentedBy":"$Comments.CommentedBy.Name", 
    "Comments._id" : "$Comments.CommentedBy._id"
  }
},
{"$group" : {
    _id: "$PostId",
    //Comments: { "$push" : {"_id" : "$Comments._id", "CommentedBy" : "$Comments.CommentedBy"}} If you want to push specifi field.
    Comments: { "$push" : "$Comments"} // Only push whole Comments object will also work.
  }
}
])

Вывод:

{
    "_id" : "Post001",
    "Comments" : [ 
        {
            "_id" : "User001",
            "CommentedBy" : "UserName001"
        }, 
        {
            "_id" : "User002",
            "CommentedBy" : "UserName002"
        }, 
        {
            "_id" : "User003",
            "CommentedBy" : "UserName003"
        }
    ]
}

Примечание:

Если вы хотите нажать определенное поле.

Comments: { "$push" : {"_id" : "$Comments._id", "CommentedBy" : "$Comments.CommentedBy"}}

Или весь объект:

Comments: { "$push" : "$Comments"}
0 голосов
/ 12 сентября 2018

Вы можете попробовать использовать агрегацию $map и изменить ключи внутри, зациклившись на массиве Comments.

db.collection.aggregate([
  { "$project": {
    "PostId": 1,
    "Comments": {
      "$map": {
        "input": "$Comments",
        "as": "comment",
        "in": {
          "_id": "$$comment._id",
          "CommentedBy": "$$comment.CommentedBy.Name"
        }
      }
    }
  }}
])
0 голосов
/ 12 сентября 2018

Вы можете начать с планирования того, что вы хотите сделать.Например, вы можете попробовать это:

План

  • отмена комментариев
  • проецирование полей, которые вы хотите
  • сгруппировать это вместе
  • (необязательно) очистка

Реализация

  1. Размотка все комментарии

Таким образом, этапы будут:

const unwind = {
    $unwind: '$Comments',
};

Это приводит к дублированию - или, скорее, к умножению - ваших документов на столько, сколько у вас есть комментариев.

Проект

Теперь Проект Имя / идентификатор комментатора, если необходимо:

const project = {
    $project: {
        PostId: 1,
        CommentId: '$Comments._id',
        CommentedBy: '$Comments.commentedBy.Name',
    },
}

Теперь для каждого комментария у вас есть документ: { PostId, CommentId, CommentedBy }.

Сгруппируйте их снова.

Теперь вы можете группировать свои комментарии обратно, сгруппировать их по PostId:

const group = {
    $group: {
        _id: '$PostId',
        PostId: '$PostId',
        Comments: {
            $push: {
                _id: '$CommentId',
                CommentedBy: '$CommentedBy',
            },
        },
    },
};

Теперь вы будетеполучите ваши документы так:

{
    _id: '<PostID>',
    PostId: '<PostID>',
    Comments: [
      { _id: '<CommentId>', CommentedBy: '<username>' },
    ],
}
(опционально) очистка

Вы заметите, что у вас есть дополнительный верхний уровень _id, вы можете избавиться от него в другой $project фазе:

const cleanup = { $project: { _id: 0, ... } };

Итак, вся ваша труба теперь проста:

 db.getCollection('posts')
     .aggregate([
        unwind,
        project,
        group,
        cleanup,
     ]);

Я опустил шаблон и набираю здесь без MongoDB, так что вы можете проверить код дважды.(Возможно, вы все равно захотите сделать это с помощью кодов из internetz.)

...