Script Valley
MongoDB: Complete Course
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 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.

Up next

MongoDB $unwind, $addFields, and $bucket stages explained

Sign in to track progress

How to join collections in MongoDB with $lookup โ€” Aggregation Pipeline โ€” MongoDB: Complete Course โ€” Script Valley โ€” Script Valley