MongoDB is a NoSQL database, so it does not use the traditional SQL JOIN statement. However, MongoDB provides the $lookup aggregation stage to perform a join between two collections.
1. Basic Syntax of $lookup
db.collection1.aggregate([
{
$lookup: {
from: "collection2",
localField: "field1",
foreignField: "field2",
as: "result"
}
}
])
Meaning of fields
| Field | Meaning |
|---|---|
from | Collection to join |
localField | Field from the current collection |
foreignField | Matching field from the other collection |
as | Name of the resulting array |
2. Example: Student and Department
Suppose we have two collections:
students
db.students.insertMany([
{ _id: 1, name: "Rahul", dept_id: 101 },
{ _id: 2, name: "Priya", dept_id: 102 },
{ _id: 3, name: "Amit", dept_id: 101 }
])
departments
db.departments.insertMany([
{ _id: 101, dept_name: "Computer Science" },
{ _id: 102, dept_name: "Information Technology" }
])
3. Perform JOIN using $lookup
db.students.aggregate([
{
$lookup: {
from: "departments",
localField: "dept_id",
foreignField: "_id",
as: "department"
}
}
])
Output
[
{
_id: 1,
name: "Rahul",
dept_id: 101,
department: [
{
_id: 101,
dept_name: "Computer Science"
}
]
},
{
_id: 2,
name: "Priya",
dept_id: 102,
department: [
{
_id: 102,
dept_name: "Information Technology"
}
]
}
]
The department field contains the matching documents from the departments collection.
4. $lookup with $unwind
Because $lookup normally returns the matching records as an array, we can use $unwind to convert it into an individual object.
db.students.aggregate([
{
$lookup: {
from: "departments",
localField: "dept_id",
foreignField: "_id",
as: "department"
}
},
{
$unwind: "$department"
}
])
Result
{
_id: 1,
name: "Rahul",
dept_id: 101,
department: {
_id: 101,
dept_name: "Computer Science"
}
}
5. Display Only Required Fields
We can use $project after $lookup.
db.students.aggregate([
{
$lookup: {
from: "departments",
localField: "dept_id",
foreignField: "_id",
as: "department"
}
},
{
$unwind: "$department"
},
{
$project: {
_id: 0,
name: 1,
"department.dept_name": 1
}
}
])
Output
{
name: "Rahul",
department: {
dept_name: "Computer Science"
}
}
6. SQL JOIN vs MongoDB $lookup
| SQL | MongoDB |
|---|---|
JOIN | $lookup |
ON | localField + foreignField |
SELECT | $project |
WHERE | $match |
| Multiple rows | Array of matching documents |
GROUP BY | $group |
SQL Example
SELECT students.name, departments.dept_name
FROM students
JOIN departments
ON students.dept_id = departments.id;
MongoDB Equivalent
db.students.aggregate([
{
$lookup: {
from: "departments",
localField: "dept_id",
foreignField: "_id",
as: "department"
}
},
{
$unwind: "$department"
},
{
$project: {
name: 1,
"department.dept_name": 1
}
}
])
Important point for exams
MongoDB performs JOIN-like operations using the
$lookupstage of the aggregation pipeline.$lookupmatches documents from one collection with documents from another collection based on specified fields.
Short answer:$lookup in MongoDB is mainly used to perform a JOIN operation between collections.
The important point is: $unwind works on arrays, not ordinary objects. An object itself is not “unwound.”
1. Array
An array contains multiple values:
{
name: "Rahul",
subjects: ["MongoDB", "PHP", "Python"]
}
Using:
db.students.aggregate([
{ $unwind: "$subjects" }
])
produces 3 documents:
{ name: "Rahul", subjects: "MongoDB" }
{ name: "Rahul", subjects: "PHP" }
{ name: "Rahul", subjects: "Python" }
So:
Array → $unwind → multiple documents
2. Object
An object contains key-value pairs:
{
name: "Rahul",
address: {
city: "Rajkot",
state: "Gujarat"
}
}
Here, address is an object, not an array.
You do not use $unwind on it:
{ $unwind: "$address" } // Not useful
You can directly access its fields:
db.students.find(
{},
{
name: 1,
"address.city": 1
}
)
Result:
{
name: "Rahul",
address: {
city: "Rajkot"
}
}
Array vs Object
| Feature | Array | Object |
|---|---|---|
| Contains | Multiple values/elements | Key-value pairs |
| Example | ["PHP", "MongoDB"] | {city:"Rajkot", state:"Gujarat"} |
$unwind | ✅ Used | ❌ Not normally used |
Result of $unwind | One document per array element | No decomposition |
| Access | $subjects | $address.city |
Very important for $lookup
$lookup normally produces an array:
course: [
{
_id: 101,
course_name: "BCA"
}
]
Therefore, we often write:
{
$unwind: "$course"
}
After $unwind:
course: {
_id: 101,
course_name: "BCA"
}
So remember:
Array = collection of elements →
$unwindcan separate them.
Object = one structured value → access its fields directly.