R
Rishtaara
MongoDB Fundamentals
Lesson 14 of 40Article12 min

Delete Operations

deleteOne() removes the first document matching the filter. deleteMany() removes all matches. Both return deletedCount so you know how many documents were removed.

deleteOne() and deleteMany()

deleteOne() removes the first document matching the filter. deleteMany() removes all matches. Both return deletedCount so you know how many documents were removed.

Real-life example: deleteOne is like removing one expired product from a shelf. deleteMany is like clearing every item tagged 'discontinued'.

deleteMany({}) wipes every document but keeps the collection and its indexes. drop() removes the collection entirely including indexes.
Delete single and multiple documents
use rishtaara

// Delete one product by name
const one = db.products.deleteOne({ name: "USB-C Hub" })
// { acknowledged: true, deletedCount: 1 }

// Delete all inactive students
const many = db.students.deleteMany({ active: false })
// { acknowledged: true, deletedCount: 42 }

// Delete with compound filter
db.orders.deleteMany({
  status: "cancelled",
  createdAt: { $lt: ISODate("2023-01-01T00:00:00Z") }
})

// No matches — deletedCount: 0 (not an error)
db.products.deleteOne({ name: "Does Not Exist" })
Delete entire collection contents
// Remove ALL documents in a collection (keeps indexes)
db.temp_logs.deleteMany({})

// Faster alternative — drops and recreates empty collection
db.temp_logs.drop()

// ⚠️ Never run deleteMany({}) on production users or orders
// without a backup and explicit approval

Delete database

To remove an entire database, switch to it and run db.dropDatabase(). This is irreversible without backups.

Real-life example: dropDatabase() is like demolishing a warehouse — everything inside is gone, not just emptying the shelves.

  • Atlas backups and point-in-time restore protect against accidental drops.
  • Use separate databases for dev, staging, and production.
  • Never expose dropDatabase permissions to application users.
Drop database in mongosh
// List databases first
show dbs

// Switch to the database you want to remove
use staging_rishtaara

// Confirm what you are about to destroy
show collections

// Drop it
db.dropDatabase()
// { ok: 1, dropped: "staging_rishtaara" }

// Production safety: restrict dropDatabase to admin roles only

Soft delete pattern — production best practice

Production apps rarely hard-delete user data. Instead, set a deletedAt timestamp or isDeleted flag and filter it out in every read query. This enables undo, audit trails, and GDPR-compliant delayed purges.

Real-life example: Soft delete is like moving a file to the Recycle Bin instead of permanently deleting it — you can restore it later if needed.

Rishtaara recommendation: soft delete for users, orders, and content. Hard delete only for temp data, sessions, and after legal retention periods.
Implement soft delete
// Soft delete a user
db.users.updateOne(
  { _id: ObjectId("665a1b2c3d4e5f6789012345") },
  {
    $set: {
      deletedAt: new Date(),
      active: false
    }
  }
)

// ALWAYS exclude soft-deleted in reads
db.users.find({
  deletedAt: { $exists: false },
  active: true
})

// Or explicitly check null
db.users.find({
  $or: [
    { deletedAt: { $exists: false } },
    { deletedAt: null }
  ]
})

// Restore (undo soft delete)
db.users.updateOne(
  { _id: ObjectId("665a1b2c3d4e5f6789012345") },
  { $unset: { deletedAt: "" }, $set: { active: true } }
)
TTL index for delayed hard delete
// Auto-purge soft-deleted records after 90 days
db.users.createIndex(
  { deletedAt: 1 },
  { expireAfterSeconds: 90 * 24 * 60 * 60 }
)

// MongoDB removes documents when deletedAt + 90 days passes
// Use for logs, sessions, and GDPR right-to-erasure workflows