Skip to main content

MongoDB Evaluation Operators

Introduction

MongoDB evaluation operators allow you to perform sophisticated query operations beyond simple matching. These operators enable you to use regular expressions, evaluate JavaScript expressions, check data types, and perform other evaluative operations on your document fields. Mastering these operators significantly expands your query capabilities in MongoDB and allows you to implement complex filtering logic in your applications.

In this guide, we'll explore the main evaluation operators available in MongoDB and how they can be used in practical scenarios.

Key Evaluation Operators

MongoDB provides several evaluation operators to help you build complex queries:

OperatorDescription
$regexMatches fields against regular expression patterns
$exprAllows the use of aggregation expressions within queries
$jsonSchemaValidates documents against a JSON Schema
$modPerforms a modulo operation on field values
$textPerforms text search on string content
$whereUses JavaScript expressions for complex matching logic
$typeSelects documents where field is a specific BSON type

Let's explore each of these operators in detail.

The $regex Operator

The $regex operator allows you to match string fields using regular expressions, which is particularly useful for pattern matching.

Syntax

js
{ field: { $regex: pattern, $options: options } }
// or
{ field: /pattern/options }

Examples

Finding all products whose names contain "phone" (case insensitive):

js
db.products.find({ name: { $regex: /phone/i } })

Finding all users whose email ends with "gmail.com":

js
db.users.find({ email: { $regex: /gmail\.com$/i } })

Finding all documents with a description containing either "amazing" or "incredible":

js
db.reviews.find({ 
description: { $regex: /amazing|incredible/i }
})

Output

js
// For the query finding products containing "phone"
[
{ "_id": ObjectId("5f85a2f88c7ba17a13b73f1c"), "name": "Smartphone X10", "price": 699 },
{ "_id": ObjectId("5f85a3018c7ba17a13b73f1d"), "name": "iPhone 12 Pro", "price": 999 },
{ "_id": ObjectId("5f85a30a8c7ba17a13b73f1e"), "name": "Headphones for Phone", "price": 59 }
]

$regex Options

  • i: Case insensitivity
  • m: Multiline matching
  • s: Allows dot (.) to match newline characters
  • x: Ignores whitespace in the pattern

Performance Considerations

  • Prefix regular expressions (like /^prefix/) can use indexes
  • Non-prefix expressions force collection scans
  • For case-insensitive searches on frequently queried fields, consider storing a lowercase copy of the field and indexing it

The $expr Operator

The $expr operator allows you to use aggregation expressions within query language, enabling comparisons between fields in the same document.

Syntax

js
{ $expr: { <expression> } }

Examples

Finding all sales documents where the amount sold exceeds the target:

js
db.sales.find({
$expr: { $gt: ["$actual", "$target"] }
})

Finding products where the discount price is at least 20% less than the original price:

js
db.products.find({
$expr: {
$gte: [
{ $divide: [
{ $subtract: ["$originalPrice", "$discountPrice"] },
"$originalPrice"
]
},
0.2
]
}
})

Output

js
// For the sales query where actual > target
[
{ "_id": ObjectId("5f8a3e1f8c7ba17a13b73f20"), "salesperson": "Alice", "actual": 12000, "target": 10000 },
{ "_id": ObjectId("5f8a3e2a8c7ba17a13b73f21"), "salesperson": "Bob", "actual": 9500, "target": 9000 }
]

The $type Operator

The $type operator selects documents where the value of a field is an instance of the specified BSON type(s).

Syntax

js
{ field: { $type: <BSON type> } }
// or
{ field: { $type: [ <BSON type1>, <BSON type2>, ... ] } }

BSON Types

MongoDB uses BSON (Binary JSON) for data storage. Some common BSON types include:

  • 1: Double
  • 2: String
  • 3: Object
  • 4: Array
  • 8: Boolean
  • 9: Date
  • 16: Int32
  • 18: Int64

You can specify types by number or by string name (e.g., "string", "double", "bool").

Examples

Finding all documents where the age field is a string (perhaps indicating a data quality issue):

js
db.users.find({ age: { $type: "string" } })

Finding products where the price is either an integer or a double:

js
db.products.find({ price: { $type: ["int", "double"] } })

Output

js
// For the query finding users where age is a string
[
{ "_id": ObjectId("5f8a4c1f8c7ba17a13b73f25"), "name": "John Doe", "age": "thirty" },
{ "_id": ObjectId("5f8a4c2a8c7ba17a13b73f26"), "name": "Jane Smith", "age": "25" }
]

Use Cases for $type

  • Data validation and cleanup
  • Finding potential data quality issues
  • Handling different data representations in unstructured data
  • Processing mixed data types in imported datasets

The $mod Operator

The $mod operator performs the modulo operation on a field's value and selects documents where the result matches a specified value.

Syntax

js
{ field: { $mod: [divisor, remainder] } }

Examples

Finding all users with even user IDs:

js
db.users.find({ user_id: { $mod: [2, 0] } })

Finding products to include in a "every third item free" promotion:

js
db.inventory.find({ product_id: { $mod: [3, 0] } })

Output

js
// For the query finding users with even IDs
[
{ "_id": ObjectId("5f8a5d1f8c7ba17a13b73f30"), "name": "Alice", "user_id": 2 },
{ "_id": ObjectId("5f8a5d2a8c7ba17a13b73f32"), "name": "Bob", "user_id": 4 },
{ "_id": ObjectId("5f8a5d358c7ba17a13b73f34"), "name": "Charlie", "user_id": 6 }
]

The $text Operator

The $text operator performs a text search on fields that have a text index. This is particularly useful for full-text search functionality.

Pre-requisite: Creating a Text Index

Before using $text, you must create a text index on the field(s) you want to search:

js
db.articles.createIndex({ content: "text", title: "text" })

Syntax

js
{ $text: { $search: "search string", $language: "en", $caseSensitive: false, $diacriticSensitive: false } }

Examples

Finding all articles containing either "mongodb" or "database":

js
db.articles.find({ $text: { $search: "mongodb database" } })

Finding articles containing the exact phrase "document database":

js
db.articles.find({ $text: { $search: "\"document database\"" } })

Output

js
// For the query finding articles with "mongodb" or "database"
[
{
"_id": ObjectId("5f8a6a1f8c7ba17a13b73f40"),
"title": "Introduction to MongoDB",
"content": "MongoDB is a document database..."
},
{
"_id": ObjectId("5f8a6a2a8c7ba17a13b73f41"),
"title": "Choosing the Right Database",
"content": "When selecting a database system..."
}
]

Sorting by Text Score

You can sort the results by relevance using the $meta operator:

js
db.articles.find(
{ $text: { $search: "mongodb database" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })

The $where Operator

The $where operator allows you to pass a JavaScript expression or function to the query system. This provides maximum flexibility but comes with performance and security considerations.

Syntax

js
{ $where: "JavaScript expression" }
// or
{ $where: function() { return JavaScript expression; } }

Examples

Finding all products where the sum of ratings is greater than 100:

js
db.products.find({
$where: function() {
return Array.isArray(this.ratings) &&
this.ratings.reduce((sum, rating) => sum + rating, 0) > 100;
}
})

Finding users whose first and last names are the same:

js
db.users.find({
$where: "this.firstName.toLowerCase() === this.lastName.toLowerCase()"
})

Output

js
// For the query finding products with sum of ratings > 100
[
{
"_id": ObjectId("5f8a7b1f8c7ba17a13b73f50"),
"name": "Popular Gadget",
"ratings": [30, 45, 28, 17]
}
]

Important Considerations for $where

  • $where uses JavaScript evaluation, which is significantly slower than other operators
  • It cannot use indexes effectively, leading to full collection scans
  • It poses potential security risks if used with user-provided input
  • Use other operators whenever possible for better performance
  • Reserved for complex queries that cannot be expressed using other operators

The $jsonSchema Operator

The $jsonSchema operator allows you to validate documents against a specified JSON Schema. This is particularly useful for ensuring data quality and structure.

Syntax

js
{
$jsonSchema: {
bsonType: <type>,
required: [<field1>, <field2>, ...],
properties: {
<field1>: {<validation rules>},
<field2>: {<validation rules>},
...
}
}
}

Example

Finding all products that match a specific schema:

js
db.products.find({
$jsonSchema: {
bsonType: "object",
required: ["name", "price", "category"],
properties: {
name: { bsonType: "string", minLength: 3 },
price: { bsonType: "number", minimum: 0 },
category: { enum: ["electronics", "clothing", "food", "books"] }
}
}
})

This query finds all products that:

  • Have required fields: name, price, and category
  • Have a name that is a string with at least 3 characters
  • Have a price that is a number greater than or equal to 0
  • Have a category that is one of the specified values

Output

js
// For the jsonSchema query
[
{
"_id": ObjectId("5f8a8c1f8c7ba17a13b73f60"),
"name": "Smartphone",
"price": 599.99,
"category": "electronics"
},
{
"_id": ObjectId("5f8a8c2a8c7ba17a13b73f61"),
"name": "Winter Jacket",
"price": 89.95,
"category": "clothing"
}
]

Real-world Applications

User Search with Flexible Matching

Imagine building a user search feature that needs to handle partial matches, typing errors, and different name formats:

js
db.users.find({
$or: [
{ firstName: { $regex: new RegExp(searchTerm, "i") } },
{ lastName: { $regex: new RegExp(searchTerm, "i") } },
{ email: { $regex: new RegExp(searchTerm, "i") } }
]
})

Dynamic Price Filtering

For an e-commerce application where you need to find products with significant discounts:

js
db.products.find({
$expr: {
$gt: [
{ $subtract: ["$originalPrice", "$currentPrice"] },
{ $multiply: ["$originalPrice", 0.25] }
]
}
})

This query finds products where the discount amount is more than 25% of the original price.

Content Moderation System

For a content platform that needs to flag potentially problematic content:

js
db.posts.createIndex({ content: "text", title: "text" })

db.posts.find({
$text: {
$search: "offensive inappropriate explicit"
},
postDate: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
})

This query finds recently posted content containing potentially problematic terms.

Data Quality Monitoring

For a system that needs to identify inconsistent data types across records:

js
// Find all users where age is not a number
db.users.find({
age: { $exists: true, $not: { $type: ["int", "double"] } }
})

Summary

MongoDB's evaluation operators provide a powerful toolkit for creating sophisticated queries:

  • $regex enables pattern matching with regular expressions
  • $expr allows for field comparisons and calculations within the same document
  • $type helps identify and filter based on data types
  • $mod is useful for divisibility checks and cyclic patterns
  • $text provides full-text search capabilities (with proper indexing)
  • $where offers JavaScript evaluation for maximum flexibility
  • $jsonSchema validates documents against a JSON Schema

By leveraging these operators, you can implement complex filtering logic directly in your database queries, reducing the need for post-query processing in your application code.

Practice Exercises

  1. Write a query to find all documents where the email field is a valid email address (contains "@" and ends with a domain).
  2. Create a query to find all products that have a margin percentage (calculated as (price - cost) / price) greater than 40%.
  3. Find all transactions where the amount is divisible by 5.
  4. Write a query to find user documents where the phone number is in an incorrect format (not a string or not following a specific pattern).
  5. Use $text search to find articles about MongoDB that don't mention "SQL".

Additional Resources

Remember that while evaluation operators provide powerful capabilities, they should be used judiciously. Operations like $where and complex $regex patterns can have performance implications, especially on large collections. Always consider indexing strategies and query optimization when working with these operators in production environments.



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)