$lookup (Joins in MongoDB)
Combine data from two collections in a single aggregation pipeline using $lookup, MongoDB's equivalent of a left outer join.
What $lookup Does
$lookup performs a left outer join, pulling in matching documents from another collection based on a field comparison — MongoDB's answer to a relational JOIN, used specifically within the aggregation framework.
A Basic $lookup
db.orders.aggregate([ { $lookup: { from: "users", // the collection to join with localField: "userId", // field on "orders" foreignField: "_id", // field on "users" as: "customer", // name of the new array field holding matches }, },]);Click Run to see what this code prints.
$lookup adds the matched documents as an array (customer, above), even when you expect exactly one match — this reflects that a join could, in general, match multiple documents.
$unwind to Flatten Results
When you know a $lookup will match exactly one document, $unwind flattens that single-element array into a plain embedded object, which is often more convenient to work with.
db.orders.aggregate([ { $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "customer" } }, { $unwind: "$customer" }, // customer becomes an object instead of a one-item array]);A Pipeline-Based $lookup
A more advanced form of $lookup accepts its own sub-pipeline, letting you filter or reshape the joined data before it's attached — useful for more selective joins.
db.orders.aggregate([ { $lookup: { from: "users", let: { userId: "$userId" }, pipeline: [ { $match: { $expr: { $eq: ["$_id", "$$userId"] } } }, { $project: { name: 1, _id: 0 } }, ], as: "customer", }, },]);When to Use $lookup vs Embedding
As covered in the schema design lessons, embedding is often preferred for data that's always read together. $lookup is the right tool specifically when the data was deliberately referenced (not embedded) — because it's large, independently queried, or shared — and you occasionally need to combine it for a specific report or view.
FAQs
It can be more expensive, especially across large collections without good indexes on the join fields — MongoDB's general design philosophy favors embedding over frequent $lookup use for exactly this reason.
It can join collections within the same database only by default; cross-database and cross-cluster scenarios need different tooling (like MongoDB's $merge or application-level joins).
Summary
$lookup enables join-like operations across collections when data has been deliberately referenced rather than embedded. Next, you'll combine everything covered so far into more advanced, multi-stage aggregation pipelines.