$lookup
The $lookup stage performs a left outer join to another collection in the same database to enrich documents with matching data.
📌 Syntax (Basic Form)
{
"$lookup": {
"from": "<foreign_collection>",
"localField": "<field_in_local_doc>",
"foreignField": "<field_in_foreign_doc>",
"as": "<output_field>"
}
}
✅ Base Example – Enrich Orders with Product Details
📥 Input Document (Order)
{ "orderId": 1, "productId": 101 }
📚 Foreign Collection (products)
[
{ "_id": 101, "name": "Mouse", "price": 50 },
{ "_id": 102, "name": "Keyboard", "price": 100 }
]
📌 Stage
{
"$lookup": {
"from": "products",
"localField": "productId",
"foreignField": "_id",
"as": "productDetails"
}
}
📤 Output Document
{
"orderId": 1,
"productId": 101,
"productDetails": [
{ "_id": 101, "name": "Mouse", "price": 50 }
]
}
🧱 Deep Nested Pipeline Example – Filter + Transform Product Match
{
"$lookup": {
"from": "products",
"let": { "pid": "$productId" },
"pipeline": [
{
"$match": {
"$expr": { "$eq": ["$_id", "$$pid"] }
}
},
{
"$project": {
"name": 1,
"price": 1,
"isExpensive": { "$gt": ["$price", 100] },
"_id": 0
}
}
],
"as": "productInfo"
}
}
📥 Input Document
{ "productId": 102 }
📚 Foreign Collection (products)
[
{ "_id": 102, "name": "Keyboard", "price": 120 },
{ "_id": 103, "name": "Monitor", "price": 200 }
]
📤 Output
{
"productId": 102,
"productInfo": [
{
"name": "Keyboard",
"price": 120,
"isExpensive": true
}
]
}
âž• Supported Accumulators (Used After $unwind)
If joined documents are unwound, you can apply grouping with these:
| Accumulator | Description |
|---|---|
$sum |
Count or total values |
$avg |
Mean of numeric values |
$min / $max |
Min/max across joined fields |
$push |
Collect into array |
$addToSet |
Unique values only |
$first / $last |
First/last matched records |
🔧 Common Operators Used in $lookup Pipelines
| Operator | Purpose |
|---|---|
$eq |
Join condition (via $expr) |
$project |
Field reshaping |
$match |
Filter after join |
$let / $$var |
Use variables in pipeline |
$gt, $cond |
Conditional logic in projections |
🧠Best Practices
- Use
$unwindafter$lookupwhen expecting a single match - Use
$projectto clean large foreign documents - Use
$let+$exprfor flexible joins
🔗 Related
$unwind– deconstruct joined arrays$group– aggregate joined data$expr,$function– join on expressions