Если предположить, что room
из коллекции room_prices
содержит совпадающие данные из name
коллекции rooms
, то это выражение будет соответствовать "внутреннему" конвейеру $lookup
выражение с еще одним $lookup
:
db.collection('users').aggregate([
{ $match: cond },
{ $lookup: {
from: 'rooms',
let: { "user_id": "$_id" },
pipeline: [
{ $match:{ $expr: { $eq: ["$user", "$$user_id"] } } },
{ $lookup: {
from: 'room_prices',
let: { 'name': '$name' },
pipeline: [
{ $match: { $expr: { $eq: [ '$room', '$$name'] } } },
{ $project: { _id: 0, morning: 1, afternoon: 1 } }
],
as: 'room_prices'
}}
],
as: "rooms"
}}
])
Это также добавляет туда $project
, чтобы выбрать только те поля, которые вы хотите из цен. При использовании выразительной формы $lookup
вы действительно можете выразить «конвейер», который может быть любой комбинацией конвейерного объединения. Это допускает сложные манипуляции и такие «вложенные поиски».
Обратите внимание, что используя mongoose, вы также можете получить имя коллекции из объекта модели, используя что-то вроде:
from: RoomPrice.collection.name
Обычно это защита от возможных изменений конфигурации модели, которые могут изменить имя базовой коллекции.
Вы также можете сделать почти то же самое с «устаревшей» формой $lookup
до синтаксиса суб-конвейера, доступного от MongoDB 3.6 и выше. Это просто немного больше обработки и реконструкции:
db.collection('users').aggregate([
{ $match: cond },
// in legacy form
{ $lookup: {
from: 'rooms',
localField: 'user_id',
foreignField: 'user',
as: 'rooms'
}},
// unwind the output array
{ $unwind: '$rooms' },
// lookup for the second collection
{ $lookup: {
from: 'room_prices',
localField: 'name',
foreignField: 'room',
as: 'rooms.room_prices'
}},
// Select array fields with $map
{ $addFields: {
'rooms': {
'room_prices': {
$map: {
input: '$rooms.room_prices',
in: {
morning: '$this.morning',
afternoon: '$this.afternoon'
}
}
}
}
}},
// now group back to 'users' data
{ $group: {
_id: '$_id',
name: { $first: '$name' },
username: { $first: '$username' },
// same for any other fields, then $push 'rooms'
rooms: { $push: '$rooms' }
}}
])
Это немного больше накладных расходов, в основном из-за использования $unwind
, а также из-за того, что «выбор поля» на самом деле означает, что вы сначала вернули «целые документы» из room_prices
" ", и только после того, как это будет завершено, вы можете выбрать поля.
Таким образом, у нового синтаксиса есть свои преимущества, но это все же можно сделать с более ранними версиями, если вы захотите.