Convert SQL INNER JOIN to MongoDB $lookup with $unwind
Learn how SQL INNER JOIN between tables is converted to MongoDB $lookup aggregation stage followed by $unwind for collection-to-collection joins.
Detailed Explanation
INNER JOIN to $lookup + $unwind
SQL's INNER JOIN combines rows from two tables where the join condition is met. MongoDB handles this through the $lookup aggregation stage, which performs a left outer join, followed by $unwind to flatten the results.
Example SQL
SELECT orders.id, orders.total, users.name
FROM orders
INNER JOIN users ON orders.user_id = users.id
Generated MongoDB Query
db.orders.aggregate([
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "id",
as: "users"
}
},
{ $unwind: "$users" },
{
$project: {
id: 1,
total: 1,
"users.name": 1
}
}
])
How $lookup Works
$lookup performs a left outer join between the current collection and a foreign collection. The key parameters are:
- from: the foreign collection to join with
- localField: the field from the input documents
- foreignField: the field from the foreign collection
- as: the output array field name
Why $unwind?
$lookup produces an array of matching documents in the as field. For an INNER JOIN equivalent, $unwind is used to:
- Flatten the array so each matching document becomes a separate result
- Remove documents with empty arrays (no matches), simulating INNER JOIN behavior
Without $unwind, each result document would contain an array of all matching foreign documents.
Multiple JOINs
For queries joining three or more tables, chain multiple $lookup + $unwind stages:
db.orders.aggregate([
{ $lookup: { from: "users", localField: "user_id", foreignField: "id", as: "user" } },
{ $unwind: "$user" },
{ $lookup: { from: "products", localField: "product_id", foreignField: "id", as: "product" } },
{ $unwind: "$product" }
])
Performance Considerations
MongoDB $lookup does not use indexes on the foreign collection's foreignField in the same way SQL uses indexed joins. For high-performance joins, ensure the foreign field is indexed and consider denormalizing your data model where frequent joins are needed.
Use Case
Order management systems that need to display order details with customer names, or product catalogs with manufacturer information, require table joins. Understanding $lookup is critical when migrating a relational schema to MongoDB while maintaining the same query patterns.
Try It — SQL to MongoDB Query
Related Topics
Convert SQL LEFT JOIN to MongoDB $lookup with Preserved Nulls
Migration
Convert SQL GROUP BY with COUNT to MongoDB $group with $sum
Aggregation
Convert SQL WHERE with Comparison Operators to MongoDB
Filtering
Convert SQL ORDER BY to MongoDB sort() or $sort Stage
Migration
Convert SQL LIMIT and OFFSET to MongoDB limit() and skip()
Migration