Practical Lab: Build a Course Management Dataset

In this lab, build a small course catalog and practice all four CRUD categories using realistic documents.

Step 1: Create the practice dataset

use mongodb_course_lab

db.courseCatalog.insertMany([
  {
    _id: "course-101",
    title: "MongoDB Fundamentals",
    category: "database",
    level: "beginner",
    price: NumberDecimal("0.00"),
    published: true,
    tags: ["mongodb", "database", "beginner"],
    instructor: {
      name: "Arun",
      city: "Chennai"
    },
    enrolledStudents: 120,
    createdAt: ISODate("2026-10-01T10:00:00Z")
  },
  {
    _id: "course-102",
    title: "MongoDB Aggregation",
    category: "database",
    level: "intermediate",
    price: NumberDecimal("499.00"),
    published: true,
    tags: ["mongodb", "aggregation"],
    instructor: {
      name: "Meera",
      city: "Bengaluru"
    },
    enrolledStudents: 80,
    createdAt: ISODate("2026-10-05T10:00:00Z")
  },
  {
    _id: "course-103",
    title: "React Essentials",
    category: "frontend",
    level: "beginner",
    price: NumberDecimal("299.00"),
    published: false,
    tags: ["react", "javascript"],
    instructor: {
      name: "Ravi",
      city: "Chennai"
    },
    enrolledStudents: 0,
    createdAt: ISODate("2026-10-10T10:00:00Z")
  }
]);

Step 2: Read operations

Find all published courses:

db.courseCatalog.find({ published: true });

Find beginner courses:

db.courseCatalog.find({ level: "beginner" });

Find courses with prices between 100 and 500:

db.courseCatalog.find({
  price: {
    $gte: NumberDecimal("100"),
    $lte: NumberDecimal("500")
  }
});

Find courses taught by instructors in Chennai:

db.courseCatalog.find({ "instructor.city": "Chennai" });

Find courses containing the mongodb tag:

db.courseCatalog.find({ tags: "mongodb" });

Return only titles and prices:

db.courseCatalog.find(
  {},
  { title: 1, price: 1, _id: 0 }
);

Step 3: Update one course

db.courseCatalog.updateOne(
  { _id: "course-102" },
  {
    $set: {
      published: false
    },
    $currentDate: {
      updatedAt: true
    }
  }
);

Verify the result:

db.courseCatalog.findOne({ _id: "course-102" });

Step 4: Update several courses

db.courseCatalog.updateMany(
  { category: "database" },
  { $set: { featured: true } }
);

Inspect the affected documents and the operation result.

Step 5: Update an array

db.courseCatalog.updateOne(
  { _id: "course-101" },
  {
    $addToSet: {
      tags: "developer"
    }
  }
);

Step 6: Upsert a course setting

db.courseSettings.updateOne(
  { courseId: "course-101" },
  {
    $set: {
      theme: "dark"
    },
    $setOnInsert: {
      createdAt: new Date()
    }
  },
  { upsert: true }
);

For a real application, consider a unique index on courseId if each course should have only one settings document.

Step 7: Delete a practice course

Inspect the target:

db.courseCatalog.find({ _id: "course-103" });

Delete it:

db.courseCatalog.deleteOne({ _id: "course-103" });

Step 8: Verify the final state

db.courseCatalog.find().sort({ _id: 1 });

Challenge tasks

  1. Insert a course with an ObjectId identifier instead of a string identifier.
  2. Find all courses that have either the mongodb or react tag.
  3. Find unpublished courses with zero enrollments.
  4. Update the instructor city for one course without replacing the entire instructor document.
  5. Increase the enrollment count of one course by 10.
  6. Remove a deprecated tag from every course that contains it.
  7. Create a unique index on a suitable course field and test duplicate insertion behavior.
  8. Perform an upsert using a stable course key.
  9. Explain the difference between matchedCount and modifiedCount.
  10. Explain why updateMany() is not automatically atomic across the entire collection.
  11. Compare updateOne() with replaceOne().
  12. Describe a safe workflow for deleting all unpublished test courses without affecting published courses.
Run a CRUD verification query
print("Total courses:", db.courseCatalog.countDocuments({}));

print("Published courses:");
printjson(db.courseCatalog.find({ published: true }).toArray());

print("Database courses:");
printjson(db.courseCatalog.find({ category: "database" }).toArray());

print("Course titles and levels:");
printjson(
  db.courseCatalog.find(
    {},
    { title: 1, level: 1, _id: 0 }
  ).toArray()
);
💡 The verification queries count the current documents, filter published courses, select database courses, and project selected fields. In a large production collection, avoid using toArray() on an unbounded query because it materializes the entire result set in memory.
Key Takeaways
  • CRUD operations can be combined to build complete data-management workflows.
  • Filters should identify the intended documents precisely.
  • Projection reduces the fields returned to the application.
  • Updates can target nested documents and array elements.
  • Upserts, deletes, and bulk updates require careful filter and concurrency design.
Knowledge Check

Which operation should you use when changing only the nested field `instructor.city` while preserving the instructor's other fields?