MongoDB Null and Missing Fields

Distinguish MongoDB null from absent fields, handle array matches, use exact aggregation types, and design clear update and fallback behavior.

Lesson 26 of 43Intermediate10 min read

A flexible document schema can represent an unknown value in several ways: a field can be absent, explicitly null, an empty string, or an empty array. Those representations are not interchangeable. A filter written with SQL-like assumptions can select more documents than intended because MongoDB equality to null also matches a missing field. This reference targets MongoDB 8.0 and uses complete mongosh fixtures in an isolated teaching database. Each fixture resets only its named demo collection. The examples deliberately include an array containing null, making the difference between query matching and exact field-type inspection visible.

Assign a meaning to each stored representation

An absent field can mean not collected, not applicable, or a legacy document that predates the field. A stored null can mean an explicitly unknown value. An empty string can mean the user supplied an empty text answer, while an empty array can mean a known list with no entries. The database does not choose those business meanings for you. Document them in the schema and API contract so writes and reads use the same interpretation.

Do not automatically convert every falsy JavaScript value to null. Zero and false can be valid measurements or decisions. A patch endpoint should distinguish an omitted property from a property explicitly set to null. Omitting a patch field often means leave the stored value unchanged, while a documented null may mean clear or mark unknown. When data arrives from a typed application, remember that optional properties describe a source-language contract but do not eliminate these stored distinctions. Validate the chosen representation at the database boundary when consistency matters.

Know the broad equality-to-null rule

A query such as { value: null } can match documents where value is explicitly null and documents where it is absent. Query matching also has array semantics: an array containing null can satisfy that equality. This is useful when the intended question is broad, but surprising when the question was which documents store a scalar null. Do not describe equality-to-null as an exact BSON type test.

$exists: false selects absence. $exists: true requires the field to be present, including when its value is null. In the demonstrated field shape, $ne: null selects present values that do not match null. Combine conditions according to the actual question rather than placing exists into every query by habit. Nested paths and arrays can introduce further matching behavior, so design dedicated fixtures when those structures matter. The main example keeps the path simple and explicitly includes a scalar zero, an empty array, and a null-containing array to prevent an accidentally oversimplified explanation.

Use exact type inspection when shape matters

The query $type operator can match a field's type and can also match qualifying elements within arrays. Therefore a query type check for BSON null is not automatically a scalar-field check. Aggregation $type examines the expression value itself. It returns a distinct missing result for an absent field, null for a scalar null, and array for an array even when that array contains a null element. Use an expression-based type comparison when the requirement is the exact outer field type.

This distinction is especially valuable in audits and migrations. First measure which representations exist, then choose a transformation that preserves intended data. Do not rewrite null-containing arrays into missing scalar values merely because a broad filter matched them. A schema validator can restrict a field to a defined scalar type or null and optionally require its presence. Query logic becomes simpler when the stored shape is enforced, but legacy data still needs an explicit migration policy rather than an assumption that every old document already complies.

Choose fallback and clearing behavior deliberately

$ifNull supplies a fallback for null-like or absent input according to its documented aggregation behavior. It does not replace every falsy value, so zero remains zero and an empty array remains an array. That is usually preferable to a generic truthiness fallback for numeric reporting. Decide whether a displayed fallback should hide the difference between unknown and absent, or whether an audit needs to preserve those states separately. A display label is not necessarily the right stored representation.

$set with null retains the field and stores a null value. $unset removes the field. These operations can therefore change which filters match and which validation rules apply. A required field can remain present under set-to-null if null is an allowed type, while unset can violate a presence requirement. Test the exact transition your API promises. The practice removes one email field and checks absence separately from null, making it clear that clearing a field is a schema choice rather than a cosmetic change to how it prints.

MongoDB documentation and version notes

MongoDB Null and Missing Fields Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Matching null, missing and scalar-null fields

Run the complete script in mongosh against a disposable MongoDB 8.0 fixture server. It uses codevra_ref_null.values and resets that collection before insertion. ids prints only sorted numeric identifiers, avoiding shell-specific document formatting. Broad null equality includes the null-containing array. Query $type also includes that array, while the aggregation expression selects only the scalar null document. The last filter preserves the zero and empty array values. The displayed output is the expected result for these controlled documents; engine execution is recorded separately by the verification run rather than inferred from the presence of a code fence.

MongoDB Shell
const demo = db.getSiblingDB('codevra_ref_null');
const values = demo.getCollection('values');
values.deleteMany({});
values.insertMany([{ _id: 1, value: null }, { _id: 2 },
  { _id: 3, value: 0 }, { _id: 4, value: [] }, { _id: 5, value: [null] }]);
function ids(filter) { return values.find(filter).sort({ _id: 1 }).toArray().map(row => row._id); }
print(JSON.stringify(ids({ value: null })));
print(JSON.stringify(ids({ value: { $exists: false } })));
print(JSON.stringify(ids({ value: { $type: 10 } })));
print(JSON.stringify(ids({ $expr: { $eq: [{ $type: '$value' }, 'null'] } })));
print(JSON.stringify(ids({ value: { $ne: null } })));
Output
[1,2,5]
[2]
[1,5]
[1]
[3,4]

MongoDB Null and Missing Fields in Real Projects

Classify cleared, empty and missing contact emails

Model four contact records: an explicit unknown email, an absent email, an empty email string, and a supplied address. Remove the supplied address with $unset, then predict the missing-field and exact scalar-null groups. The solution uses a separate demo collection, so it can run without the main fixture. The empty string remains present and is neither missing nor null. This exercise demonstrates why a patch contract must specify whether clearing means null or removal. Changing the operation to $set: { email: null } would produce a different answer, which is a useful deliberate mutation after checking the original result.

MongoDB Shell
const contacts = db.getSiblingDB('codevra_ref_null').getCollection('contacts');
contacts.deleteMany({});
contacts.insertMany([{ _id: 1, email: null }, { _id: 2 },
  { _id: 3, email: '' }, { _id: 4, email: 'ada@example.test' }]);
contacts.updateOne({ _id: 4 }, { $unset: { email: '' } });
print(JSON.stringify(contacts.find({ email: { $exists: false } }).sort({ _id: 1 }).toArray().map(x => x._id)));
print(JSON.stringify(contacts.find({ $expr: { $eq: [{ $type: '$email' }, 'null'] } }).sort({ _id: 1 }).toArray().map(x => x._id)));
print(contacts.findOne({ _id: 3 }).email === '');
Output
[2,4]
[1]
true

MongoDB Null and Missing Fields FAQ

Why does value: null match a missing field?

That is the documented broad null-equality behavior. It can answer a question about unavailable values, but it does not prove a field stores scalar BSON null. Use exists for presence questions and an exact expression type check when outer shape matters. Include missing and array cases in a fixture before using the filter for a migration or an operation that changes many documents.

Does query $type: 10 select only scalar null?

Not when arrays are possible. Query type matching can include an array with a null element. Aggregation $type inspects the expression's outer value, so comparing it with null identifies the scalar case in this fixture. If your schema forbids arrays for the field, the practical result can be simpler, but make that schema assumption explicit rather than teaching it as a universal matching rule.

Is an empty string the same as an absent email?

No. The field exists and stores a string whose length is zero. Whether that is allowed depends on the contact schema. Reject it during validation if the application needs either a meaningful address or a defined unknown state. Do not rely on a null filter to detect every incomplete contact record. Business completeness often needs its own validation predicate.

Should a clear operation use null or unset?

Choose according to the documented schema and patch semantics. Null keeps the field present with an explicit value; unset removes it. Required-field validation, presence queries, and migrations can distinguish those results. The practice uses unset intentionally. A different product may need null to record an explicit unknown answer. Consistency across writers and readers matters more than choosing one representation for every possible application.

MongoDB Null and Missing Fields Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

After the worked $unset on contact 4, which contacts have no email field?

MongoDB Null and Missing Fields Quiz

2 questions to check what you learned.

  1. Which filter identifies absence rather than a stored null?

    • { value: { $exists: false } }
    • { value: null }
    • { value: { $exists: true } }
  2. Why use aggregation $type for the scalar-null audit?

    • It distinguishes the outer array value from its null element
    • It converts every array into a scalar automatically
    • It makes missing fields store null permanently