Delete and TTL Indexes

Introduction

Removing data is as important as inserting it. MongoDB offers deleteOne, deleteMany, and collection-level drop. For temporary data—sessions, OTP codes, raw events—TTL indexes expire documents automatically. This chapter covers safe deletes, soft-delete patterns, and TTL setup.

Prerequisites

deleteOne and deleteMany

javascript
use hello_demo
 
// Remove one matching document
db.posts.deleteOne({ slug: "draft-notes" })
 
// Remove all unpublished drafts
db.posts.deleteMany({ published: false })
 
// DANGEROUS — empty filter deletes everything in collection
// db.posts.deleteMany({})

Return value: deletedCount.

Warning

Always Filter in Production

Run countDocuments(filter) before deleteMany in ops scripts. Empty filter {} wipes the collection.

Soft Delete Pattern

Keep rows for audit; hide in queries:

javascript
db.posts.updateOne(
  { slug: "mongodb-basics" },
  { $set: { deletedAt: new Date() } }
)
 
// Application queries exclude soft-deleted
db.posts.find({ deletedAt: { $exists: false } })
db.posts.find({ deletedAt: null })

Restore:

javascript
db.posts.updateOne(
  { slug: "mongodb-basics" },
  { $unset: { deletedAt: "" } }
)

Relational equivalent: deleted_at column in MySQL UPDATE/DELETE patterns.

findOneAndDelete

Atomic delete and return removed doc:

javascript
db.sessions.findOneAndDelete({ token: "abc123" })

Useful for one-time tokens or queue consumption patterns.

drop Collection and Database

javascript
// Remove entire collection and its indexes
db.temp_logs.drop()
 
// Remove database — all collections gone
db.getSiblingDB("hello_demo").dropDatabase()

Irreversible on standalone without backup—use mongodump first — Insert and Import Export.

TTL Indexes (Time To Live)

MongoDB deletes documents when indexed date field + expireAfterSeconds elapses.

Session example

javascript
db.sessions.createIndex(
  { createdAt: 1 },
  { expireAfterSeconds: 3600 }
)
 
db.sessions.insertOne({
  userId: "user-1",
  token: "secret",
  createdAt: new Date()
})

Background thread removes expired documents—may lag slightly; not a real-time timer per millisecond.

OTP / verification codes

javascript
db.verification_codes.createIndex(
  { expiresAt: 1 },
  { expireAfterSeconds: 0 }
)
 
db.verification_codes.insertOne({
  email: "ada@example.com",
  code: "482910",
  expiresAt: new Date(Date.now() + 10 * 60 * 1000)
})

expireAfterSeconds: 0 expires at absolute time in expiresAt.

TTL rules

RuleDetail
Field typeDate or array of dates
Single fieldOne TTL index per collection
Not instantMonitor lag under load
Replica setsTTL runs on primary

Compare Redis key TTL — Redis expiration for cache; MongoDB TTL for durable short-lived documents.

When to Use TTL vs Application Delete

ApproachBest for
TTL indexSessions, logs, temp tokens, cache-like documents in MongoDB
Scheduled jobComplex rules, archival to cold storage
Soft deleteUser-facing "undo", compliance retention

Cleanup Before Drop

Dev reset:

javascript
db.posts.deleteMany({})
db.posts.dropIndexes()

Production: never dropDatabase without backup and approval.

Common Mistakes

MistakeFix
TTL on string dateConvert to Date
Expect sub-second expiryTTL is best-effort (~60s granularity historically)
TTL field updated accidentallyResets expiry clock
deleteMany({}) in scriptRequire explicit confirmation flag

Post-Chapter Checklist

  • deleteOne / deleteMany with explicit filters
  • Soft delete with deletedAt + query filter
  • TTL index on sessions or verification_codes
  • You know drop() vs deleteMany

FAQ

Recover deleted documents?

Only from backup (mongorestore) or if soft-deleted.

TTL vs capped collections?

Capped collections are FIFO fixed size—legacy; TTL is more flexible.

Delete performance?

Large deletes may block; batch delete by _id range in maintenance window.

GDPR "right to be forgotten"?

deleteOne({ userId }) across collections or anonymize fields—design in Data modeling.

Redis DEL vs MongoDB delete?

Redis DEL removes cache keys instantly; MongoDB delete removes persisted documents—different layers.