Table of Contents
- 1.1 What is Full-text Search in MongoDB?
- 1.2 Compare
$text,$regexand equality filters - 1.3 How do text indexes work in MongoDB
- 1.4 Limitations of the full-text search native of MongoDB
- 2.1 Create and run a simple text search
- 2.2 Phrase searches and term exclusion
- 2.3 Use relevance scores to sort and rank results
- 2.4 Combine text search with query filters
- 3.1 Understand and apply basic regex patterns
- 3.2 Using regex flags and advanced patterning techniques
- 3.3 Combine regex with filters and query conditions
- 3.4 Limitations and performance considerations of regex in MongoDB
- 4.1 Search product reviews and descriptions with text search
- 4.2 Identify and exclude flagged terms or phrases
- 4.3 Find documents with missing tags or metadata
- 4.4 Combine language filters, category and search terms
- 4.5 Capstone — Social Media Monitoring for Brand Reputation
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:
- Tokenization: the text is divided into units called tokens (individual words)
- Removal of stop words: common words like
the,is,aare ignored because they do not provide semantic value - Matching: tokens are compared to the user’s request
- 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:
- Create a text index on the field(s) to index
- Use the
$textoperator 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?
| Location | Recommended 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:
- MongoDB takes the text value of a field (e.g.:
"Fast Wireless Charger for iPhone") - It breaks it up into individual words (tokens):
["Fast", "Wireless", "Charger", "iPhone"] - The stop words are removed:
["Fast", "Wireless", "Charger", "iPhone"](in this case, no stop words) - stemming is applied: words are reduced to their root —
chargerandchargersboth becomecharg - 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()
1.4 Limitations of MongoDB’s native full-text search
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.
When to use MongoDB Atlas Search?
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
2.1 Create and run a simple text search
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" })
Perform a basic full-text search
// 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.
Use MongoDB Compass for text search
In MongoDB Compass:
- Go to the Documents tab of your collection
- In the filter bar, enter:
{ $text: { $search: "wireless charger" } } - Click Find to run the query
- Results show all documents that match
To display the relevance score, switch to the Projection tab:
- Add
scorefield 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
Phrase Search
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:
- Filter:
{ $text: { $search: "wireless charger" } } - Projection:
{ score: { $meta: "textScore" } } - 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
| Flag | Name | Effect |
|---|---|---|
i | Case-insensitive | Ignore upper/lower case difference |
m | Multiline | ^ and $ apply to each line |
x | Extended | Ignore spaces and comments in the pattern |
s | Dotall | . 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
$orwith 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 Type | Based on tokens (whole words) | Based on characters (patterns) |
| Relevance Scoring | Yes (textScore) | No |
| Performance | Quick (index) | Variable (depends on anchor) |
| Required Index | Yes (text index) | No (but beneficial if anchored) |
| Stop words | Automatically ignored | Not taken into account |
| Stemming | Yes | No |
| Fuzzy matching | No | No (except complex patterns) |
| Use cases | Searching for keywords in descriptions, reviews, blogs | Substrings, format validation, hashtags, emails, currencies |
The decision rule:
- Use
$textwhen you want tokenized search with performance and scoring — for fields like product names, blog content, reviews, descriptions - Use
$regexwhen 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
4.1 Search product reviews and descriptions with text search
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
languagefield in your document is a filter that you defined in your schema - The
$languageparameter in$textis 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:
| Objective | Technique used |
|---|---|
| Brand mentions without noise | $text + term exclusion (-spam) |
| Territorial filtering | Filter 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 Dashboard | Combination 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
| Characteristic | Equality (field: value) | $text | $regex |
|---|---|---|---|
| Search type | Exact match | Tokens (whole words) | Character Pattern |
| Index required | Recommended (classic index) | Yes (text index) | No (beneficial if anchored ^) |
| Performance | Very fast | Fast (indexed) | Varies |
| Relevance scoring | No | Yes (textScore) | No |
| Stemming | No | Yes | No |
| Stop words | No | Ignored | No |
| Case-insensitive | No (default) | Yes (native) | With flag i |
| Fuzzy search | No | No | No |
| Partial matching | No | No | Yes |
| Multiple fields | Multiple conditions | Single index (multi-field) | By field |
| Multilingual | According to values | Via $language | Via 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
- Plan your search fields in advance — you can only have one text index per collection
- Use weights to prioritize certain fields in relevance scoring
- Only index the fields you really need — text indexes consume memory and slow down inserts
- Prefer targeted multi-field indexes rather than the wildcard
"$**": "text"which indexes everything
Rules for using $text
- Always check that the text index exists before using
$text - Combine
$textwith structured filters for more targeted and efficient queries - Use
$meta: "textScore"to expose and rank results by relevance - Only one
$textquery per query — you cannot combine several$texts in the same query
Rules for using $regex
- Anchor your patterns with
^when possible to allow the use of indexes - Combine
$regexwith other filters to reduce the number of documents to scan - Avoid patterns not anchored on large unindexed fields (descriptions, logs)
- Avoid
$orwith multiple regexes — MongoDB does not optimize them efficiently - Use
$regexfor 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
$textoperates on tokens (whole words), supports stemming and ignores stop words$regexoperates on characters, offers full flexibility but can be costly in performance- Equality filters remain the fastest choice for exact value searches
- Combining
$textor$regexwith 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