#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. я не писал их вручную, и в моей библиотеке такой же сложный код, поэтому я иногда забываю использовать более простые версии.