#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)