Text Search
MongoDB's text search provides full-text search capability over string content. It tokenizes strings, ignores stop words, and supports stemming for language-aware searches. For advanced search requirements, MongoDB Atlas Search (built on Apache Lucene) offers far more power.
Creating a Text Index
Before using $text, you must create a text index on the field(s) you want to search. A collection can have at most one text index.
Creating text indexes
// Single field text index
db.articles.createIndex({ title: "text" })
// Multiple fields — each gets a weight (default 1)
db.articles.createIndex(
{ title: "text", body: "text", tags: "text" },
{
weights: {
title: 10, // title matches rank higher
body: 5,
tags: 2
},
name: "ArticleTextIndex"
}
)
// Wildcard text index — covers ALL string fields
db.articles.createIndex({ "$**": "text" })Basic Text Search with $text
$text search examples
// Search for documents containing "mongodb" OR "tutorial"
db.articles.find({ $text: { $search: "mongodb tutorial" } })
// Phrase search — wrap in escaped quotes
db.articles.find({ $text: { $search: '"mongodb tutorial"' } })
// Negation — exclude documents containing "beginner"
db.articles.find({ $text: { $search: "mongodb -beginner" } })
// Combine: must contain "mongodb", must NOT contain "deprecated"
db.articles.find({ $text: { $search: "mongodb -deprecated" } })$text Operator Options
Option | Type | Description |
|---|---|---|
$search | string | The search string — space-separated terms are OR'd; phrases use escaped quotes; prefix with - to negate |
$language | string | Language for stop word removal and stemming (e.g. "english", "french", "german"). Default: "english" |
$caseSensitive | boolean | Whether matching is case-sensitive. Default: false (case-insensitive) |
$diacriticSensitive | boolean | Whether to distinguish accented characters (e.g. é vs e). Default: false |
Relevance Score with $meta
MongoDB assigns a text relevance score to each matched document. Use { score: { $meta: "textScore" } } in projection to retrieve it, and sort by it for best-first ordering.
Sort by text relevance
// Project the relevance score and sort best-first
db.articles.find(
{ $text: { $search: "mongodb indexing performance" } },
{ score: { $meta: "textScore" }, title: 1, _id: 0 }
).sort({ score: { $meta: "textScore" } })
// Example result:
// { title: "MongoDB Index Performance Deep Dive", score: 4.75 }
// { title: "Optimizing MongoDB Queries", score: 3.20 }
// { title: "Introduction to MongoDB", score: 1.10 }
// In an aggregation pipeline
db.articles.aggregate([
{ $match: { $text: { $search: "mongodb performance" } } },
{ $addFields: { score: { $meta: "textScore" } } },
{ $sort: { score: -1 } },
{ $limit: 10 },
{ $project: { title: 1, score: 1, _id: 0 } }
])Phrase Search and Negation
Advanced $text syntax
// Exact phrase — must appear as-is
db.articles.find({
$text: { $search: '"web development"' }
})
// Exclude a term — results must NOT contain "mongodb"
db.articles.find({
$text: { $search: "-mongodb" }
})
// Combine: exact phrase AND exclude a term
db.articles.find({
$text: { $search: '"web development" -php' }
})
// Multiple phrases
db.articles.find({
$text: { $search: '"node.js" "rest api" -deprecated' }
})Language Support
MongoDB supports stop words and stemming for many languages. The default is English. Set $language in the query or on the index itself to override.
Language-specific search
// Create a German text index
db.articles_de.createIndex(
{ title: "text", body: "text" },
{ default_language: "german" }
)
// Query with explicit language override
db.articles_de.find({
$text: {
$search: "datenbank performance",
$language: "german"
}
})
// Disable stop word removal by setting language to "none"
db.articles.find({
$text: {
$search: "the quick brown fox",
$language: "none" // "the" is now searchable
}
})
// Supported languages include:
// "english", "french", "german", "spanish", "italian",
// "portuguese", "dutch", "russian", "chinese", "arabic", and moreLimitations of $text
Only one text index is allowed per collection — you cannot have two separate text indexes
No partial word / prefix matching — searching "mongo" does NOT match "mongodb" (use regex for that)
No fuzzy matching — typos return no results
No ranking customization beyond field weights set at index creation
No faceted search or aggregation-style filtering by category
$text cannot be used inside $or with other predicates at the same query level
Not available in $lookup pipelines
Atlas Search — The Better Alternative
For production full-text search, MongoDB Atlas Search (available on Atlas M10+ clusters) provides: fuzzy matching, autocomplete, highlighting, faceted navigation, synonyms, and custom scoring — all powered by Apache Lucene.
Atlas Search with $search
// Atlas Search aggregation pipeline using $search stage
db.articles.aggregate([
{
$search: {
index: "default", // Atlas Search index name
text: {
query: "mongodb performance",
path: ["title", "body"],
// Fuzzy matching — tolerates typos
fuzzy: {
maxEdits: 1, // 1 character difference allowed
prefixLength: 3 // first 3 chars must match exactly
}
}
}
},
{
$project: {
title: 1,
score: { $meta: "searchScore" },
highlights: { $meta: "searchHighlights" }
}
},
{ $sort: { score: -1 } },
{ $limit: 10 }
])
// Atlas Search autocomplete
db.articles.aggregate([
{
$search: {
index: "autocomplete_index",
autocomplete: {
query: "mongo", // user is typing "mongo..."
path: "title",
tokenOrder: "sequential"
}
}
},
{ $limit: 5 },
{ $project: { title: 1, _id: 0 } }
])$text operator can only be used in the first stage of an aggregation pipeline, and it cannot be combined with $or on a different field at the same query filter level. If you need to text-search AND filter on another field, add the additional filter as a separate query predicate alongside $text.$text is perfectly adequate. For production user-facing search — where users expect Google-like relevance, typo tolerance, and faceted filtering — migrate to Atlas Search early. Retrofitting it later is more work than starting with it.