Как разложить и сгруппировать коллекции в mongoDB

Я не понимаю, как раскрутить, а затем вложить коллекции в mongoDB. в основном у меня есть две коллекции, которые структурированы следующим образом:

вопросы док:

{
    "_id" : 1,
    "questions" : [
        {
            "_id" : 1,
            "body" : "What fabric is the top made of?",
            "date_written" : "2018-01-04",
            "asker_name" : "yankeelover",
            "asker_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 2
        },
        {
            "_id" : 2,
            "body" : "HEY THIS IS A WEIRD QUESTION!!!!?",
            "date_written" : "2019-04-28",
            "asker_name" : "jbilas",
            "asker_email" : "[email protected]",
            "reported" : 1,
            "helpful" : 4
        },
        {
            "_id" : 4,
            "body" : "How long does it last?",
            "date_written" : "2019-07-06",
            "asker_name" : "funnygirl",
            "asker_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 6
        },

отвечает док:

{
    "_id" : 1,
    "answers" : [
        {
            "_id" : 8,
            "body" : "DONT BUY IT! It's bad for the environment",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 8
        },
        {
            "_id" : 7,
            "body" : "Its the best! Seriously magic fabric",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 7
        },
        {
            "_id" : 5,
            "body" : "Something pretty soft but I can't be sure",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 5,
            "photos" : [
                {
                    "_id" : 1,
                    "url" : "https://images.unsplash.com/photo-1530519729491-aea5b51d1ee1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1651&q=80"
                },

Поле _id в документе ответов соответствует полю _id вопросов, для которых они также являются ответами.

конечная цель состоит в том, чтобы иметь данные, которые выглядят примерно так:

{
    "_id": "17762",
    "questions": [
        {
            "question_id": 152829,
            "question_body": "Why Does it look like this?",
            "question_date": "2021-03-06T00:00:00.000Z",
            "asker_name": "garethTheGreato",
            "question_helpfulness": 60,
            "reported": false,
            "answers": {
                "1443770": {
                    "id": 1443770,
                    "body": "This question was really helpful! Thank you.",
                    "date": "2021-03-09T00:00:00.000Z",
                    "answerer_name": "SatisfiedCustomer",
                    "helpfulness": 3,
                    "photos": []
                },
                "1443807": {
                    "id": 1443807,
                    "body": "mimk",
                    "date": "2021-03-09T00:00:00.000Z",
                    "answerer_name": "jij",
                    "helpfulness": 3,
                    "photos": [
                        "blob:http://localhost:3000/8f6375b3-0795-4210-bef7-f112feed8244"
                    ]
                },
                "1443834": {
                    "id": 1443834,
                    "body": "10/10 would recomend.",
                    "date": "2021-03-09T00:00:00.000Z",
                    "answerer_name": "Krista",
                    "helpfulness": 2,
                    "photos": []
                },
                "1443845": {
                    "id": 1443845,
                    "body": "Thank you so much for playing my game!",
                    "date": "2021-03-10T00:00:00.000Z",
                    "answerer_name": "itsameemario",
                    "helpfulness": 1,
                    "photos": []
                },
                "1443880": {
                    "id": 1443880,
                    "body": "Tree",
                    "date": "2021-03-10T00:00:00.000Z",
                    "answerer_name": "Tree",
                    "helpfulness": 0,
                    "photos": [
                        "blob:http://localhost:3000/123051b6-4dfb-410a-a96f-d4a5128e3056"
                    ]
                }
            }
        },
        {
            "question_id": 152702,
            "question_body": "Please write your question here",
            "question_date": "2021-03-05T00:00:00.000Z",
            "asker_name": "Your nickname",
            "question_helpfulness": 32,
            "reported": false,
            "answers": {}
        },

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

Вот что у меня есть до сих пор: (игнорирование среза и сортировки на данный момент - это параметры, которые мне понадобятся позже в качестве другой части проекта)

  db.prodquests.aggregate([
    { $match: { _id: 5 } },
    { $unwind: '$questions' },
    { $match: { 'questions.reported': { $lt: 1 } } },
    { $sort: { 'questions.helpful': -1 } },
    { $group: { _id: '$_id', questions: { $push: '$questions' } } },
    { $project: { _id: 1, questions: { $slice: ['$questions', 0, 1] } } },
    { $unwind: '$questions' },
    {
      $lookup: {
        from: 'groupansphotos',
        localField: 'questions._id',
        foreignField: '_id',
        as: 'answers',
      },
    },
  ])

Возврат из этого оператора выглядит следующим образом:

{
    "_id" : 5,
    "questions" : {
        "_id" : 37,
        "body" : "Why is this product cheaper here than other sites?",
        "date_written" : "2018-10-18",
        "asker_name" : "willsmith",
        "asker_email" : "[email protected]",
        "reported" : 0,
        "helpful" : 4
    },
    "answers" : [
        {
            "_id" : 37,
            "answers" : [
                {
                    "_id" : 68,
                    "body" : "We are selling it here without any markup from the middleman!",
                    "date_written" : "2018-08-18",
                    "answerer_name" : "Seller",
                    "answerer_email" : "null",
                    "reported" : 0,
                    "helpful" : 4
                }
            ]
        }
    ]
}

По сути, я хочу сгруппировать только этот массив ответов по соответствующим вопросам, для которых совпадает поле _id.

Заранее спасибо!


person Alexander Shold    schedule 21.03.2021    source источник


Ответы (1)


ОБНОВЛЕНИЕ на основе комментариев:

Обновленный запрос:

db.questions.aggregate([
    { $match: { _id: 5 } },
    { $unwind: '$questions' },
    { $match: { 'questions.reported': { $lt: 1 } } },
    { $sort: { 'questions.helpful': -1 } },
    {
        $lookup: {
            from: "answers",
            let: { question_id: "$questions._id" },
            pipeline: [
                {
                    $match: {
                        $expr: { $eq: ["$_id", "$$question_id"] }
                    }
                },
                { $unwind: "$answers" },
                {
                    $project: {
                        _id: 0,
                        k: { $toString: "$answers._id" },
                        v: "$$ROOT.answers"
                    }
                }
            ],
            as: "answers"
        }
    },
    {
        $group: {
            _id: "$_id",
            questions: {
                $push: {
                    question_id: "$questions._id",
                    question_body: "$questions.body",
                    question_date: "$questions.date_written",
                    asker_name: "$questions.asker_name",
                    question_helpfulness: "$questions.helpful",
                    reported: "$questions.reported",
                    answers: { $arrayToObject: "$answers" }
                }
            }
        }
    }
]);

Старый запрос:

Примечание. Пожалуйста, исправьте название коллекции и/или имена полей. Попробуйте этот запрос:

db.questions.aggregate([
    { $match: { _id: 5 } },
    { $unwind: '$questions' },
    { $match: { 'questions.reported': { $lt: 1 } } },
    { $sort: { 'questions.helpful': -1 } },
    {
        $lookup: {
            from: "answers",
            let: { question_id: "$questions._id" },
            pipeline: [
                {
                    $match: {
                        $expr: { $eq: ["$_id", "$$question_id"] }
                    }
                },
                { $unwind: "$answers" },
                {
                    $project: {
                        _id: 0,
                        k: { $toString: "$answers._id" },
                        v: "$$ROOT.answers"
                    }
                }
            ],
            as: "answers"
        }
    },
    {
        $match: {
            $expr: {
                $gt: [{ $size: "$answers" }, 0]
            }
        }
    },
    {
        $group: {
            _id: "$_id",
            questions: {
                $push: {
                    question_id: "$questions._id",
                    question_body: "$questions.body",
                    question_date: "$questions.date_written",
                    asker_name: "$questions.asker_name",
                    question_helpfulness: "$questions.helpful",
                    reported: "$questions.reported",
                    answers: { $arrayToObject: "$answers" }
                }
            }
        }
    }
]);

Выход:

{
    "_id" : 5,
    "questions" : [
        {
            "question_id" : 2,
            "question_body" : "HEY THIS IS A WEIRD QUESTION!!!!?",
            "question_date" : "2019-04-28",
            "asker_name" : "jbilas",
            "question_helpfulness" : 4,
            "reported" : 0,
            "answers" : {
                "14" : {
                    "_id" : 14,
                    "body" : "DONT BUY IT! It's bad for the environment",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "[email protected]",
                    "reported" : 0,
                    "helpful" : 8
                },
                "15" : {
                    "_id" : 15,
                    "body" : "Its the best! Seriously magic fabric",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "[email protected]",
                    "reported" : 0,
                    "helpful" : 7
                },
                "16" : {
                    "_id" : 16,
                    "body" : "Something pretty soft but I can't be sure",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "[email protected]",
                    "reported" : 0,
                    "helpful" : 5
                }
            }
        },
        {
            "question_id" : 1,
            "question_body" : "What fabric is the top made of?",
            "question_date" : "2018-01-04",
            "asker_name" : "yankeelover",
            "question_helpfulness" : 2,
            "reported" : 0,
            "answers" : {
                "11" : {
                    "_id" : 11,
                    "body" : "DONT BUY IT! It's bad for the environment",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "[email protected]",
                    "reported" : 0,
                    "helpful" : 8
                },
                "12" : {
                    "_id" : 12,
                    "body" : "Its the best! Seriously magic fabric",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "[email protected]",
                    "reported" : 0,
                    "helpful" : 7
                },
                "13" : {
                    "_id" : 13,
                    "body" : "Something pretty soft but I can't be sure",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "[email protected]",
                    "reported" : 0,
                    "helpful" : 5
                }
            }
        }
    ]
}

Данные испытаний:

questions коллекция

{
    "_id" : 5,
    "questions" : [
        {
            "_id" : 1,
            "body" : "What fabric is the top made of?",
            "date_written" : "2018-01-04",
            "asker_name" : "yankeelover",
            "asker_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 2
        },
        {
            "_id" : 2,
            "body" : "HEY THIS IS A WEIRD QUESTION!!!!?",
            "date_written" : "2019-04-28",
            "asker_name" : "jbilas",
            "asker_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 4
        },
        {
            "_id" : 4,
            "body" : "How long does it last?",
            "date_written" : "2019-07-06",
            "asker_name" : "funnygirl",
            "asker_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 6
        }
    ]
}

answers коллекция:

/* 1 */
{
    "_id" : 1,
    "answers" : [
        {
            "_id" : 11,
            "body" : "DONT BUY IT! It's bad for the environment",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 8
        },
        {
            "_id" : 12,
            "body" : "Its the best! Seriously magic fabric",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 7
        },
        {
            "_id" : 13,
            "body" : "Something pretty soft but I can't be sure",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 5
        }
    ]
},

/* 2 */
{
    "_id" : 2,
    "answers" : [
        {
            "_id" : 14,
            "body" : "DONT BUY IT! It's bad for the environment",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 8
        },
        {
            "_id" : 15,
            "body" : "Its the best! Seriously magic fabric",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 7
        },
        {
            "_id" : 16,
            "body" : "Something pretty soft but I can't be sure",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "[email protected]",
            "reported" : 0,
            "helpful" : 5
        }
    ]
}
person Dheemanth Bhat    schedule 21.03.2021
comment
Это было именно то, что я искал, большое спасибо! Наличие конвейера на этапе поиска кажется очень мощным, и я надеюсь освоить его, как и вы. - person Alexander Shold; 21.03.2021
comment
Есть ли способ, чтобы на этапе поиска не исключались локальные документы, для которых нет совпадений в документе ответов? - person Alexander Shold; 22.03.2021
comment
Мы не можем сделать это изнутри этапа $lookup. Извне мы можем использовать $unwind в поле, полученном в результате операции соединения, чтобы исключить документы из внешней/локальной коллекции, но в вашем случае мы не можем использовать $unwind, так как нам нужен этот массив, чтобы скрыть его в объект. - person Dheemanth Bhat; 22.03.2021
comment
Да, я вижу, что ты говоришь. Теперь я думаю, что, возможно, лучше всего убедиться, что в коллекции ответов всегда есть совпадение, даже если ответы на соответствующий идентификатор вопроса представляют собой просто пустой массив. - person Alexander Shold; 22.03.2021
comment
Я предполагаю, что мой новый вопрос: есть ли способ добавить пустой массив ответов для каждого вопроса, ответы на который не совпадают, например, если вопрос _id: 5 не имеет совпадения _id в ответах: я бы вставил документ = { _id: 5, answer= [ ] }, который я могу отправить позже - person Alexander Shold; 22.03.2021
comment
Извините, я должен был быть более ясным. Прямо сейчас, когда я выполняю соединение с выбранными вопросами, вопросы будут удалены из окончательных результатов, если у них нет соответствующего документа с ответами. Я хочу включить ВСЕ вопросы, возвращаемые с исходного этапа сопоставления до того, как поиск только что объединился с их ответами, если они существуют. - person Alexander Shold; 22.03.2021
comment
Я обновил ответ. Проверьте, помогает ли это. Просто удалил этап $match перед $group. - person Dheemanth Bhat; 22.03.2021
comment
Вау, это было действительно простое решение. Теперь я вижу, что определенно неправильно смотрел на проблему. Большое спасибо за Вашу помощь!!! - person Alexander Shold; 22.03.2021