Element Operators
Element operators query documents based on the presence or type of a field, rather than its value.
$exists — Field Presence
$exists checks whether a field exists in the document (regardless of its value, including null).
$exists examples
// Find documents where the 'phone' field EXISTS (any value, even null)
db.users.find({ phone: { $exists: true } })
// Find documents where the 'phone' field does NOT EXIST
db.users.find({ phone: { $exists: false } })
// Find documents where the optional 'middleName' field is present
db.people.find({ middleName: { $exists: true } })
// Combine $exists with other operators
// Find documents where 'deletedAt' is present and is a date
db.posts.find({
deletedAt: { $exists: true, $type: 'date' }
})null vs Missing Fields
null vs missing
// { field: null } matches BOTH documents with null value AND documents missing the field
db.users.find({ phone: null })
// Matches: { name: 'Alice', phone: null } <- null value
// Matches: { name: 'Bob' } <- field is absent
// Does NOT match: { name: 'Carol', phone: '555-1234' }
// { field: { $eq: null } } — same behavior as above
db.users.find({ phone: { $eq: null } })
// To match ONLY documents where field EXISTS but is null:
db.users.find({ phone: { $exists: true, $eq: null } })
// Matches: { name: 'Alice', phone: null } <- null value (yes)
// Does NOT match: { name: 'Bob' } <- field is absent (no)
// To match ONLY documents where field is MISSING:
db.users.find({ phone: { $exists: false } })Query | Matches null | Matches missing |
|---|---|---|
{field: null} | Yes | Yes |
{field: {$eq: null}} | Yes | Yes |
{field: {$exists: false}} | No | Yes |
{field: {$exists: true, $eq: null}} | Yes | No |
$type — Field Type
$type selects documents where the field value is of the specified BSON type. You can use the type name string or numeric type code.
$type examples
// Find documents where 'name' is stored as a string
db.users.find({ name: { $type: 'string' } })
// Find documents where 'age' is stored as a number (int or double)
db.users.find({ age: { $type: 'number' } })
// 'number' is an alias that matches double, int, long, and decimal
// Find documents where 'tags' is stored as an array
db.posts.find({ tags: { $type: 'array' } })
// Using numeric type codes (less readable but equivalent)
db.users.find({ name: { $type: 2 } }) // 2 = string
db.users.find({ age: { $type: 16 } }) // 16 = 32-bit intBSON Type Aliases
Alias | BSON Type | Number |
|---|---|---|
"double" | Double | 1 |
"string" | String | 2 |
"object" | Object | 3 |
"array" | Array | 4 |
"binData" | Binary | 5 |
"bool" | Boolean | 8 |
"date" | Date | 9 |
"null" | Null | 10 |
"int" | 32-bit integer | 16 |
"long" | 64-bit integer | 18 |
"decimal" | Decimal128 | 19 |
"objectId" | ObjectId | 7 |
Using $type for Array Detection
Find array fields
// Find documents where 'tags' is stored as an array
// Useful when the same field may be a string in some docs and an array in others
db.posts.find({ tags: { $type: 'array' } })
// Find documents where 'tags' is a plain string (not an array)
db.posts.find({ tags: { $type: 'string' } })
// Useful during migrations where schema was inconsistent:
// Some old docs: { tags: 'mongodb' } <- string
// Some new docs: { tags: ['mongodb'] } <- arrayChecking for Mixed Types
$type with multiple types
// Match documents where 'age' is stored as any numeric type
db.users.find({
age: { $type: ['int', 'double', 'long'] }
})
// Match documents where a field is either a string or null
db.products.find({
description: { $type: ['string', 'null'] }
})
// Numeric type codes also work in arrays
db.users.find({
age: { $type: [16, 18, 1] } // int32, int64, double
})Practical Use: Data Cleanup Queries
Find incorrectly typed fields
// Find documents where 'price' was accidentally stored as a string
// (should be a number)
db.products.find({ price: { $type: 'string' } })
// Find documents where 'createdAt' was stored as a string instead of Date
db.users.find({ createdAt: { $type: 'string' } })
// Find documents where 'isActive' is stored as a string instead of boolean
db.users.find({ isActive: { $type: 'string' } })
// Count how many documents have the wrong type (for impact assessment)
db.products.countDocuments({ price: { $type: 'string' } })
// Fix them: convert string prices to numbers
db.products.find({ price: { $type: 'string' } }).forEach(doc => {
db.products.updateOne(
{ _id: doc._id },
{ $set: { price: parseFloat(doc.price) } }
)
})$exists: true combined with $type is a powerful way to validate that a field is both present AND the correct type — useful for data quality checks.$type operator compares the stored BSON type, not the JavaScript type. MongoDB's BSON types are more granular than JavaScript types (e.g., int32 vs int64 vs double are all "number" in JS).