MongoDB Query to Join Collections Using $lookup
$lookup to join collections in MongoDB, for example: db.orders.aggregate([{ $lookup: { from: 'products', localField: 'product_id', foreignField: '_id', as: 'product_info' } }]).Examples
How to Think About It
$lookup to specify the second collection, the fields to match, and the name of the new array field to hold matched documents.Algorithm
Code
db.orders.aggregate([
{
$lookup: {
from: 'products',
localField: 'product_id',
foreignField: '_id',
as: 'product_info'
}
}
])Dry Run
Let's trace joining orders with products where order has product_id 101.
Start with orders document
{ _id: 1, product_id: 101 }
Match product_id with products._id
Find product where _id = 101: { _id: 101, name: 'Pen' }
Add matched product to product_info array
{ _id: 1, product_id: 101, product_info: [{ _id: 101, name: 'Pen' }] }
| order._id | order.product_id | matched products | result.product_info |
|---|---|---|---|
| 1 | 101 | { _id: 101, name: 'Pen' } | [{ _id: 101, name: 'Pen' }] |
Why This Works
Step 1: Use $lookup to join collections
$lookup lets you combine documents from two collections by matching fields.
Step 2: Specify matching fields
You tell MongoDB which field in the first collection (localField) matches which field in the second (foreignField).
Step 3: Store matched documents in an array
The matched documents from the second collection are added as an array in the field named by as.
Alternative Approaches
const orders = db.orders.find().toArray(); const products = db.products.find().toArray(); const joined = orders.map(order => ({ ...order, product_info: products.filter(p => p._id === order.product_id) })); printjson(joined);
db.orders.aggregate([
{
$lookup: {
from: 'products',
let: { pid: '$product_id' },
pipeline: [
{ $match: { $expr: { $eq: ['$_id', '$$pid'] } } }
],
as: 'product_info'
}
}
])Complexity: O(n*m) time, O(n + m) space
Time Complexity
The $lookup stage may scan the foreign collection for each document in the local collection, leading to O(n*m) in worst case, where n and m are sizes of collections.
Space Complexity
Additional space is used to store the joined arrays in the output documents, proportional to the number of matches.
Which Approach is Fastest?
$lookup is efficient for server-side joins; manual client-side joins require more memory and network overhead.
| Approach | Time | Space | Best For |
|---|---|---|---|
| $lookup | O(n*m) | O(n + m) | Server-side joins with moderate data |
| $lookup with pipeline | O(n*m) | O(n + m) | Complex join conditions |
| Manual client-side join | O(n*m) | O(n + m) | Small datasets or custom logic |