Ответ 1
Недавно я задал аналогичный вопрос и могу дать общий ответ (см. Использование позиционного оператора MongoDB $в глубоко вложенном запросе документа)
Это решение поддерживается только для Mongo 2.6+, но с этого момента вы можете использовать функцию преобразования $aggregation framework.
Вот пример запроса, который должен вернуть только ваш ученик Bort.
db.users.aggregate({
$match: { schoolName: 'Cool School' }
}, {
$project: {
_id: 0,
'schoolName': 1,
'rooms.number': 1,
'rooms.students': 1
}
}, {
$redact: {
$cond: {
"if": {
$or: [{
$gte: ['$schoolName', '']
}, {
$eq: ['$number', 100]
}]
},
"then": "$$DESCEND",
"else": {
$cond: {
"if": {
$eq: ['$name', 'Bort']
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}
}
}
});
$redact может использоваться для создания подзапросов путем согласования или обрезки суб-документов рекурсивно в согласованных документах.
Здесь вы можете прочитать о $redact, чтобы лучше понять, что происходит, но шаблон дизайна, который я определил, имеет следующие требования:
- Условие redact применяется на уровне каждого суб-документа, поэтому вам нужно уникальное поле на каждом уровне, например. вы не можете иметь номер в качестве ключа в обеих комнатах, и студенты говорят
- Он работает только с полями данных, а не с индексами массивов, поэтому, если вы хотите узнать возвращенную позицию вложенного документа (например, чтобы обновить его), вам необходимо включить и поддерживайте его в своих документах.
- Каждая часть инструкции $или в $redact должна соответствовать документам, которые вы хотите на определенном уровне.
- Поэтому каждая часть оператора $или должна содержать совпадение с уникальным полем документа на этом уровне. Например,
$eq: ['$number', 100]
соответствует комнате с номером 100 - Если вы не указываете запрос на уровне, вам нужно по-прежнему включать уникальное поле. Например, если это строка, вы можете сопоставить ее с
$gte: ['$uniqueField': '']
- Последний уровень документа находится в выражении второй, чтобы сохранить весь этот документ.