0
0
MongodbHow-ToBeginner · 2 min read

MongoDB Query to Join Two Collections Using $lookup

Use the $lookup stage in an aggregation pipeline to join two collections, for example: db.collection1.aggregate([{ $lookup: { from: 'collection2', localField: 'field1', foreignField: 'field2', as: 'joinedData' } }]).
📋

Examples

Inputdb.orders.aggregate([{ $lookup: { from: 'products', localField: 'productId', foreignField: '_id', as: 'productDetails' } }])
Output[{ _id: 1, productId: 101, productDetails: [{ _id: 101, name: 'Pen', price: 1.5 }] }, { _id: 2, productId: 102, productDetails: [{ _id: 102, name: 'Notebook', price: 3 }] }]
Inputdb.students.aggregate([{ $lookup: { from: 'classes', localField: 'classId', foreignField: '_id', as: 'classInfo' } }])
Output[{ _id: 1, name: 'Alice', classId: 10, classInfo: [{ _id: 10, name: 'Math' }] }, { _id: 2, name: 'Bob', classId: 20, classInfo: [{ _id: 20, name: 'Science' }] }]
Inputdb.employees.aggregate([{ $lookup: { from: 'departments', localField: 'deptId', foreignField: '_id', as: 'deptDetails' } }])
Output[{ _id: 1, name: 'John', deptId: 5, deptDetails: [] }]
🧠

How to Think About It

To join two collections in MongoDB, think of combining data like matching keys in two lists. Use $lookup to specify which collection to join, which fields to match, and where to put the joined data. This works like a left join, keeping all documents from the first collection and adding matching data from the second.
📐

Algorithm

1
Start with the first collection you want to query.
2
Use the aggregation pipeline and add a $lookup stage.
3
Specify the second collection to join using the 'from' field.
4
Set 'localField' to the field in the first collection to match.
5
Set 'foreignField' to the field in the second collection to match.
6
Define 'as' to name the new array field that will hold joined documents.
7
Run the aggregation to get combined results.
💻

Code

mongodb
db.orders.aggregate([
  {
    $lookup: {
      from: 'products',
      localField: 'productId',
      foreignField: '_id',
      as: 'productDetails'
    }
  }
])
Output
[ { _id: 1, productId: 101, productDetails: [ { _id: 101, name: 'Pen', price: 1.5 } ] }, { _id: 2, productId: 102, productDetails: [ { _id: 102, name: 'Notebook', price: 3 } ] } ]
🔍

Dry Run

Let's trace joining orders with products using $lookup.

1

Start with orders collection

[{ _id: 1, productId: 101 }, { _id: 2, productId: 102 }]

2

Match productId with products._id

For order with productId 101, find product with _id 101

3

Add matched products as productDetails array

Order 1 gets productDetails: [{ _id: 101, name: 'Pen', price: 1.5 }]

Order _idproductIdMatched ProductproductDetails
1101{ _id: 101, name: 'Pen', price: 1.5 }[{ _id: 101, name: 'Pen', price: 1.5 }]
2102{ _id: 102, name: 'Notebook', price: 3 }[{ _id: 102, name: 'Notebook', price: 3 }]
💡

Why This Works

Step 1: Use $lookup to join collections

The $lookup stage lets MongoDB combine documents from two collections by matching fields.

Step 2: Match fields to find related documents

It compares localField from the first collection with foreignField from the second to find matches.

Step 3: Add matched documents as an array

The matched documents are added as an array under the field named by as, keeping original documents intact.

🔄

Alternative Approaches

Manual join in application code
mongodb
// Fetch documents from both collections separately and join in app code
Slower and more complex; use only if aggregation is not possible.
Using $graphLookup for recursive joins
mongodb
db.collection1.aggregate([{ $graphLookup: { from: 'collection2', startWith: '$field1', connectFromField: 'field2', connectToField: 'field1', as: 'joinedData' } }])
Useful for hierarchical or recursive relationships but more complex and resource-intensive.

Complexity: O(n*m) time, O(n + m) space

Time Complexity

The join compares each document in the first collection (n) with matching documents in the second (m), so worst case is O(n*m).

Space Complexity

Extra space is needed to store the joined arrays, proportional to the number of matches.

Which Approach is Fastest?

Using $lookup is generally fastest for server-side joins; manual joins in app code are slower and more complex.

ApproachTimeSpaceBest For
$lookup aggregationO(n*m)O(n + m)Server-side joins, simple relationships
Manual app joinO(n*m)O(n + m)When aggregation is not available
$graphLookupHigher than $lookupHigherRecursive or hierarchical joins
💡
Always index the fields used in localField and foreignField for faster joins.
⚠️
Forgetting that $lookup returns an array, so you may need to unwind if you want single objects.