⚡️ MongoDB $exists vs null: Query Optimization Deep Dive
Adam C. |

When working with MongoDB queries, it's common to use $exists: false to filter out documents missing a certain field. But did you know this pattern can dramatically affect performance — depending on the context?

Photo by Pushparaj S on Unsplash

Let’s explore why, and when you should consider switching to { field: null } instead.

🔍 The Problem with $exists: false

Consider a collection with 1M swim records and a query like this:

db.records.find({
  event: "50FR",
  type: "LCM",
  ageGroup: "13-14",
  isWeak: { $exists: false }
})

This looks harmless. But if isWeak is not indexed, MongoDB must:

Use the index to filter event, type, ageGroup

Then fetch every matching document to check if isWeak is missing

This fetch is expensive — especially under load or on cold cache. It turns a fast index scan into a slow document scan.

✅ The Better Alternative: { field: null }

You can rewrite the query as:

db.records.find({
  event: "50FR",
  type: "LCM",
  ageGroup: "13-14",
  isWeak: null
})

This has two advantages:

It matches both missing fields and fields explicitly set to null

MongoDB can use an index if isWeak is part of it

That means your query may become index-covered, avoiding document fetches entirely.

⚠️ When $exists: false is okay

There’s one major exception: text search.

Consider this search query:

db.swimmers.find({
  $text: { $search: "cai" },
  dupSlug: { $exists: false }
})

This performs well even without indexing dupSlug because:

MongoDB runs the $text filter first using the text index

It then filters the small result set using dupSlug

So $exists: false is okay when it follows a highly selective condition like text search.

💡 Indexing Strategy Tip

If you frequently filter by missing fields:

Consider using { field: null } in queries

And include field in your compound index

Example:

db.best_strokes.createIndex({
  event: 1,
  type: 1,
  ageGroup: 1,
  sex: 1,
  isWeak: 1
})

Then filter with isWeak: null for full index optimization.

🧪 TL;DR

Query PatternIndex Friendly?Notes
{ isWeak: { $exists: false } }❌ NoNeeds full document fetch
{ isWeak: null }✅ YesWorks with compound indexes
$text + $exists✅ OftenSafe if $text is selective
Partial index on $exists: false❌ Not allowedMust use null

Need help tuning your swim data queries? I’ve got scripts, explain outputs, and index plans for all major patterns. Let me know if you want a follow-up post with explain() walkthroughs.