Intermediate

Text Search and Pattern Matching in MongoDB

The performance impact of a regex query depends primarily on whether or not it can use an index.

Table of Contents

  1. General Introduction
  2. Module 1 — Understanding MongoDB’s capabilities for text searching
  1. Perform a basic full text search with $text
  1. Pattern Matching with Regular Expressions
  1. Module 4 — Applying text search techniques to common analysis tasks
  1. Quick Reference — Operator Comparison
  2. Best practices and summary

1. General Introduction

This training covers the text search functionalities of MongoDB: full-text search via the $text operator and pattern matching via regular expressions ($regex). It is aimed at developers and analysts who work with semi-structured or unstructured data, such as customer reviews, product descriptions, social media posts, and user-generated content.

Recommended prerequisites: basic knowledge of MongoDB (Compass, Shell, connection to a cluster)


2. Understand MongoDB’s capabilities for text searching


1.1 What is Full-text Search in MongoDB?

The problem of searching in unstructured data

In modern applications — from e-commerce to social media — users expect search that “just works.” But behind that experience, you have to be able to search through messy semi-structured data: reviews, descriptions, comments, user-generated content.

The fundamental challenge is: how do you search for something when you don’t know the exact words used, or when the data lives in fields like description, reviews or comments?

The limits of classic queries

If you’ve worked with MongoDB, you’ve probably used basic queries like:

// Requête d'égalité classique
db.products.find({ name: "charger" })
db.products.find({ status: "active" })

The problem: These queries only work if the value matches exactly what is stored in the database. If a user types wireless charger, but the product is registered as fast phone charger, a typical query will not return it. You lose flexibility, relevance, and you risk missing what the user really wanted to say.

How Full-text Search works

Full-text search solves this problem by analyzing the text inside your documents to find relevant matches, not just exact matches. The process takes place in several stages:

  1. Tokenization: the text is divided into units called tokens (individual words)
  2. Removal of stop words: common words like the, is, a are ignored because they do not provide semantic value
  3. Matching: tokens are compared to the user’s request
  4. Relevance score: MongoDB assigns a relevance score to each result, allowing them to be ranked from most to least relevant

This is what gives a natural feel to the search, similar to Google or Amazon. You’re not just matching one string to another — you’re looking for meaningful matches in free text.

To use full-text search in MongoDB

Two steps are necessary:

  1. Create a text index on the field(s) to index
  2. Use the $text operator in your queries
// Étape 1 : Créer un text index
db.products.createIndex({ description: "text" })

// Étape 2 : Effectuer une recherche
db.products.find({ $text: { $search: "wireless charger" } })

1.2 Compare $text, $regex and equality filters

MongoDB provides three main approaches for searching text fields. Each has its strengths and appropriate use cases.

Equality filters

db.products.find({ status: "active" })
db.orders.find({ category: "electronics" })
  • Mechanism: exact value comparison
  • Performance: fast and efficient, especially with a classic index
  • Use case: search by category, status, identifier — when you know the exact value
  • Limit: no flexibility, no tolerance for variations

The $regex operator

db.products.find({ name: { $regex: /^wireless/i } })
db.users.find({ email: { $regex: /\w+@\w+\.\w+/ } })
  • Mechanism: character-based pattern matching
  • Flexibility: very high — prefixes, suffixes, specific formats (email, telephone)
  • Required index: no (works on any field)
  • Performance: can be slow, especially if the pattern is not anchored at the beginning
  • Use case: format validation, hashtag detection, substring matching, structured patterns

The $text operator

db.products.find({ $text: { $search: "wireless charger" } })
  • Mechanism: token-based search with relevance scoring
  • Flexibility: intermediate — ignores stop words, does stemming
  • Index required: yes, a text index must exist on the field
  • Performance: fast thanks to indexing
  • Use case: searching for keywords in descriptions, reviews, free content — when users type in a search bar

How to choose?

LocationRecommended operator
Search by status: "active" or category: "electronics"Equality filter
Validate an email format or detect a hashtag$regex
Search for starts with "Sam" or ends with ".com"$regex
User types in a search bar and expects relevant results$text
Find products like “wireless charger” or “noise-canceling headphones”$text
Detect forbidden words with patterns$regex

The golden rule: choose according to your search intention. If you know the exact value, use equality. If you need to match a pattern, use regex. If your users type free text and expect smart, ordered results, use $text.


1.3 How text indexes work in MongoDB

Transforming text into tokens

Behind each $text query is a text index which transforms character strings into intelligent, searchable tokens. Understanding this transformation is essential to designing good schematics.

The tokenization process:

  1. MongoDB takes the text value of a field (e.g.: "Fast Wireless Charger for iPhone")
  2. It breaks it up into individual words (tokens): ["Fast", "Wireless", "Charger", "iPhone"]
  3. The stop words are removed: ["Fast", "Wireless", "Charger", "iPhone"] (in this case, no stop words)
  4. stemming is applied: words are reduced to their root — charger and chargers both become charg
  5. The resulting tokens are stored in the index

Thanks to stemming, a search for charger will also find documents containing chargers, charging, etc.

Create a text index

// Index sur un seul champ
db.products.createIndex({ description: "text" })

// Index sur plusieurs champs
db.products.createIndex({ name: "text", description: "text" })

// Index sur tous les champs de type string (wildcard text index)
db.products.createIndex({ "$**": "text" })

// Index avec des poids (weights) pour prioriser certains champs
db.products.createIndex(
  { name: "text", description: "text" },
  { weights: { name: 10, description: 5 } }
)

The weights allow you to give more importance to certain fields in the calculation of the relevance score. For example, a match in name (weight 10) will have more impact on the score than a match in description (weight 5).

Important constraint: only one text index per collection

MongoDB only allows one text index per collection. This means you need to carefully plan which fields to include in your text index. If you need full-text search on several fields, all of them must be included in the same index.

// Correct : un index qui couvre plusieurs champs
db.reviews.createIndex({ title: "text", body: "text", tags: "text" })

// Incorrect : impossible d'avoir deux text indexes sur la même collection
// db.reviews.createIndex({ title: "text" })      // Premier index
// db.reviews.createIndex({ body: "text" })       // Erreur !

Check existing indexes

// Afficher tous les index d'une collection
db.products.getIndexes()

It is important to know the limitations of MongoDB’s built-in full-text search to avoid unpleasant surprises when designing your system.

What $text cannot do

1. No fuzzy search (typo tolerance) If a user types load instead of load, MongoDB will not find it. There is no automatic correction, no suggestion, no Levenshtein distance.

2. No wildcard support You cannot do partial matches like contains, starts with or ends with inside a $text query.

3. Works only on indexed fields If a field is not part of a text index, it is invisible to $text. You cannot use $text on a non-indexed field.

4. Only one text index per collection As seen previously, you can only have one text index, so your search fields must be chosen in advance.

5. It is not a complete search engine MongoDB’s built-in text search does not support:

  • Correction of typos (typo correction)
  • Autocompletion (autocomplete)
  • Synonym matching (synonym matching)
  • Advanced ranking beyond simple term frequency
  • Advanced encoding models (semantic embeddings)
  • Suggesting related terms

6. Scalability issues For large collections with hundreds of fields or millions of documents, native full-text search does not scale well without custom tuning.

For more advanced needs, MongoDB offers Atlas Search, which is based on Apache Lucene. Atlas Search offers:

  • Fuzzy search (typo tolerance)
  • Autocompletion
  • Semantic search
  • Advanced linguistic analyzes
  • More sophisticated scoring

MongoDB’s native full-text search is perfect for simple to intermediate use cases. For enterprise search needs, Atlas Search is the recommended solution.

$regex as complement

When $text is not enough — especially for matching substrings, prefixes or specific formats — $regex takes over. Regex can work on any field, without indexes, and offers full control at the character level. On the other hand, it can be slow on large collections.


3. Perform a basic full text search with $text


Setting up the environment

Before performing full-text searches, you must ensure that you are in the correct database and have created a text index on your collection.

// Se connecter à la base de données
use reviewsDB

// Insérer quelques produits de test
db.products.insertMany([
  { name: "Wireless Charger", description: "Fast wireless charger for smartphones" },
  { name: "USB Charger", description: "Standard USB charger, not wireless" },
  { name: "Phone Case", description: "Protective case for iPhone and Samsung" },
  { name: "Bluetooth Mouse", description: "Wireless Bluetooth mouse for laptops" }
])

// Créer un text index sur le champ 'description'
db.products.createIndex({ description: "text" })
// Recherche simple : trouver des documents où 'description' contient 'wireless'
db.products.find({ $text: { $search: "wireless" } })

// Recherche avec plusieurs mots-clés (comportement OU par défaut)
// Retourne les documents qui contiennent 'wireless' OU 'charger'
db.products.find({ $text: { $search: "wireless charger" } })

Important: By default, when you pass multiple words into $search, MongoDB performs a logical OR between these terms — each returned document must contain at least one of the words. It is not a logical AND.

In MongoDB Compass:

  1. Go to the Documents tab of your collection
  2. In the filter bar, enter: { $text: { $search: "wireless charger" } }
  3. Click Find to run the query
  4. Results show all documents that match

To display the relevance score, switch to the Projection tab:

  • Add score field with value { $meta: "textScore" }

To sort by relevance, switch to the Sort tab:

  • Add score: { $meta: "textScore" } to sort from most to least relevant

2.2 Phrase searches and term exclusion

Sometimes you want to find an exact phrase in a specific order — for example wireless mouse and not any document that contains the word wireless or the word mouse separately. To do this, you must surround the sentence with escaped quotation marks:

// Phrase search : trouver exactement la phrase "wireless mouse"
db.products.find({ $text: { $search: "\"wireless mouse\"" } })

// Dans le shell MongoDB, les guillemets internes doivent être échappés
db.products.find({ $text: { $search: '"wireless mouse"' } })

Why it’s useful: Phrase searches are especially valuable in scenarios where context and word order matter, such as:

  • Product searches on an e-commerce (the user wants exactly “wireless mouse” and not just any wireless item)
  • Document analysis where a specific expression must be identified
  • Review filtering where the context of the sentence is significant

Term Exclusion

You can exclude specific terms from your results by adding a - (minus) sign in front of the word to exclude:

// Trouver des "charger" mais pas ceux qui mentionnent "apple"
db.products.find({ $text: { $search: "charger -apple" } })

// Recherche multi-mots avec exclusion
db.products.find({ $text: { $search: "wireless mouse -bluetooth -gaming" } })

// Combiner phrase exacte et exclusion de terme
db.products.find({ $text: { $search: '"wireless charger" -apple' } })

Complete practical example

// Données de test
db.products.insertMany([
  { name: "Wireless Mouse", description: "Ergonomic wireless mouse for PC" },
  { name: "Bluetooth Mouse", description: "Wireless Bluetooth mouse for Mac" },
  { name: "Apple Magic Mouse", description: "Wireless mouse designed by Apple" },
  { name: "USB Wired Mouse", description: "Standard wired USB mouse" },
  { name: "Apple Charger", description: "Wireless charger by Apple for iPhone" },
  { name: "Samsung Charger", description: "Fast wireless charger for Samsung" }
])

db.products.createIndex({ name: "text", description: "text" })

// Chercher "wireless mouse" exact
db.products.find({ $text: { $search: '"wireless mouse"' } })
// Résultat : Wireless Mouse, Bluetooth Mouse, Apple Magic Mouse (tous contiennent "wireless" + "mouse")

// Chercher "wireless mouse" exact en excluant Apple
db.products.find({ $text: { $search: '"wireless mouse" -apple' } })
// Résultat : Wireless Mouse, Bluetooth Mouse (Apple Magic Mouse est exclu)

These two tools — phrase searches and term exclusions — give users more control over their searches and help you find the right balance between precision and recall in your search engine.


2.3 Use relevance scores to sort and rank results

Understanding the relevance score

MongoDB automatically assigns a relevance score (relevance score or textScore) to each document returned by a $text query. This score is based on:

  • The frequency of appearance of terms in the document
  • The number of fields in which the terms appear
  • The weights defined in the text index (if specified)

The higher the score, the more relevant the document is to the query.

Project score into results

To display the score in the results, use the projection with $meta: "textScore":

// Projeter le score de pertinence
db.products.find(
  { $text: { $search: "wireless charger" } },
  { score: { $meta: "textScore" } }
)

Example result:

{ "_id": ObjectId("..."), "name": "Fast Wireless Charger", "score": 2.1 }
{ "_id": ObjectId("..."), "name": "USB Charger (not wireless)", "score": 0.75 }

Sort by relevance score

// Trier du plus pertinent au moins pertinent
db.products.find(
  { $text: { $search: "wireless charger" } },
  { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })

This query transforms a simple keyword search into a ranked, intent-driven search experience — exactly how users expect a search engine to work.

Concrete example with test data

// Insérer des produits avec différentes densités de mots-clés
db.products.insertMany([
  {
    name: "Super Fast Wireless Charger",
    description: "Ultra-fast wireless charger, charges wirelessly at 20W"
  },
  {
    name: "Standard USB Charger",
    description: "Standard USB charger, not wireless, charges at 5W"
  },
  {
    name: "iPhone Charger",
    description: "iPhone charger, works with USB-C, no wireless charging"
  }
])

db.products.createIndex({ name: "text", description: "text" })

// Requête avec score et tri
db.products.find(
  { $text: { $search: "wireless charger" } },
  { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })

// Résultat attendu :
// 1. "Super Fast Wireless Charger" — score le plus élevé (contient "wireless" plusieurs fois)
// 2. "Standard USB Charger" — score moyen (contient "charger")
// 3. "iPhone Charger" — score le plus bas (contient "charger" mais pas "wireless")

In MongoDB Compass

To perform the same operation in Compass:

  1. Filter: { $text: { $search: "wireless charger" } }
  2. Projection: { score: { $meta: "textScore" } }
  3. Sort: { score: { $meta: "textScore" } }

This allows you to inspect and validate the quality of results without writing additional code — ideal for data validation or debugging research workflows.


2.4 Combining text search with query filters

Why combine text search and structured filters?

In real applications, you never filter on a single criterion. A product search may require:

  • A user keyword (“load”`)
  • A product category (electronics)
  • A status (active)
  • A price range

Text search and structured filters can coexist in the same MongoDB query.

Basic syntax

// Combinaison : text search + filtre de catégorie
db.products.find({
  $text: { $search: "charger" },
  category: "electronics"
})

// Combinaison : text search + filtre de statut
db.products.find({
  $text: { $search: "wireless" },
  status: "active"
})

// Combinaison : text search + filtre numérique
db.products.find({
  $text: { $search: "charger" },
  price: { $lt: 50 }
})

Language Filters

MongoDB’s text search supports multilingual search. The $text operator accepts a $language parameter which influences stemming and removal of stop words:

// Recherche en anglais uniquement
db.reviews.find({
  $text: { $search: "battery life", $language: "english" }
})

// Recherche en français
db.articles.find({
  $text: { $search: "performance excellent", $language: "french" }
})

// Recherche en espagnol
db.reviews.find({
  $text: { $search: "entrega rápida", $language: "spanish" }
})

Languages ​​supported by MongoDB: Danish, Dutch, English, Finnish, French, German, Hungarian, Italian, Norwegian, Portuguese, Romanian, Russian, Spanish, Swedish, Turkish.

Complete practical example

// Collection de produits avec catégories et statuts
db.products.insertMany([
  { name: "Wireless Charger Pro", category: "electronics", status: "active", price: 29.99 },
  { name: "Wireless Gaming Mouse", category: "accessories", status: "active", price: 49.99 },
  { name: "Wireless Keyboard", category: "electronics", status: "inactive", price: 79.99 },
  { name: "Apple Wireless Charger", category: "electronics", status: "active", price: 39.99 }
])

db.products.createIndex({ name: "text" })

// Chercher "wireless" uniquement dans la catégorie 'electronics' et statut 'active'
db.products.find({
  $text: { $search: "wireless" },
  category: "electronics",
  status: "active"
})
// Résultat : "Wireless Charger Pro" et "Apple Wireless Charger" seulement

Combine with Relevance Sort

// Recherche avancée : texte + filtres + tri par pertinence
db.products.find(
  {
    $text: { $search: "wireless charger" },
    category: "electronics",
    status: "active"
  },
  { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })

This combination is what makes modern applications powerful: natural search by keyword, refined by structured filters, classified by relevance.

Use case:

  • E-commerce product catalogs: keyword + category + price range
  • Content platforms: keyword + language + publication status
  • Review systems: keyword + rating + publication date
  • Search dashboards: combination of free text and multiple filters

4. Pattern Matching with Regular Expressions


3.1 Understanding and applying basic regex patterns

When to use $regex rather than $text

Full-text search ($text) is excellent for keyword searches ranked by relevance on large blocks of text. But sometimes you don’t need that. You are looking for a specific pattern inside a field, for example:

  • Names that start with "wireless"
  • Entries ending in "mouse"
  • Any string field that is not part of a text index

In these cases, MongoDB offers $regex — a matching operator based on regular expressions. Regex allows you to search with flexible rules rather than exact values. They work on any field, do not require an index** (but can benefit from an index in certain cases), and can match prefixes, suffixes, and even structured formats.

Basic syntax of $regex

// Syntaxe 1 : avec l'opérateur explicite $regex
db.collection.find({ field: { $regex: /pattern/ } })

// Syntaxe 2 : raccourci avec le pattern directement
db.collection.find({ field: /pattern/ })

// Syntaxe 3 : avec options (flags)
db.collection.find({ field: { $regex: /pattern/flags } })
db.collection.find({ field: { $regex: "pattern", $options: "flags" } })

Prepare test data

use reviewsDB

db.devices.insertMany([
  { name: "Wireless Mouse" },
  { name: "Bluetooth Mouse" },
  { name: "Smart Mouse" },
  { name: "Gaming wireless device" },
  { name: "wireless keyboard" },
  { name: "Touchpad Pro" }
])

Anchor at start with ^ (Caret)

The symbol ^ anchors the pattern at the beginning of the string. It allows you to find all documents whose field starts with a certain value:

// Trouver tous les produits dont le nom COMMENCE par "wireless"
db.devices.find({ name: { $regex: /^wireless/i } })
// Résultat : "Wireless Mouse", "wireless keyboard" (avec le flag 'i' pour ignorer la casse)

Anchor at the end with $ (Dollar)

The symbol $ anchors the pattern at the end of the string. It allows you to find all documents whose field ends with a certain value:

// Trouver tous les produits dont le nom SE TERMINE par "mouse"
db.devices.find({ name: { $regex: /mouse$/i } })
// Résultat : "Wireless Mouse", "Bluetooth Mouse", "Smart Mouse"

Match in the middle (Contains)

Without anchors, the pattern can match anywhere in the chain:

// Trouver tous les documents dont le champ CONTIENT "mouse"
db.devices.find({ name: { $regex: /mouse/i } })
// Résultat : tous les documents qui ont "mouse" n'importe où dans le nom

// Trouver des documents qui contiennent "less" quelque part
db.devices.find({ name: { $regex: /less/ } })
// Correspond à : "wireless", "powerless", "wireless charger", etc.

Example with different patterns

// Pattern plus complexe : commence par "w" suivi de n'importe quoi
db.devices.find({ name: { $regex: /^w/i } })

// Trouve des produits avec un format de code produit (ex: ABC-123)
db.products.find({ sku: { $regex: /^[A-Z]{3}-\d{3}$/ } })

// Trouve des URLs dans un champ
db.posts.find({ content: { $regex: /https?:\/\// } })

3.2 Using regex flags and advanced pattern techniques

The -i flag: Case-Insensitive

The i flag makes the search case insensitive — upper and lower cases are treated the same. This is extremely useful for user-generated content where case is unpredictable.

// Sans le flag : "mongodb" ne correspond pas à "MongoDB"
db.comments.find({ content: { $regex: /mongodb/ } })

// Avec le flag 'i' : correspond à mongodb, MongoDB, MONGODB, etc.
db.comments.find({ content: { $regex: /mongodb/i } })
db.comments.find({ content: { $regex: "mongodb", $options: "i" } })

Test data for advanced patterns

db.socialPosts.insertMany([
  { comment: "I love MongoDB! #NoSQL #database", currency: "€20" },
  { comment: "Check this out: test@example.com", currency: "$15.99" },
  { comment: "Great deal at ₹999 only! #sale", currency: "₹999" },
  { comment: "Contact: user.name@company.org", currency: null },
  { comment: "MONGODB is the best database!", currency: "$50" }
])

Detect hashtags

// Pattern pour les hashtags : # suivi de lettres, chiffres, ou underscores
db.socialPosts.find({ comment: { $regex: /#\w+/ } })
// Résultat : tous les posts avec au moins un hashtag
// \w = [a-zA-Z0-9_]
// + = un ou plusieurs caractères

Detect currency symbols

// Correspondre au symbole dollar $, euro € ou roupie indienne ₹
db.socialPosts.find({ currency: { $regex: /[\$€₹]/ } })
// Les crochets [] définissent une classe de caractères (OU logique)
// Le $ à l'intérieur des [] n'est pas une ancre, c'est le caractère littéral $
// On échappe le $ avec \ pour éviter toute ambiguïté

Detect email addresses

// Pattern pour les emails standard : mot@domaine.extension
db.socialPosts.find({ comment: { $regex: /\w+@\w+\.\w+/ } })
// \w+ = une ou plusieurs lettres/chiffres/underscore
// @ = le caractère @
// \. = un point littéral (le . sans \ correspond à n'importe quel caractère)
// Correspondance : test@example.com, user.name@company.org

The -m flag: Multiline

The m flag treats each line of a multi-line string as a separate string for the ^ and $ anchors:

// Avec le flag 'm', ^ correspond au début de chaque ligne
db.docs.find({ content: { $regex: /^chapter/mi } })

The -s flag: Dotall

The s flag matches the . (period) to any character, including newlines:

// Sans 's', le point ne correspond pas aux \n
db.docs.find({ content: { $regex: /start.*end/s } })

Summary of flags

FlagNameEffect
iCase-insensitiveIgnore upper/lower case difference
mMultiline^ and $ apply to each line
xExtendedIgnore spaces and comments in the pattern
sDotall. also matches \n

3.3 Combine regex with filters and query conditions

Why combine regex and structured filters?

In real applications you never filter on a single field. You’ll often want to combine regex patterns with structured filters to create precise, layered queries. For example:

  • All products whose name starts with "Sam" AND whose category is "electronics"
  • All users whose email contains "@gmail.com" AND whose status is "active"

Regex + Structured Filters

// Nom commence par "Sam" ET catégorie = "electronics"
db.products.find({
  name: { $regex: /^Sam/i },
  category: "electronics"
})

// Email contient "@gmail.com" ET statut = "active"
db.users.find({
  email: { $regex: /@gmail\.com$/i },
  status: "active"
})

// Sku commence par "WL-" ET prix inférieur à 100
db.products.find({
  sku: { $regex: /^WL-/ },
  price: { $lt: 100 }
})

Use $or with regex

// Documents qui correspondent à un pattern dans le nom OU dans la description
db.products.find({
  $or: [
    { name: { $regex: /wireless/i } },
    { description: { $regex: /wireless/i } }
  ]
})

Performance note: Using $or with multiple regex conditions is not well optimized by MongoDB. Avoid large, unindexed collections if possible.

Regex with $and for multiple conditions

// Nom commence par "S" ET ne se termine pas par "Pro"
db.products.find({
  $and: [
    { name: { $regex: /^S/i } },
    { name: { $not: /Pro$/i } }
  ]
})

Regex on Nested Fields

// Recherche dans un champ imbriqué
db.users.find({
  "address.city": { $regex: /^Montreal/i }
})

// Recherche dans un champ d'un tableau d'objets
db.orders.find({
  "items.name": { $regex: /charger/i }
})

Complete practical example: filter combination

db.products.insertMany([
  { name: "Samsung Galaxy S23", category: "phones", brand: "Samsung", status: "active" },
  { name: "Samsung TV 55", category: "televisions", brand: "Samsung", status: "active" },
  { name: "Sony WH-1000XM5", category: "audio", brand: "Sony", status: "active" },
  { name: "Samsung Earbuds Pro", category: "audio", brand: "Samsung", status: "inactive" },
  { name: "Samsara Tracker", category: "accessories", brand: "Samsara", status: "active" }
])

// Trouver les produits Samsung actifs dans la catégorie audio
db.products.find({
  name: { $regex: /^Sam/i },
  brand: "Samsung",
  category: "audio",
  status: "active"
})
// Résultat : aucun (Samsung Earbuds Pro est inactive)

// Version sans le filtre de statut
db.products.find({
  brand: "Samsung",
  category: "audio"
})
// Résultat : "Samsung Earbuds Pro"

3.4 Limitations and performance considerations of regex in MongoDB

How MongoDB executes regex queries

The performance impact of a regex query depends primarily on whether or not it can use an index.

Patterns anchored at start (^) — can use an index:

// MongoDB peut utiliser un index sur le champ 'name' pour cette requête
db.products.find({ name: { $regex: /^Samsung/i } })

When the pattern is anchored at the start with ^ and does not use an i flag, MongoDB can perform a range search on the index — which is significantly faster than a full scan.

Unanchored patterns — full collection scan:

// MongoDB doit scanner TOUS les documents de la collection
db.products.find({ name: { $regex: /samsung/i } })
db.products.find({ description: { $regex: /charger/ } })

For these patterns, MongoDB performs a full collection scan (COLLSCAN), which can be very slow on large collections.

Do’s (good practices)

// DO: Ancrer vos patterns quand c'est possible
db.products.find({ name: { $regex: /^Samsung/ } })

// DO: Combiner regex avec d'autres filtres pour réduire le jeu de données
db.products.find({
  category: "electronics",      // filtre d'abord sur un champ indexé
  name: { $regex: /^Sam/ }       // puis applique le regex sur un sous-ensemble
})

// DO: Utiliser regex là où il excelle
db.posts.find({ content: { $regex: /#\w+/ } })           // hashtags
db.users.find({ email: { $regex: /\w+@\w+\.\w+/ } })     // format email
db.transactions.find({ amount: { $regex: /[\$€₹]/ } })   // symboles de devise

Don’ts (bad practices to avoid)

// DON'T: Regex non ancré sur de grands champs non indexés comme 'description' ou 'logs'
// Cela provoquera un scan complet de la collection
db.products.find({ description: { $regex: /charger/ } })  // lent sur grandes collections

// DON'T: Plusieurs conditions regex dans un $or (MongoDB n'optimise pas bien)
db.products.find({
  $or: [
    { field1: { $regex: /pattern1/i } },
    { field2: { $regex: /pattern2/i } },
    { field3: { $regex: /pattern3/i } }
  ]
})
// Préférez une approche différente ou une logique applicative

When to use $text vs $regex

Criterion$text$regex
Search TypeBased on tokens (whole words)Based on characters (patterns)
Relevance ScoringYes (textScore)No
PerformanceQuick (index)Variable (depends on anchor)
Required IndexYes (text index)No (but beneficial if anchored)
Stop wordsAutomatically ignoredNot taken into account
StemmingYesNo
Fuzzy matchingNoNo (except complex patterns)
Use casesSearching for keywords in descriptions, reviews, blogsSubstrings, format validation, hashtags, emails, currencies

The decision rule:

  • Use $text when you want tokenized search with performance and scoring — for fields like product names, blog content, reviews, descriptions
  • Use $regex when you need substring pattern matching, case-insensitive queries, or format validations — for tags, prices, emails, codes

These two tools are complementary. The key is to choose based on the use case, and you will get the best flexibility and speed.


5. Apply text search techniques to common analysis tasks


The scenario: management of a product review system

Imagine that you manage a product review system. Users write reviews about your electronics, appliances, or mobile accessories. Your goal is to quickly identify complaints, positive feedback, and recurring themes in free texts.

Setting up the review collection

use productDB

db.reviews.insertMany([
  {
    productId: "P001",
    product: "Wireless Charger",
    reviewText: "The battery charging is too slow. Not happy with the speed.",
    rating: 2,
    category: "electronics"
  },
  {
    productId: "P002",
    product: "Bluetooth Speaker",
    reviewText: "Amazing battery life! Sound quality is excellent.",
    rating: 5,
    category: "audio"
  },
  {
    productId: "P003",
    product: "USB-C Hub",
    reviewText: "Good value for money. Battery backup is decent.",
    rating: 4,
    category: "accessories"
  },
  {
    productId: "P004",
    product: "Laptop Stand",
    reviewText: "Build quality is great. My laptop doesn't overheat anymore.",
    rating: 5,
    category: "accessories"
  },
  {
    productId: "P005",
    product: "Phone Case",
    reviewText: "Poor quality. The charger port cover broke after a week.",
    rating: 1,
    category: "accessories"
  }
])

// Créer un text index sur le champ reviewText
db.reviews.createIndex({ reviewText: "text" })

Search by keywords in reviews

// Trouver tous les avis qui mentionnent "battery"
db.reviews.find({ $text: { $search: "battery" } })

// Trouver les avis sur les "charger" ou "charging"
// (grâce au stemming, ces deux termes sont liés)
db.reviews.find({ $text: { $search: "charger" } })

// Identifier les plaintes : avis qui mentionnent "slow", "poor", "broke"
db.reviews.find({ $text: { $search: "slow poor broke" } })

Sort reviews by relevance

// Recherche "battery" avec score pour identifier les avis les plus pertinents
db.reviews.find(
  { $text: { $search: "battery" } },
  { score: { $meta: "textScore" }, reviewText: 1, product: 1 }
).sort({ score: { $meta: "textScore" } })

Combine with category and rating filters

// Avis négatifs (note ≤ 2) sur la catégorie "electronics"
db.reviews.find({
  $text: { $search: "battery charger" },
  category: "electronics",
  rating: { $lte: 2 }
})

// Avis positifs (note ≥ 4) qui mentionnent "quality"
db.reviews.find({
  $text: { $search: "quality" },
  rating: { $gte: 4 }
},
{ score: { $meta: "textScore" }, reviewText: 1 }
).sort({ score: { $meta: "textScore" } })

Real use cases:

  • E-commerce: identify products that receive recurring complaints about the same problem
  • Customer Support: prioritize tickets that mention critical issues
  • Market analysis: detect trends in user feedback (battery life, build quality, delivery)
  • Analytical dashboards: aggregate reviews by theme and note trends

4.2 Identify and exclude reported terms or phrases

The problem: inappropriate content in user data

In any system that accepts user-generated content, some posts may contain problematic terms: spam, explicit content, reported fraud, policy violations. This content isn’t just negative — it may violate trust, safety, or content guidelines.

Identify terms reported with $text

// Créer une collection d'avis avec du contenu mélangé
db.reviews.insertMany([
  { reviewText: "Great product! Works perfectly. Very happy.", status: "active" },
  { reviewText: "This is a complete scam. Do not buy!", status: "active" },
  { reviewText: "Amazing quality, fast delivery.", status: "active" },
  { reviewText: "Spam offer! Buy 10 get 100 free, click here.", status: "active" },
  { reviewText: "Contains explicit content warning", status: "active" }
])

db.reviews.createIndex({ reviewText: "text" })

// Trouver les avis qui mentionnent "scam", "spam" ou "explicit"
db.reviews.find({ $text: { $search: "scam spam explicit" } })

This query can be used to:

  • Feed a moderation queue
  • Generate Flagged Content Reports
  • Trigger a nightly cleaning job

Exclude terms reported with -term syntax

To filter documents that do not contain certain terms, use the - sign:

// Retourner UNIQUEMENT les avis qui ne mentionnent PAS "scam", "spam" ou "explicit"
db.reviews.find({
  $text: { $search: "-scam -spam -explicit" }
})

// Combiner recherche positive et exclusion de termes négatifs
db.reviews.find({
  $text: { $search: "charger -scam -spam" },
  status: "active"
})

Approach with $regex: negative lookahead

If you don’t have a text index or if you want more control, regex with negative lookahead allows you to exclude patterns:

// Negative lookahead : retourner les documents qui NE CONTIENNENT PAS les termes signalés
db.reviews.find({
  reviewText: { $regex: /^(?!.*(scam|spam|explicit))/is }
})
// (?!...) = negative lookahead
// .* = n'importe quelle séquence de caractères
// (scam|spam|explicit) = l'un de ces termes
// 'i' = case-insensitive
// 's' = dotall (le . correspond aussi aux \n)

Note: The negative lookahead is syntactically more complex, but useful when you don’t have a text index or for more nuanced patterns.

Combine content filters and status filters

// Avis actifs qui ne contiennent pas de termes problématiques
db.reviews.find({
  $text: { $search: "-scam -spam -explicit" },
  status: "active"
})

// Avec regex : contenu actif, pas de termes signalés
db.reviews.find({
  reviewText: { $not: { $regex: /scam|spam|explicit/i } },
  status: "active"
})

Use cases in real workflows:

  • Moderation queue: identify reviews to manually review
  • Reporting system: count violations by category
  • Nightly cleanup job: automatically archive or delete flagged content
  • Product Listings: Show only end-user specific reviews
  • Review applications: ensure content quality before publication

4.3 Find documents with missing tags or metadata

The problem: Incomplete data in collections

In any real data system, some documents may be incomplete — they lack tags, descriptions, important metadata, or required fields. Identifying these gaps is essential to maintaining data quality.

Detect missing fields

// Trouver les documents où le champ 'tags' n'existe pas du tout
db.products.find({ tags: { $exists: false } })

// Trouver les documents où 'tags' existe mais est null
db.products.find({ tags: null })

// Combinaison : champ absent OU null
db.products.find({
  $or: [
    { tags: { $exists: false } },
    { tags: null }
  ]
})

Detect empty arrays or empty strings

// Trouver les produits avec un tableau 'tags' vide
db.products.find({ tags: { $size: 0 } })

// Trouver les produits avec une description vide ou blanche
db.products.find({
  description: { $regex: /^\s*$/ }
})
// ^\s*$ correspond à une chaîne qui ne contient que des espaces (ou rien)

// Trouver les produits avec description vide ou nulle
db.products.find({
  $or: [
    { description: { $exists: false } },
    { description: null },
    { description: "" },
    { description: { $regex: /^\s*$/ } }
  ]
})

Combine missing field detection with other filters

// Produits actifs sans tags
db.products.find({
  status: "active",
  tags: { $exists: false }
})

// Avis publiés mais sans catégorie
db.reviews.find({
  published: true,
  category: { $exists: false }
})

// Posts avec campaignTag manquant
db.socialPosts.find({
  campaignTag: { $exists: false }
})

Use $regex to detect substitution values

Sometimes missing fields are filled with substitution values ​​like "N/A", "unknown", "TBD", etc. Regex allows you to detect them:

// Détecter les valeurs de remplacement courantes
db.products.find({
  description: { $regex: /^(n\/a|unknown|tbd|todo|placeholder)$/i }
})

// Détecter les descriptions trop courtes (moins de 10 caractères utiles)
db.products.find({
  description: { $regex: /^.{0,9}$/ }
})

Complete Data Quality Audit Example

db.products.insertMany([
  { name: "Wireless Charger", category: "electronics", tags: ["charging", "wireless"], description: "Fast charger" },
  { name: "Phone Case", category: "accessories", tags: [], description: "" },
  { name: "Laptop Stand", category: "accessories", description: "Ergonomic stand" }, // tags absent
  { name: "USB Hub", category: "electronics", tags: null, description: "  " }         // tags null, description espaces
])

// Rapport d'audit : produits incomplets
db.products.find({
  $or: [
    { tags: { $exists: false } },
    { tags: null },
    { tags: { $size: 0 } },
    { description: { $regex: /^\s*$/ } },
    { description: { $exists: false } }
  ]
})
// Résultat : Phone Case, Laptop Stand, USB Hub

Real use cases:

  • Data quality audit: identify incomplete records before a migration
  • Content completeness check: ensure all products have descriptions and tags
  • Monitoring ingestion pipelines: detect documents arriving without required metadata
  • Admin Dashboards: Show Data Incompleteness Statistics

4.4 Combine language, category and search term filters

The challenge: multilingual and multi-category data

Actual data is not flat. A product review can be in English or Spanish, belong to different categories, and be written by users in different markets. How to filter all of this effectively while still maintaining intelligent search?

This is where the combination of $text with structured filters on language, category and status comes into play.

Prepare multilingual data

use reviewsDB

db.reviews.insertMany([
  {
    reviewText: "The wireless charger works perfectly for my phone",
    language: "en",
    category: "electronics",
    status: "active"
  },
  {
    reviewText: "La entrega fue rápida y el producto excelente",
    language: "es",
    category: "electronics",
    status: "active"
  },
  {
    reviewText: "Battery life could be better but overall good value",
    language: "en",
    category: "audio",
    status: "active"
  },
  {
    reviewText: "La entrega llegó tarde y en mal estado",
    language: "es",
    category: "accessories",
    status: "active"
  }
])

db.reviews.createIndex({ reviewText: "text" })

Combined query: language + category + keyword

// Recherche orientée production : avis en anglais sur les "electronics" qui mentionnent "charger"
db.reviews.find({
  $text: { $search: "charger" },
  language: "en",
  category: "electronics"
})

// Résultat : uniquement "The wireless charger works perfectly for my phone"
// Cette requête est scopée par langue, catégorie, et mot-clé — rien d'autre ne filtre

Search with exclusion of terms in Spanish

// Avis en espagnol sur la livraison ("entrega") qui n'évoquent pas "tarde" (retard)
db.reviews.find({
  $text: { $search: "entrega -tarde" },
  language: "es"
})
// Résultat : "La entrega fue rápida y el producto excelente"
// "La entrega llegó tarde" est exclu grâce à "-tarde"

Use $language parameter of $text

In addition to the language field filter, you can pass the $language parameter to the $text operator itself. This influences the stemming and stop words used by MongoDB:

// $language affecte le stemming utilisé par MongoDB
db.reviews.find({
  $text: { $search: "battery", $language: "english" }
})

// Combiner $language dans $text ET filtre de champ
db.reviews.find({
  $text: { $search: "entrega excelente", $language: "spanish" },
  language: "es",
  category: "electronics"
})

Important distinction:

  • The language field in your document is a filter that you defined in your schema
  • The $language parameter in $text is a system parameter that controls how MongoDB parses text (stemming, stop words)

Build a funnel dashboard query

// Approche en entonnoir :
// 1. Commencer large avec des mots-clés
// 2. Réduire avec des filtres de catégorie
// 3. Affiner encore avec un filtre de langue
// 4. Ajouter un filtre de statut pour exclure les inactifs

db.reviews.find({
  $text: { $search: "wireless battery charging" },  // 1. mots-clés larges
  category: "electronics",                           // 2. filtre de catégorie
  language: "en",                                    // 3. filtre de langue
  status: "active"                                   // 4. filtre de statut
},
{ score: { $meta: "textScore" }, reviewText: 1 }
).sort({ score: { $meta: "textScore" } })

Advantages of combined filters:

  • Fewer false positives (false positives)
  • Cleaner and targeted results
  • Improved performance because MongoDB applies indexed filters first
  • Applicable to real processing pipelines

Use case:

  • Regional Review Analysis: understand what users are saying in a specific market
  • Content tagging: identify and tag reviews by language and theme
  • Search dashboards: power views filtered by region and category
  • Moderation pipelines: target specific content categories by language for review

4.5 Capstone — Social Media Monitoring for Brand Reputation

The scenario

In this capstone module, you apply all the techniques learned to a real-world problem: monitoring social media to manage brand reputation. The goal is to build a set of queries that cover different aspects of brand monitoring.

Setting up data

use socialDB

db.posts.insertMany([
  {
    content: "Our brand is amazing! #BrandName #quality",
    platform: "instagram",
    lang: "en",
    sentiment: "positive",
    campaignTag: "summer2024",
    url: null
  },
  {
    content: "Worst experience with BrandName. Total scam! #BrandName",
    platform: "twitter",
    lang: "en",
    sentiment: "negative",
    campaignTag: "summer2024",
    url: null
  },
  {
    content: "J'adore BrandName! Super produits #BrandName",
    platform: "instagram",
    lang: "fr",
    sentiment: "positive",
    campaignTag: "summer2024",
    url: null
  },
  {
    content: "BrandName Produkt ist gut! #BrandName",
    platform: "linkedin",
    lang: "de",
    sentiment: "positive",
    campaignTag: null,       // campaignTag manquant !
    url: null
  },
  {
    content: "Check out this spam.com offer! #BrandName deal",
    platform: "twitter",
    lang: "en",
    sentiment: "neutral",
    campaignTag: "summer2024",
    url: "https://spam.com/promo"
  },
  {
    content: "Battery life of BrandName product is incredible! Best I've tried.",
    platform: "facebook",
    lang: "en",
    sentiment: "positive",
    campaignTag: "summer2024",
    url: null
  }
])

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

Query 1: Brand mentions with spam exclusion

// Trouver toutes les mentions de "BrandName" sans spam
db.posts.find({
  $text: { $search: "BrandName -spam" }
})
// Le score baisse progressivement. Ajouter le filtrage de spam réduit le bruit.

Query 2: Filtering by language and platform

// Mentions françaises sur Instagram
db.posts.find({
  $text: { $search: "BrandName" },
  lang: "fr",
  platform: "instagram"
})
// Résultat : le post français Instagram

// Mentions allemandes sur LinkedIn
db.posts.find({
  $text: { $search: "BrandName" },
  lang: "de",
  platform: "linkedin"
})
// Résultat : le post allemand LinkedIn

Request 3: Prioritize risks — negative mentions with campaign tag

// Posts négatifs qui ont un campaignTag (problèmes PR prioritaires)
db.posts.find({
  $text: { $search: "BrandName" },
  sentiment: "negative",
  campaignTag: { $exists: true, $ne: null }
})
// Résultat attendu : les plaintes avec tag de campagne — priorité haute pour l'équipe PR

Query 4: Extract positive testimonials

// Meilleurs témoignages : positifs, sans spam, triés par pertinence
db.posts.find(
  {
    $text: { $search: "BrandName -spam" },
    sentiment: "positive"
  },
  { score: { $meta: "textScore" }, content: 1, platform: 1 }
).sort({ score: { $meta: "textScore" } })
// Résultat : une petite sélection de posts — ce sont vos "hero quotes"

Request 5: Metadata audit — posts without campaignTag

// Posts qui manquent le campaignTag (toutes langues confondues)
db.posts.find({
  $text: { $search: "BrandName" },
  campaignTag: { $exists: false }
})
// ou
db.posts.find({
  $text: { $search: "BrandName" },
  $or: [{ campaignTag: { $exists: false } }, { campaignTag: null }]
})
// Note : cette vérification ignore la langue — c'est intentionnel
// L'audit de complétude ne doit pas être limité par la langue

Query 6: Detect hashtags with $regex

// Détecter tous les posts avec un hashtag (ne nécessite pas de text index)
db.posts.find({
  content: { $regex: /#\w+/ }
})
// Note de performance : fonctionne bien sur des petits datasets
// Pour de grandes collections, préfiltrez par plateforme ou fenêtre temporelle

Query 7: Detect URLs (promotion or spam indicators)

// Détecter les posts qui contiennent une URL
db.posts.find({
  url: { $regex: /https?:\/\//  }
})
// Note : cela détecte seulement la PRÉSENCE d'une URL, ne la valide pas
// Généralement suffisant pour le triage

Query 8: Follow a specific product theme — battery

// Analyser le feedback sur la batterie (positif ET négatif)
db.posts.find({
  $text: { $search: "battery" }
})
// Résultat : à la fois les éloges et les problèmes
// Astuce : le regex est basé sur les caractères, donc il capturera aussi "batteries"
// que $text pourrait manquer selon le stemming

// Version regex pour capturer les variantes
db.posts.find({
  content: { $regex: /batter(y|ies)/i }
})

Request 9: Damage Control List in English

// Mentions de marque en anglais, non spam, avec sentiment négatif
db.posts.find({
  $text: { $search: "BrandName -spam" },
  lang: "en",
  sentiment: "negative"
})
// Ce sont les plaintes authentiques qui comptent pour le support

Request 10: Dashboard feed — clean and well-tagged content

// Feed de tableau de bord :
// - Mentions de marque
// - Pas de spam
// - Plateformes cibles (instagram, linkedin)
// - Sentiment non négatif
// - Correctement tagué

db.posts.find({
  $text: { $search: "BrandName -spam" },
  platform: { $in: ["instagram", "linkedin"] },
  sentiment: { $ne: "negative" },
  campaignTag: { $exists: true, $ne: null }
},
{ score: { $meta: "textScore" }, content: 1, platform: 1, lang: 1 }
).sort({ score: { $meta: "textScore" } })
// Résultat : un petit ensemble de posts propres, tagués, de campagne

Capstone Summary

This practical case illustrates how to intelligently combine $text, $regex, and structured filters to build a complete monitoring system:

ObjectiveTechnique used
Brand mentions without noise$text + term exclusion (-spam)
Territorial filteringFilter on lang + platform
Prioritization of PR risks$text + sentiment: "negative" + campaignTag
Extraction of testimonials$text + sentiment: "positive" + sort by textScore
Metadata Audit$exists: false + $ne: null
Hashtag detection$regex: /#\w+/
URL detection$regex: /https?:\/\//
Thematic monitoring$text + $regex for variants
Damage Control$text + lang + sentiment
Clean DashboardCombination of all filters

Final performance tip: For large social media collections, always pre-filter by platform or a time window (e.g.: createdAt: { $gte: new Date(Date.now() - 86400000) }) before applying regex patterns. This significantly reduces the number of documents to be scanned.


6. Quick Reference — Operator Comparison

Summary table of the three operators

CharacteristicEquality (field: value)$text$regex
Search typeExact matchTokens (whole words)Character Pattern
Index requiredRecommended (classic index)Yes (text index)No (beneficial if anchored ^)
PerformanceVery fastFast (indexed)Varies
Relevance scoringNoYes (textScore)No
StemmingNoYesNo
Stop wordsNoIgnoredNo
Case-insensitiveNo (default)Yes (native)With flag i
Fuzzy searchNoNoNo
Partial matchingNoNoYes
Multiple fieldsMultiple conditionsSingle index (multi-field)By field
MultilingualAccording to values ​​Via $languageVia flag i

Key Syntax Cheat Sheet

// ===== ÉGALITÉ =====
db.col.find({ status: "active" })
db.col.find({ price: { $gt: 50, $lt: 200 } })

// ===== TEXT SEARCH =====
// Créer un index
db.col.createIndex({ field: "text" })
db.col.createIndex({ field1: "text", field2: "text" }, { weights: { field1: 10 } })

// Recherche simple
db.col.find({ $text: { $search: "keyword" } })

// Phrase exacte
db.col.find({ $text: { $search: '"exact phrase"' } })

// Exclusion
db.col.find({ $text: { $search: "keyword -excluded" } })

// Avec score et tri
db.col.find(
  { $text: { $search: "keyword" } },
  { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })

// Avec filtre de langue
db.col.find({ $text: { $search: "mot", $language: "french" } })

// ===== REGEX =====
// Commence par
db.col.find({ field: { $regex: /^value/i } })

// Se termine par
db.col.find({ field: { $regex: /value$/i } })

// Contient
db.col.find({ field: { $regex: /value/i } })

// Format email
db.col.find({ email: { $regex: /\w+@\w+\.\w+/ } })

// Hashtag
db.col.find({ content: { $regex: /#\w+/ } })

// URL
db.col.find({ url: { $regex: /https?:\/\// } })

// Negative lookahead (ne contient pas)
db.col.find({ content: { $regex: /^(?!.*(spam|scam))/is } })

// ===== MÉTADONNÉES MANQUANTES =====
db.col.find({ field: { $exists: false } })
db.col.find({ field: null })
db.col.find({ arrayField: { $size: 0 } })
db.col.find({ stringField: { $regex: /^\s*$/ } })

7. Best practices and summary

Rules for designing text indexes

  1. Plan your search fields in advance — you can only have one text index per collection
  2. Use weights to prioritize certain fields in relevance scoring
  3. Only index the fields you really need — text indexes consume memory and slow down inserts
  4. Prefer targeted multi-field indexes rather than the wildcard "$**": "text" which indexes everything

Rules for using $text

  1. Always check that the text index exists before using $text
  2. Combine $text with structured filters for more targeted and efficient queries
  3. Use $meta: "textScore" to expose and rank results by relevance
  4. Only one $text query per query — you cannot combine several $texts in the same query

Rules for using $regex

  1. Anchor your patterns with ^ when possible to allow the use of indexes
  2. Combine $regex with other filters to reduce the number of documents to scan
  3. Avoid patterns not anchored on large unindexed fields (descriptions, logs)
  4. Avoid $or with multiple regexes — MongoDB does not optimize them efficiently
  5. Use $regex for specific patterns: hashtags, emails, currencies, code formats

When to use what — Decision guide

Besoin d'une correspondance exacte ?
    → Filtre d'égalité (field: value)

Besoin de chercher dans du texte libre ?
    ├── Tokens avec scoring de pertinence ?
    │       → $text + $meta: "textScore"
    ├── Phrase exacte dans l'ordre ?
    │       → $text { $search: '"phrase exacte"' }
    ├── Mot-clé à exclure ?
    │       → $text { $search: "mot -exclu" }
    └── Pattern de caractères ?
            ├── Commence par / se termine par / contient ?
            │       → $regex avec ^, $, ou sans ancres
            ├── Format structuré (email, hashtag, URL) ?
            │       → $regex avec pattern spécifique
            └── Case-insensitive ?
                    → $regex avec flag /i

Key Takeaways

  • MongoDB’s full-text search is powerful for simple to intermediate use cases, but it is not a full-fledged search engine — for advanced needs, use MongoDB Atlas Search
  • $text operates on tokens (whole words), supports stemming and ignores stop words
  • $regex operates on characters, offers full flexibility but can be costly in performance
  • Equality filters remain the fastest choice for exact value searches
  • Combining $text or $regex with structured filters is the key to efficient and accurate queries in production
  • Always test performance with explain() on large collections to verify that your queries are using indexes
// Analyser le plan d'exécution d'une requête
db.products.find({ $text: { $search: "wireless" } }).explain("executionStats")
db.products.find({ name: { $regex: /^Sam/ } }).explain("executionStats")

Search Terms

text · search · pattern · matching · mongodb · nosql · databases · sql · regex · filters · data · query · combine · detect · full-text · language · relevance · exclusion · request · sort · syntax · term · terms · category

More MongoDB & NoSQL courses

View all 7

Interested in this course?

Contact us to book it or get a custom training plan for your team.