0Pricing
MongoDB Academy · Lesson

Regex Queries and Pattern Matching

Learners will apply regular expressions to string fields for flexible text-pattern searches.

Regex Queries and Pattern Matching is a free MongoDB Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the MongoDB Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

When You Need Pattern Matching

Sometimes you need to search for documents where a string field contains, starts with, or ends with a certain pattern—but you do not know the exact value. Simple equality filters like { name: 'Alice' } only match exact strings. Regular expressions (regex) give you flexible pattern matching against string fields in MongoDB.

MongoDB supports regex through the $regex operator or by passing a JavaScript regex literal directly in a query filter. Both approaches work, with slightly different syntax for setting options like case-insensitivity.

Basic Regex Syntax in MongoDB

There are two ways to use regex in MongoDB queries:

  • JS regex literal: { name: /alice/i } — concise, flags go after the closing slash
  • $regex operator: { name: { $regex: 'alice', $options: 'i' } } — more verbose but required when combining with other operators

Both forms produce identical results. Use the JS literal syntax for simple queries; use the $regex operator form when you need to build the pattern dynamically from a string variable or combine it with other operators on the same field.

// JS regex literal - concise
db.users.find({ name: /alice/i });
// 'i' flag = case-insensitive

// $regex operator - equivalent
db.users.find({ name: { $regex: 'alice', $options: 'i' } });

// Dynamic pattern from variable:
const searchTerm = req.query.name;
const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape special chars
db.users.find({ name: { $regex: escapedTerm, $options: 'i' } });

Regex Options: i, m, s, x

MongoDB supports four regex option flags:

  • i — Case-insensitive: /alice/i matches 'Alice', 'ALICE', 'alice'
  • m — Multiline: ^ and $ match the start/end of each line rather than the whole string
  • s — Dot-all: . matches any character including newlines
  • x — Extended: whitespace in the pattern is ignored (for readable patterns with comments)

The most commonly used flag is i. The m flag is useful for searching multi-line text fields like blog content. Multiple flags can be combined: { $options: 'im' }.

// Case-insensitive search for 'javascript'
db.courses.find({ title: { $regex: 'javascript', $options: 'i' } });
// Matches: 'JavaScript', 'JAVASCRIPT', 'javascript', 'JavaScript Tutorial'

// Multiline match: ^ anchors to start of each line
db.articles.find({ content: { $regex: '^Introduction', $options: 'm' } });
// Matches articles where any line starts with 'Introduction'

// Combined flags
db.posts.find({ body: { $regex: 'mongodb', $options: 'si' } });
// Case-insensitive + dot matches newlines in body

Anchors: Starts With and Ends With

Use regex anchors to match the start or end of a string field:

  • ^ anchors to the start: /^Admin/ matches strings that begin with 'Admin'
  • $ anchors to the end: /\.pdf$/ matches strings ending with '.pdf'

A critical performance fact: a prefix regex with a caret (/^prefix/) CAN use a regular B-tree index on that field—MongoDB can seek directly to the prefix in the index. This makes prefix searches much faster than mid-string searches. Never use a leading wildcard (/.*pattern/ or /pattern/ without ^) on a large collection without a text index.

// Prefix match - CAN use index (very efficient)
db.users.find({ username: /^admin/i });
// Matches: 'admin', 'administrator', 'Admin123'
// Uses the index on username (if it exists)

// Suffix match - CANNOT use regular index (slower)
db.files.find({ filename: /\.pdf$/i });
// Matches: 'report.pdf', 'invoice.PDF'
// Must scan all documents - add text index if used frequently

// Mid-string - CANNOT use index
db.products.find({ description: /wireless/i });
// Must scan all documents - use $text index for this

Character Classes and Quantifiers

Regex gives you expressive pattern primitives:

  • . — any character (except newline, unless s flag set)
  • [abc] — character class: matches a, b, or c
  • [a-z] — range: any lowercase letter
  • \d — any digit (equivalent to [0-9])
  • \w — any word character ([a-zA-Z0-9_])
  • *, +, ? — zero or more, one or more, zero or one
  • {n}, {n,m} — exactly n, between n and m occurrences
// Phone number pattern: +1-XXX-XXX-XXXX or similar
db.users.find({ phone: /^\+?\d{1,3}[-. ]?\(?\d{3}\)?[-. ]?\d{3}[-. ]?\d{4}$/ });

// 6-character alphanumeric codes
db.coupons.find({ code: /^[A-Z0-9]{6}$/ });
// Matches: 'SALE99', 'DISC25', 'FREE01'

// Email basic validation
db.users.find({ email: { $regex: '^[\\w.]+@[\\w.]+\\.[a-z]{2,}$', $options: 'i' } });

Regex vs Text Index: Know the Difference

Regex queries and MongoDB's $text operator serve different purposes and have very different performance characteristics:

  • Regex: matches by character pattern. Works on any string field, no special index needed (though prefix anchors use B-tree indexes). Suitable for format validation, prefix search, or pattern matching on small collections or indexed prefix queries.
  • $text / text index: full-text keyword search with tokenization, stemming, and stop-word filtering. Requires a text index. Much faster for keyword search in large text fields (articles, descriptions, comments).

Never use /.*keyword.*/i on a large unindexed collection—it is a full scan and may be slow.

// Use REGEX for: format matching, prefix searches on indexed field
db.orders.find({ orderNumber: /^ORD-2024/ }); // Prefix - can use index

// Use $text for: keyword search in content fields
// First: create a text index
db.articles.createIndex({ content: 'text', title: 'text' });
// Then: full-text search
db.articles.find({ $text: { $search: 'mongodb performance' } });
// Much faster than /mongodb.*performance/i on large collections

ReDoS: The Security Risk

ReDoS (Regular Expression Denial of Service) is an attack where a malicious user crafts an input string that causes a poorly written regex to take exponential time to evaluate. If you pass user input directly into a regex without escaping, an attacker can hang your database query.

Always escape user input before using it in a regex by replacing all special regex characters (. * + ? ^ $ { } ( ) [ ] | \ ) with their escaped equivalents. Many libraries (like escape-string-regexp in Node.js) do this automatically. Additionally, prefer $text for user-facing search inputs over raw regex.

// DANGEROUS: raw user input in regex (ReDoS risk)
const input = req.query.search; // User controls this
db.products.find({ name: { $regex: input } }); // NEVER DO THIS

// SAFE: escape user input before using in regex
function escapeRegex(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const safeInput = escapeRegex(req.query.search);
db.products.find({ name: { $regex: safeInput, $options: 'i' } });

// BETTER: Use $text for user-facing search
db.products.find({ $text: { $search: req.query.search } });

Regex on Array Fields

When you apply a regex to an array field, MongoDB checks whether any element of the array matches the pattern—the same containment semantics as equality queries on arrays. This lets you search through arrays of tags, categories, or any string array without a special operator.

For example, a document with tags: ['node.js', 'mongodb', 'backend'] would be returned by { tags: /^mongo/ } because 'mongodb' starts with 'mongo'. The regex is applied to each element independently.

// Document: { tags: ['node.js', 'mongodb', 'backend-dev'] }

// Find products where any tag starts with 'mongo'
db.products.find({ tags: /^mongo/i });
// Returns the document (tags contains 'mongodb')

// Find articles where any category contains 'tech'
db.articles.find({ categories: /tech/i });
// Matches: categories containing 'technology', 'tech-news', 'fintech'

// Index on array field improves regex prefix searches
db.articles.createIndex({ categories: 1 });

Practical Search Example

A typical e-commerce search feature combines regex with other filters to provide a contextual, case-insensitive product search. The query matches product names that contain the search term anywhere in the string, filtered by category and in-stock status.

For small catalogs (under 50,000 products), regex search is acceptable. For larger catalogs or full-description search, Atlas Search (Lucene-based) provides superior relevance ranking, stemming, and performance.

// Search endpoint: GET /products?q=wireless&category=Electronics
app.get('/products', async (req, res) => {
  const filter = { stock: { $gt: 0 } };

  if (req.query.q) {
    const escaped = req.query.q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    filter.name = { $regex: escaped, $options: 'i' };
  }
  if (req.query.category) {
    filter.category = req.query.category;
  }

  const products = await db.collection('products')
    .find(filter)
    .project({ name: 1, price: 1, _id: 1 })
    .limit(50)
    .toArray();

  res.json(products);
});

Regex Performance Summary

Key rules for regex performance in MongoDB:

  • Prefix with ^ (/^prefix/): Can use a B-tree index. Best performance for regex.
  • Case-sensitive prefix (/^prefix/): Fully exploits index ordering.
  • Case-insensitive prefix (/^prefix/i): Partially uses index but still much faster than no anchor.
  • Contains without anchor (/pattern/): Full collection scan—use text index instead.
  • Suffix (/pattern$/): Full collection scan or text index.

When in doubt, test with explain('executionStats') and look for IXSCAN vs COLLSCAN.

// Check if regex query uses an index
db.users.find({ username: /^alice/i })
  .explain('executionStats');
// winningPlan.stage should be 'IXSCAN' if username is indexed

// Compare index scan vs collection scan:
// /^alice/ on indexed field: totalDocsExamined ~= nReturned (efficient)
// /alice/  on any field: totalDocsExamined = all documents (slow!)

Testing Regex Patterns Before Deployment

Before running a regex query on a production collection, test the pattern against sample documents to verify it matches what you intend and does not match what you do not intend. The MongoDB shell lets you test quickly with a small limit(), and JavaScript's String.prototype.test() or regex literals let you validate patterns in Node.js before embedding them in queries.

Also check performance with explain(): a regex that requires a full collection scan should be converted to a text query or have its collection size considered. For new collections, design with search requirements in mind—add text indexes from the start rather than retrofitting them.

// Step 1: Test regex in Node.js before using in query
const pattern = /^wireless/i;
const testStrings = ['Wireless Headphones', 'wireless mouse', 'Wired Keyboard'];
testStrings.forEach(s => console.log(s, '=>', pattern.test(s)));
// 'Wireless Headphones' => true
// 'wireless mouse' => true
// 'Wired Keyboard' => false

// Step 2: Test on production with limit
db.products.find({ name: /^wireless/i }).limit(5);

// Step 3: Check performance
db.products.find({ name: /^wireless/i }).explain('executionStats');

Quick Check

Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.

Lesson Recap

In this lesson you learned: regex queries use /pattern/flags or $regex operator for flexible string pattern matching—the most important flag is i for case-insensitivity, prefix-anchored regexes (/^prefix/) can use B-tree indexes for efficient lookups while mid-string patterns require full scans, and always escape user input before inserting it into a regex to prevent ReDoS attacks—or better yet, use the $text operator with a text index for user-facing search. Next up we move into updating documents with $set, $unset, and other update operators.

Frequently asked questions

Is the “Regex Queries and Pattern Matching” lesson free?

Yes — the full text of “Regex Queries and Pattern Matching” is free to read here on the web, and the MongoDB Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the MongoDB Academy course, upgrade to CoddyKit PRO.

What will I learn in “Regex Queries and Pattern Matching”?

Learners will apply regular expressions to string fields for flexible text-pattern searches. You practise MongoDB Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start MongoDB Academy?

No prior experience is required. MongoDB Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Regex Queries and Pattern Matching” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this MongoDB Academy lesson?

Yes. Every MongoDB Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Comparison Operators: $eq, $gt, $lt, $in
  2. Logical Operators: $and, $or, $nor, $not
  3. Element Operators and Type Checks
  4. Regex Queries and Pattern Matching
← Back to MongoDB Academy