Нужны документы из разных коллекций, объединенные в один и тот же документ в MongoDB с использованием структуры агрегирования с такой информацией, как количество и детализация

#mongodb #aggregation-framework

Вопрос:

У меня есть коллекция под названием опросы, в которой хранятся подробные сведения об опросах, такие как название, изображение опроса. В объекте surveys есть множество вопросов, в которых хранится множество вопросов для этих опросов.

     {
  "surveyID": 1,
  "title": "survey title",
  "questions": [
    {
      "_id": 1,
      "title": "first question",
      "choices": [
        "A",
        "B",
        "C"
      ]
    },
    {
      "_id": 2,
      "title": "second question",
      "choices": [
        "D",
        "E",
        "F"
      ]
    }
  ]
}
 

Теперь каждый опрос будет получать множество ответов, которые хранятся в другой коллекции, называемой Ответами. Каждый объект ответа имеет

     {
  "id": 1,
  "surveyID": 1,
  "responses": [
    {
      "questionID": 1,
      "answer": "A"
    },
    {
      "questionID": 1,
      "answer": "B"
    },
    {
      "questionID": 2,
      "answer": [
        "F",
        "E"
      ]
    }
  ]
}
 

Мне нужно получить такой документ, как этот

 {
  "surveyID": 1,
  "totalNumberOfRespones":4,
  "questions": [
    {
      "questionID": 1,
      "choices": [ {"choice":"A", "numberOfResponse":1}, {"choice":"B", "numberOfResponse":1}],
      "title": "first question"
    },
    {
      "questionID": 2,
      "choices": [ {"choice":"F", "numberOfResponse":1}, {"choice":"E", "numberOfResponse":1}],
      "title": "second question"
    }
  ]
}
 

Ответ №1:

  • $unwind деконструировать questions массив
  • $lookup с response пропуском сбора surveyID и questionID конвейером поиска
  • $match surveyID состояние
  • $filter чтобы повторить цикл responses и найти соответствующий questionID ответ
  • $unwind деконструировать responses массив
  • $addFields чтобы проверить условие, если answer тип поля s не является массивом, затем преобразуйте его в массив, потому что мы собираемся развернуть на следующем этапе
  • $unwind деконструировать answer массив
  • $group мимо surveyID , questionID и answer выбор, и подсчет
  • $group по surveyID и постройте questions массив и получите totalNumberOfRespones сумму
 db.survey.aggregate([
  { $unwind: "$questions" },
  {
    $lookup: {
      from: "response",
      let: {
        surveyID: "$surveyID",
        questionID: "$questions._id"
      },
      pipeline: [
        { $match: { $expr: { $eq: ["$surveyID", "$surveyID"] } } },
        {
          $addFields: {
            responses: {
              $filter: {
                input: "$responses",
                cond: { $eq: ["$this.questionID", "$questionID"] }
              }
            }
          }
        },
        { $unwind: "$responses" },
        {
          $addFields: {
            "responses.answer": {
              $cond: [
                { $isArray: "$responses.answer" },
                "$responses.answer",
                ["$responses.answer"]
              ]
            }
          }
        },
        { $unwind: "$responses.answer" },
        {
          $group: {
            _id: {
              surveyID: "$surveyID",
              questions: "$responses.questionID",
              answer: "$responses.answer"
            },
            numberOfResponses: { $sum: 1 }
          }
        },
        {
          $project: {
            _id: 0,
            choice: "$_id.answer",
            numberOfResponses: 1
          }
        }
      ],
      as: "questions.choices"
    }
  },
  {
    $group: {
      _id: "$surveyID",
      questions: { $push: "$questions" },
      totalNumberOfRespones: {
        $sum: { $sum: "$questions.choices.numberOfResponses" }
      }
    }
  }
])
 

Игровая площадка

Комментарии:

1. Эй @turivishal,спасибо за ответ, но, похоже, я технически допустил ошибку в выходном документе запроса, на самом деле мне нужно иметь выбор и количество ответов на выбор, а не полностью на вопрос

2. хорошо, я обновил ответ, вы можете просмотреть его.

3. Большое вам спасибо! Это сработало! Не могу по-настоящему отблагодарить тебя, парень. На самом деле я опубликовал не всю проблему, что делать, если я хочу добавить свойство totalNumberOfRespones под идентификатором опроса, которое в основном суммирует все количество ответов, как в приведенном выше случае, оно будет рассматриваться как 4. Я пытаюсь сделать это сам, но свойство группы не позволяет мне это сделать. Кроме того, у меня есть только один вопрос: считаете ли вы, что архитектура этого дизайна хороша, или вы сохранили бы ответы только в одной коллекции опросов ?

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

5. Хорошо, спасибо, что ответили. Я действительно ценю это, чувак. Можете ли вы также добавить несколько общих ответов в этот запрос, я отредактировал вопрос в соответствии с окончательным требованием. Я сохраню этот запрос на потом специально, а сейчас я увеличу количество непосредственно в коллекции опросов. Спасибо!

Ответ №2:

Это не полный ответ, но попробуйте $lookup с помощью pipeline (документы).

Вы можете начать с этого и добавить $group после:

  pipeline =   [{$unwind: {
  path: "$questions"
}}, {$lookup: 
{
  from: 'a',
  let: {sid: '$surveyID',qid: "$questions._id"},
  pipeline: [{$unwind: {path: "$responses"}},{ $match:
                 { $expr:
                    { $and:
                       [
                         { $eq: [ "$surveyID",  "$sid" ] },
                         { $eq: [ "$responses.questionID", "$qid" ] }
                       ]
                    }
                 }
              }],
  as: 'questions'
}}, {$unwind: {
  path: '$questions',
  preserveNullAndEmptyArrays: false
}}]
db.collection.aggregate(pipeline)