Practice MongoDB
You’ve read the lessons — now prove it. Take the quiz for instant feedback, then work the coding exercises. Try each one yourself before you reveal the solution.
Quick quiz
16 questions- 1
In MongoDB, what is the equivalent of a row in a relational database?
- 2
Which method inserts multiple documents into a collection at once?
- 3
What does
db.students.find({ level: { $gt: 2 } })return? - 4
Which operator checks whether a field's value matches any value in a given array?
- 5
How do you retrieve only the
namefield (plus_id) from all student documents? - 6
Which update operator adds a value to a numeric field without replacing it?
- 7
What does the
$pushupdate operator do? - 8
In an aggregation pipeline, which stage filters documents (like a WHERE clause)?
- 9
What is the output of this aggregation stage?
{ $group: { _id: "$level", count: { $sum: 1 } } } - 10
When should you embed a related document rather than reference it by ID?
- 11
In the ESR rule for compound indexes, what does each letter stand for?
- 12
What is the main disadvantage of using
skip()for deep pagination? - 13
Which accumulator in
$groupcollects all matching field values into an array? - 14
What does the Bucket pattern solve in MongoDB schema design?
- 15
Which of the following correctly creates a text index on both the
titleandbodyfields? - 16
When using the Extended Reference pattern, which type of field should you avoid embedding in the referencing document?
Coding exercises
8 tasksCompound Index: ESR Rule
MediumCreate a compound index on the students collection that efficiently supports this query:
``
db.students.find({ enrolled: true, age: { $gt: 18 } }).sort({ age: 1 })
Apply the ESR rule (Equality → Sort → Range) when ordering the fields. Then run explain("executionStats") to confirm an IXSCAN` is used.
// Create the compound index
db.students.createIndex({ /* fields in ESR order */ });
// Run the query with explain
db.students.find({ enrolled: true, age: { $gt: 18 } })
.sort({ age: 1 })
.explain("executionStats");💡 Show hint
Equality field (enrolled) goes first, then the sort/range field (age). Both ascending.
✅ Show solution
// ESR order: enrolled (equality) → age (sort + range)
db.students.createIndex({ enrolled: 1, age: 1 });
db.students.find({ enrolled: true, age: { $gt: 18 } })
.sort({ age: 1 })
.explain("executionStats");
// Check winningPlan.stage === "IXSCAN"Cursor Pagination
MediumImplement a nextPage(lastId, pageSize) function using range (cursor) pagination on the students collection sorted by _id. The function should return an object { docs, lastId } where lastId is the _id of the last returned document (or null if the page is empty). On the first call, pass null as lastId.
async function nextPage(lastId, pageSize) {
// build filter
// query, sort, limit
// return { docs, lastId }
}💡 Show hint
When lastId is null use an empty filter {}; otherwise use { _id: { $gt: lastId } }. Always sort by { _id: 1 } and .limit(pageSize).
✅ Show solution
async function nextPage(lastId, pageSize) {
const filter = lastId ? { _id: { $gt: lastId } } : {};
const docs = await db.collection("students")
.find(filter)
.sort({ _id: 1 })
.limit(pageSize)
.toArray();
const newLastId = docs.length > 0 ? docs[docs.length - 1]._id : null;
return { docs, lastId: newLastId };
}Transaction: Transfer Credits
HardUsing the MongoDB Node.js driver (not Mongoose), write a transferCredits(client, fromId, toId, amount) function that:
1. Starts a session and a transaction.
2. Deducts amount from the credits field of the fromId wallet document.
3. Adds amount to the credits field of the toId wallet document.
4. Commits on success, aborts on any error, and always ends the session.
Both updateOne calls must use the same session.
async function transferCredits(client, fromId, toId, amount) {
const session = client.startSession();
try {
session.startTransaction();
const wallets = client.db("bank").collection("wallets");
// deduct
// add
// commit
} catch (err) {
// abort
throw err;
} finally {
// end session
}
}💡 Show hint
Pass { session } as the options object to every updateOne call so both operations participate in the same transaction.
✅ Show solution
async function transferCredits(client, fromId, toId, amount) {
const session = client.startSession();
try {
session.startTransaction();
const wallets = client.db("bank").collection("wallets");
await wallets.updateOne(
{ _id: fromId },
{ $inc: { credits: -amount } },
{ session }
);
await wallets.updateOne(
{ _id: toId },
{ $inc: { credits: amount } },
{ session }
);
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
throw err;
} finally {
session.endSession();
}
}Find Students by Skill
EasyWrite a query that returns the name and skills of every student whose skills array contains "JavaScript". Sort the results alphabetically by name.
db.students.find(
{ /* filter */ },
{ /* projection */ }
).sort({ /* sort */ });💡 Show hint
Use the $in operator (or simply a direct equality match on an array field) to check array membership, and pass a projection object as the second argument to find().
✅ Show solution
db.students.find(
{ skills: "JavaScript" },
{ name: 1, skills: 1, _id: 0 }
).sort({ name: 1 });Insert a New Student
EasyInsert a new student document into the students collection with the following data:
- name: "Ada Lovelace"
- level: 3
- skills: ["Python", "Math"]
Then, using insertMany, add two more students in a single call.
// Single insert
db.students.insertOne({ /* document */ });
// Bulk insert
db.students.insertMany([ /* documents */ ]);💡 Show hint
insertOne() takes a single document object; insertMany() takes an array of document objects.
✅ Show solution
// Single insert
db.students.insertOne({
name: "Ada Lovelace",
level: 3,
skills: ["Python", "Math"]
});
// Bulk insert
db.students.insertMany([
{ name: "Alan Turing", level: 4, skills: ["C", "Algorithms"] },
{ name: "Grace Hopper", level: 5, skills: ["COBOL", "Debugging"] }
]);Update Student Data
MediumFor the student named "Ada Lovelace":
1. Set her level to 4.
2. Add "TypeScript" to her skills array (avoid duplicates).
3. Increment her level by 1 more (making it 5).
Write three separate updateOne calls.
// 1. Set level
db.students.updateOne(
{ name: "Ada Lovelace" },
{ /* update */ }
);
// 2. Add skill (no duplicates)
// 3. Increment level💡 Show hint
Use $set to assign a value, $addToSet instead of $push to avoid duplicates, and $inc to increment a numeric field.
✅ Show solution
// 1. Set level to 4
db.students.updateOne(
{ name: "Ada Lovelace" },
{ $set: { level: 4 } }
);
// 2. Add skill without duplicates
db.students.updateOne(
{ name: "Ada Lovelace" },
{ $addToSet: { skills: "TypeScript" } }
);
// 3. Increment level by 1
db.students.updateOne(
{ name: "Ada Lovelace" },
{ $inc: { level: 1 } }
);Aggregate: Count Students per Level
MediumUsing the aggregation pipeline, produce a report that shows how many students are at each level, but only include levels where there are at least 2 students. Sort the output from highest count to lowest.
db.students.aggregate([
// Stage 1: group by level and count
// Stage 2: filter groups with count >= 2
// Stage 3: sort by count descending
]);💡 Show hint
Use $group with $sum: 1 to count, then $match on the count field, and finally $sort with -1 for descending order.
✅ Show solution
db.students.aggregate([
{
$group: {
_id: "$level",
count: { $sum: 1 }
}
},
{
$match: { count: { $gte: 2 } }
},
{
$sort: { count: -1 }
}
]);Full Pipeline: Top Skilled Students
HardBuild an aggregation pipeline that:
1. Filters students at level 3 or above.
2. Adds a computed field skillCount equal to the size of the skills array.
3. Filters to only students with 3 or more skills.
4. Projects only name, level, and skillCount (exclude _id).
5. Sorts by skillCount descending, then by name ascending.
6. Limits to the top 5 results.
Also create an index on level to support the first filter stage efficiently.
// Index
db.students.createIndex({ /* fields */ });
// Pipeline
db.students.aggregate([
// your stages here
]);💡 Show hint
Use $match for filtering, $addFields with $size to compute the array length, $project with 0 to exclude _id, and chain $sort then $limit at the end.
✅ Show solution
// Create index on level for efficient $match
db.students.createIndex({ level: 1 });
// Aggregation pipeline
db.students.aggregate([
{
$match: { level: { $gte: 3 } }
},
{
$addFields: {
skillCount: { $size: "$skills" }
}
},
{
$match: { skillCount: { $gte: 3 } }
},
{
$project: {
_id: 0,
name: 1,
level: 1,
skillCount: 1
}
},
{
$sort: { skillCount: -1, name: 1 }
},
{
$limit: 5
}
]);