Как сделать запрос mongodb с вложенными массивами

#mongodb #mongodb-query

#mongodb #mongodb-запрос

Вопрос:

У меня есть коллекция mongo db, в которой каждый документ содержит два вложенных массива (в каждом документе есть элемент, в котором хранится массив, в котором хранятся документы, в которых хранится другой массив), и мне нужны только некоторые поля самого глубокого документа. Я пробовал разные типы элементов проекции, но ни один из них не работал. Прямо сейчас я пытаюсь выполнить следующее:

 let answered_exam = await exams_table.findOne(
{_id: new ObjectId(req.body.course_id)}, 
{projection: { 
  _id: 1, 
  "exams": {
    "$map": {
      "input": "$exams",
      "in": { 
        "$this.exam_name": req.body.exam_name,
        "students_exams": {  
          "$map": {
            "input": "$this.students_exams",
            "in": { "student_email": req.body.student_email },
        }},
      },
    }}
  } 
});
 

Формат записи в базе данных следующий:
Блок атрибутов в середине — это документы, хранящиеся в массиве экзаменов
(каждый элемент выглядит так). Блок снизу — это формат документов.
введите описание изображения здесь

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

1. ваш рисунок — лучшее, что я видел, но текстовый JSON проще, поэтому люди могут тестировать свои запросы на них

Ответ №1:

Запрос 1
(размотать)

  • вы можете создать вложенную карту и фильтровать, но здесь мы выбираем только 1 экзамен, поэтому данные, которые я предполагаю, невелики, и мы можем использовать double unwind для простоты
  • совпадение _id
  • размотать exams
  • совпадение exam_name
  • размотать student_exams
  • совпадение email
  • проект, чтобы сохранить только professor_notes и mark
  • если вам нужен максимум 1 результат, добавьте также в конце еще один этап
    {"limit" : 1}

Тестовый код здесь

 aggregate(
[{"$match": {"$expr": {"$eq": ["$_id", 1]}}},
  {"$unwind": {"path": "$exams"}},
  {"$match": {"$expr": {"$eq": ["$exams.exam_name", "exam1"]}}},
  {"$unwind": {"path": "$exams.student_exams"}},
  {"$match": 
    {"$expr": {"$eq": ["$exams.student_exams.student_email", "email1"]}}},
  {"$project": 
    {"_id": 0,
      "professor_notes": "$exams.student_exams.professor_notes",
      "mark": "$exams.student_exams.mark"}}])
 

Запрос 2
(вложенная карта / фильтры)

  • сопоставить, если не соответствует нулевому значению, еще раз сопоставить, делая то же самое
  • при возврате фильтруйте, чтобы удалить нули
  • получите первый из первых и замените root

* запрос большой, он делает то же самое, сопоставляет вместо разматывания и фильтрует вместо сопоставления (2 сопоставления / 2 разматывания, 2 фильтра / 2 совпадения)

Тестовый код здесь

 aggregate(
[{"$match": {"$expr": {"$eq": ["$_id", 1]}}},
  {"$replaceRoot": 
    {"newRoot": 
      {"$arrayElemAt": 
        [{"$arrayElemAt": 
            [{"$filter": 
                {"input": 
                  {"$map": 
                    {"input": "$exams",
                      "in": 
                      {"$cond": 
                        [{"$not": [{"$eq": ["$exam.exam_name", "exam1"]}]}, null,
                          {"$filter": 
                            {"input": 
                              {"$map": 
                                {"input": "$exam.student_exams",
                                  "in": 
                                  {"$cond": 
                                    [{"$not": 
                                        [{"$eq": 
                                            ["$students_exam.student_email", "email1"]}]},
                                      null,
                                      {"professor_notes": 
                                        "$students_exam.professor_notes",
                                        "mark": "$students_exam.mark"}]},
                                  "as": "students_exam"}},
                              "cond": {"$ne": ["$students_exam", null]},
                              "as": "students_exam"}}]},
                      "as": "exam"}},
                  "cond": {"$ne": ["$exam", null]},
                  "as": "exam"}}, 0]}, 0]}}}])
 

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

1. Почему вы используете $expr ? Просто используйте {$match: {_id: 1}} и {$match: {"exams.exam_name": "exam1"}} , {$match: {"exams.student_exams.student_email": "email1"}} ?

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