MongoDB Query to Join Two Collections Using $lookup
$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
How to Think About It
$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
Code
db.orders.aggregate([
{
$lookup: {
from: 'products',
localField: 'productId',
foreignField: '_id',
as: 'productDetails'
}
}
])Dry Run
Let's trace joining orders with products using $lookup.
Start with orders collection
[{ _id: 1, productId: 101 }, { _id: 2, productId: 102 }]
Match productId with products._id
For order with productId 101, find product with _id 101
Add matched products as productDetails array
Order 1 gets productDetails: [{ _id: 101, name: 'Pen', price: 1.5 }]
| Order _id | productId | Matched Product | productDetails |
|---|---|---|---|
| 1 | 101 | { _id: 101, name: 'Pen', price: 1.5 } | [{ _id: 101, name: 'Pen', price: 1.5 }] |
| 2 | 102 | { _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
// Fetch documents from both collections separately and join in app codedb.collection1.aggregate([{ $graphLookup: { from: 'collection2', startWith: '$field1', connectFromField: 'field2', connectToField: 'field1', as: 'joinedData' } }])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.
| Approach | Time | Space | Best For |
|---|---|---|---|
| $lookup aggregation | O(n*m) | O(n + m) | Server-side joins, simple relationships |
| Manual app join | O(n*m) | O(n + m) | When aggregation is not available |
| $graphLookup | Higher than $lookup | Higher | Recursive or hierarchical joins |
localField and foreignField for faster joins.$lookup returns an array, so you may need to unwind if you want single objects.