Aggregation PipelineLesson 4.3
How to join collections in MongoDB with $lookup
$lookup pipeline join, from, localField, foreignField, as, $unwind, multiple lookups, pipeline-form $lookup, performance considerations
$lookup performs a left outer join
$lookup joins documents from the current pipeline stage with documents from another collection in the same database. It always produces a left outer join โ every source document is included in the output, with matched foreign documents appended as an array in the as field. If no match is found, the array is empty.
// Join orders with their user details
const enriched = await db.collection('orders').aggregate([
{ $lookup: {
from: 'users', // the collection to join from
localField: 'userId', // field in orders
foreignField: '_id', // field in users
as: 'user' // output field name (always an array)
}},
// Unwind when relationship is one-to-one
{ $unwind: '$user' },
{ $project: {
orderId: '$_id', total: 1, status: 1,
userName: '$user.name',
userEmail: '$user.email'
}}
]).toArray()Pipeline-form lookup for filtered joins
// Filter the joined collection inside the lookup itself
{ $lookup: {
from: 'products',
let: { ids: '$items' },
pipeline: [
{ $match: { $expr: { $in: ['$_id', '$$ids'] } } },
{ $project: { name: 1, price: 1, _id: 0 } }
],
as: 'productDetails'
}}$lookup adds a full cross-collection scan and cannot use indexes from sharded foreign collections. For very high read throughput endpoints, embedding the frequently-read fields directly eliminates the join entirely.
