Как объединить вложенные комментарии, используя mongodb - PullRequest
0 голосов
/ 21 марта 2020

У меня есть один документ для поста блога, подобный этому:

{
            "_id": "5d8051cdf0b1017da7bff23c",
            "description": "<p>will update soon</p>",
            "topic": "How to setup Kafka Cluster on CentOS",
            "comments": [
                {
                    "created_at": "2019-10-12T02:13:01.859Z",
                    "updated_at": "2019-10-12T02:13:01.859Z",
                    "edited": false,
                    "_id": "5da182aa013d12567e340a2d",
                    "message": "test"
                    "replies": [
                        {
                            "created_at": "2019-10-12T02:13:01.859Z",
                            "updated_at": "2019-10-12T02:13:01.859Z",
                            "edited": false,
                            "_id": "5da182aa013d12567e340a2a",
                            "message": "I am replying to first comment",
                            "commentator": "5daae8b8af029ec4533fe317"
                        },
                        {
                            "created_at": "2019-10-12T02:13:01.859Z",
                            "updated_at": "2019-10-12T02:13:01.859Z",
                            "edited": false,
                            "_id": "5da182aa013d12567e340a2c",
                            "message": "Helpful second Comment",
                            "commentator": "5d7f936544dac213e3f650ec"
                        }
                    ]
                }
            ]
        }
}

Я хочу выполнить вложенную агрегацию, используя mongodb.

На данный момент мой запрос

{ $unwind: { path: '$comments', preserveNullAndEmptyArrays: true } },
{ $unwind: { path: '$comments.replies', preserveNullAndEmptyArrays: true } },
{
  $lookup: {
    from: 'users',
    let: { thread_reply_commentator: '$comments.replies.commentator' },
    pipeline: [
       { $match: { $expr: { $eq: ['$_id', '$$thread_reply_commentator'] } } },
       { $project: AUTHOR_PROJECTION },
    ],
    as: 'comments.replies.commentator'
  }
},
{ $unwind: { path: '$comments.replies.commentator', preserveNullAndEmptyArrays: true } },
{ 
   $group: {
      _id: { _id: '$_id', comment: "$comments._id" },
      root: { $mergeObjects: '$$ROOT' },
      replies: { $push: '$comments.replies' }
   }
},
{
   $replaceRoot: {
      newRoot: {
        $mergeObjects: ['$$ROOT.replies']
      }
   }
}

А теперь мой приведенный выше запрос приводит к

{
  "_id": "5d8051cdf0b1017da7bff23c",
  "description": "<p>will update soon</p>",
  "topic": "How to setup Kafka Cluster on CentOS",
   "comments": [
      {
        "_id": "5da182aa013d12567e340a2d",
        "message": "test",
        "replies": {
           "created_at": "2019-10-12T02:13:01.859Z",
           "updated_at": "2019-10-12T02:13:01.859Z",
           "edited": false,
           "_id": "5da182aa013d12567e340a2a",
           "message": "I am replying to first comment",
           "commentator": { 
             "first_name":"test",
             "last_name":"test"
            }
         }
      },
      {
        "_id": "5da182aa013d12567e340a2d",
        "message": "test",
        "replies": {
           "created_at": "2019-10-12T02:13:01.859Z",
           "updated_at": "2019-10-12T02:13:01.859Z",
           "edited": false,
           "_id": "5da182aa013d12567e340a2a",
           "message": "Helpful second Comment",
           "commentator": { 
             "first_name":"test",
             "last_name":"test"
            }
         }
      }
    ]
}

Но мой желаемый результат:

{
  "_id": "5d8051cdf0b1017da7bff23c",
  "description": "<p>will update soon</p>",
  "topic": "How to setup Kafka Cluster on CentOS",
   "comments": [
      {
        "_id": "5da182aa013d12567e340a2d",
        "message": "test",
        "replies": [
          {
           "created_at": "2019-10-12T02:13:01.859Z",
           "updated_at": "2019-10-12T02:13:01.859Z",
           "edited": false,
           "_id": "5da182aa013d12567e340a2a",
           "message": "I am replying to first comment",
           "commentator": { 
             "first_name":"test",
             "last_name":"test"
            }
          },
          {
             "created_at": "2019-10-12T02:13:01.859Z",
             "updated_at": "2019-10-12T02:13:01.859Z",
             "edited": false,
             "_id": "5da182aa013d12567e340a2a",
             "message": "Helpful second Comment",
             "commentator": { 
               "first_name":"test",
               "last_name":"test"
              }
            }
         ]
      }
    ]
}

Пожалуйста, помогите, как мне этого добиться. Я знаю, что это легко возможно, используя mon goose, но у меня нет доступа к модели схемы, поэтому я могу использовать только mongodb и не могу использовать mon goose.

Ответы [ 2 ]

0 голосов
/ 22 марта 2020

Альтернативный подход: не заставляйте БД делать то, что вы не можете сделать так же легко и качественно, включая передачу материала по сети.

Мы (в конечном итоге) хотим заменить идентификатор комментатора в ответах с информацией о комментаторе, как имя и фамилия. Мы видим, что идентификатор комментатора является уникальным идентификатором в коллекции users. В худшем случае у каждого ответа будет свой идентификатор комментатора. Это означает, что нужно искать целую кучу имен и фамилий. Это требует времени в движке БД, но обойти это невозможно. Затем движок объединяет информацию replies с информацией users и отправляет do c по проводам. Однако некоторые комментаторы будут комментировать более одного ответа. В зависимости от сложности движка БД, нам может потребоваться, чтобы только имя и фамилия комментатора были просмотрены и переданы один раз для всех ответов во всех документах . Но вера в это, вероятно, не так. Вероятно, из-за прецедента перекрытия комментаторов на c -to-do c мало. Кроме того, нельзя обойти стороной тот факт, что имя и фамилия - одинаковые, хотя они могут присутствовать во многих документах - отправляются по проводам снова и снова, поэтому при таком подходе производительность сети / данных не достигается. Поэтому, учитывая эту настройку, возможно, идеальный запрос просто такой:

c = db.repliesColl.aggregate([
{$lookup: {
        from: "users",
        localField: "comments.replies.commentator",
        foreignField: "commentator",
        as: "z"
    }
}
]);

Вот и все. Что это будет делать? «Двойное погружение» через два массива (comments и replies) будет в каждом случае делать c, создавая массив z, содержащий уникальный поиск для этого do c. Как указывалось ранее, если комментатор C1 появлялся снова и снова, тогда да, информация передавалась по проводам снова и снова (делайте c, делайте c), но не делайте ошибку: нет возможности обойти Первоначальный поиск, чтобы найти его в первую очередь на стороне сервера. И мы утверждаем, что «частота повторения» комментаторов в разных документах, вероятно, низкая.

Поэтому на практике такие данные (некоторые дополнительные поля для ясности исключены):

{
     "_id": "5d8051cdf0b1017da7bff23c",
     "description": "<p>will update soon</p>",
     "topic": "How to setup Kafka Cluster on CentOS",
     "comments": [
     {
         "_id": "5da182aa013d12567e340a2d",
         "message": "original msg",
         "replies": [
           {"commentator": "C1", "message": "I am replying to first comment"},
           {"commentator": "C1", "message": "Forgot something"},
           {"commentator": "C2", "message": "Second comment"},
           {"commentator": "C2", "message": "Third comment, same guy"}
                     ]
     }
    ]
  }
}

будут давать эти выходные данные:

(everything in the doc above plus):
    "z" : [
        {
            "commentator" : "C1",
            "fname" : "Steve",
            "lname" : "Jones"
        },
        {
            "commentator" : "C2",
            "fname" : "Dan",
            "lname" : "Dare"
        }
    ]

Итак, теперь, в коде на стороне клиента, мы можем сделать этот псевдокод:

  Map cidmap = new HashMap();                                                                                                        

  while(cursor.hasNext()) {                                                                                                          
    Document doc = cursor.next();                                                                                                 

    // Capture id->name mappings:                                                                                                    
    for(Map m : (List)doc.get("z")) {                                                                                                
      String cid = m.get("commentator");                                                                                             
        if(!cidmap.containsKey(cid)) {                                                                                               
          cidmap.set(cid, m));                                                                                                       
        }                                                                                                                            
    }                                                                                                                                

    // Process comments and, where necessary, substitute the value for cid.                                                           
  }                                                                                                                                  

Привлекательность в том, что вы исключаете большую часть работы из Центральный ресурс, делая это самостоятельно. Чем более «уникален» набор комментаторов, тем эффективнее становится эта схема. Таким образом, мы балансируем нагрузку на механизм БД для управления данными, ожидаемую мощность уникальных комментаторов, скорость передачи по сети и сложность «постобработки» запроса на стороне клиента.

0 голосов
/ 22 марта 2020

Попробуйте это:

db.collection.aggregate([
  {
    $lookup: {
      from: "users",
      let: {
        thread_reply_commentator: {
          $reduce: {
            input: "$comments.replies.commentator",
            initialValue: [],
            in: {
              $concatArrays: [
                "$$value",
                "$$this"
              ]
            }
          }
        }
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$_id",
                "$$thread_reply_commentator"
              ]
            }
          }
        },
        { $project: AUTHOR_PROJECTION }
      ],
      as: "comentators"
    }
  },
  {
    $addFields: {
      comments: {
        $map: {
          input: "$comments",
          as: "comments",
          in: {
            $mergeObjects: [
              "$$comments",
              {
                replies: {
                  $map: {
                    input: "$$comments.replies",
                    as: "replies",
                    in: {
                      $mergeObjects: [
                        "$$replies",
                        {
                          commentator: {
                            $arrayElemAt: [
                              {
                                $filter: {
                                  input: "$comentators",
                                  cond: {
                                    $eq: [
                                      "$$this._id",
                                      "$$replies.commentator"
                                    ]
                                  }
                                }
                              },
                              0
                            ]
                          }
                        }
                      ]
                    }
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $project: {
      comentators: 0,
      "comments.replies.commentator._id": 0
    }
  }
])

MongoPlayground

...