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?
Let’s explore why, and when you should consider switching to { field: null }
instead.
$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.
{ 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.
$exists: false
is okayThere’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.
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.
Query Pattern | Index Friendly? | Notes |
---|---|---|
{ isWeak: { $exists: false } } | ❌ No | Needs full document fetch |
{ isWeak: null } | ✅ Yes | Works with compound indexes |
$text + $exists | ✅ Often | Safe if $text is selective |
Partial index on $exists: false | ❌ Not allowed | Must 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.