Atlas Search
Learn how Atlas Search brings full-text search powered by Apache Lucene to MongoDB, including fuzzy matching and autocomplete.
Atlas Search
In the Querying section, we used $text with a text index to search course titles and descriptions. $text works, but it is basic — no typo tolerance, limited ranking, no autocomplete.
Atlas Search is MongoDB's built-in full-text search engine, available on MongoDB Atlas. It is built on Apache Lucene — the same technology behind Elasticsearch — and brings powerful search features directly into your aggregation pipeline, with no separate search service to manage.
Atlas Search vs $text
$text | Atlas Search | |
|---|---|---|
| Availability | Any MongoDB deployment | Atlas only |
| Engine | MongoDB's built-in text index | Apache Lucene |
| Typo tolerance (fuzzy matching) | ❌ No | ✅ Yes |
| Autocomplete | ❌ No | ✅ Yes |
| Relevance scoring | Basic | Advanced — Lucene scoring |
| Highlighting matched terms | ❌ No | ✅ Yes |
| Multiple analyzers (language-aware) | ❌ No | ✅ Yes |
| Setup | createIndex({ field: "text" }) | Search index created in Atlas UI or via API |
If your app is hosted on Atlas — which we covered in the Basics section — Atlas Search is available with no additional infrastructure.
Creating a Search Index
Search indexes are different from regular MongoDB indexes — they are created and managed through the Atlas UI, the Atlas CLI, or the createSearchIndex command.
Via mongosh
db.courses.createSearchIndex(
"courses_search",
{
mappings: {
dynamic: false,
fields: {
title: {
type: "string",
analyzer: "lucene.standard"
},
description: {
type: "string",
analyzer: "lucene.standard"
}
}
}
}
)dynamic: false means only the fields you explicitly list are indexed for search. Set dynamic: true to automatically index all string fields — simpler to start, but less control.
Via Atlas UI
- Go to your cluster in Atlas
- Click Search in the left sidebar
- Click Create Search Index
- Choose the
coursescollection - Define field mappings — same structure as above
- Click Create
Index creation takes a minute or two. Once it shows "Active", you can use it.
The $search Aggregation Stage
Atlas Search is used through a $search stage — always as the first stage in an aggregation pipeline.
Basic Text Search
db.courses.aggregate([
{
$search: {
index: "courses_search",
text: {
query: "computer science",
path: "title"
}
}
}
])This searches the title field for documents matching "computer science" — using Lucene's relevance scoring, not just exact substring matching.
Search Across Multiple Fields
db.courses.aggregate([
{
$search: {
index: "courses_search",
text: {
query: "programming",
path: ["title", "description"]
}
}
}
])Fuzzy Matching — Typo Tolerance
This is one of the biggest advantages over $text. Atlas Search can match queries even when the user makes a typo.
// User searches "compter scince" (typos for "computer science")
db.courses.aggregate([
{
$search: {
index: "courses_search",
text: {
query: "compter scince",
path: "title",
fuzzy: {
maxEdits: 2, // allow up to 2 character edits
prefixLength: 2 // first 2 characters must match exactly
}
}
}
}
])Result — still finds "Computer Science" courses, despite the typos.
maxEdits controls how many character changes (insertions, deletions, substitutions) are allowed — 1 or 2 is typical. prefixLength requires the beginning of the word to match exactly, which improves both accuracy and performance.
Fuzzy matching is what makes search feel "smart" to users — they do not need to spell things perfectly. This is impossible with $text, which requires whole-word matches with no typo tolerance.
Relevance Scoring
Every result from $search includes a relevance score, accessible via $meta: "searchScore":
db.courses.aggregate([
{
$search: {
index: "courses_search",
text: {
query: "computer science",
path: ["title", "description"]
}
}
},
{
$project: {
title: 1,
description: 1,
score: { $meta: "searchScore" }
}
},
{ $sort: { score: -1 } }
])Result:
[
{ title: "Computer Science Fundamentals", score: 4.82 },
{ title: "Intro to Programming", score: 2.15 },
{ title: "Computer Networks", score: 1.93 }
]Higher scores mean stronger matches — a course titled "Computer Science Fundamentals" scores higher for the query "computer science" than one that only mentions "computer" in its description.
Autocomplete
Atlas Search has a dedicated autocomplete field type and operator — perfect for search-as-you-type features.
Define an Autocomplete Field
db.courses.createSearchIndex(
"courses_autocomplete",
{
mappings: {
dynamic: false,
fields: {
title: {
type: "autocomplete",
tokenization: "edgeGram",
minGrams: 2,
maxGrams: 10
}
}
}
}
)Using Autocomplete
// User has typed "Comp" so far
db.courses.aggregate([
{
$search: {
index: "courses_autocomplete",
autocomplete: {
query: "Comp",
path: "title"
}
}
},
{ $limit: 5 },
{ $project: { title: 1, _id: 0 } }
])Result:
[
{ title: "Computer Science Fundamentals" },
{ title: "Computer Networks" },
{ title: "Composition and Writing" }
]As the user types more characters, send updated queries — the results narrow down in real time, exactly like search suggestions on any modern website.
Highlighting Matches
Atlas Search can return information about exactly which parts of the text matched — useful for showing users "why" a result matched, with the matched terms highlighted.
db.courses.aggregate([
{
$search: {
index: "courses_search",
text: {
query: "programming",
path: "description"
},
highlight: {
path: "description"
}
}
},
{
$project: {
title: 1,
description: 1,
highlights: { $meta: "searchHighlights" }
}
}
])The highlights field contains the matched snippet broken into parts you can render with <mark> tags or similar in your frontend.
Combining $search with Other Stages
$search must be the first stage, but you can follow it with any normal aggregation stages — $match, $group, $lookup, $project, and so on.
// Search for "math" courses, only in 10th grade, with teacher details
db.courses.aggregate([
{
$search: {
index: "courses_search",
text: { query: "math", path: "title" }
}
},
{
$match: { grade: "10th" }
},
{
$lookup: {
from: "teachers",
localField: "teacherId",
foreignField: "_id",
as: "teacher"
}
},
{ $unwind: "$teacher" },
{
$project: {
title: 1,
grade: 1,
teacherName: "$teacher.name",
score: { $meta: "searchScore" }
}
},
{ $sort: { score: -1 } }
])Atlas Search handles the text relevance ranking; standard aggregation stages handle filtering, joining, and shaping — all in one pipeline.
School System — Upgrading Course Search
Here is the complete upgrade from $text (Querying section) to Atlas Search with fuzzy matching and autocomplete:
Search Index Setup
// Full-text search with fuzzy matching support
db.courses.createSearchIndex(
"courses_search",
{
mappings: {
dynamic: false,
fields: {
title: { type: "string", analyzer: "lucene.standard" },
description: { type: "string", analyzer: "lucene.standard" }
}
}
}
)
// Separate index for autocomplete on title
db.courses.createSearchIndex(
"courses_autocomplete",
{
mappings: {
dynamic: false,
fields: {
title: {
type: "autocomplete",
tokenization: "edgeGram",
minGrams: 2,
maxGrams: 10
}
}
}
}
)Search Endpoint — With Fuzzy Matching
app.get('/api/courses/search', async (req, res) => {
const { q } = req.query;
const results = await db.collection('courses').aggregate([
{
$search: {
index: "courses_search",
text: {
query: q,
path: ["title", "description"],
fuzzy: { maxEdits: 1, prefixLength: 2 }
}
}
},
{ $limit: 10 },
{
$project: {
title: 1,
description: 1,
grade: 1,
score: { $meta: "searchScore" }
}
}
]).toArray();
res.json(results);
});Autocomplete Endpoint
app.get('/api/courses/autocomplete', async (req, res) => {
const { q } = req.query;
if (!q || q.length < 2) {
return res.json([]);
}
const suggestions = await db.collection('courses').aggregate([
{
$search: {
index: "courses_autocomplete",
autocomplete: { query: q, path: "title" }
}
},
{ $limit: 5 },
{ $project: { title: 1, _id: 0 } }
]).toArray();
res.json(suggestions);
});GET /api/courses/search?q=compter scince
→ still finds "Computer Science" courses despite typos
GET /api/courses/autocomplete?q=Comp
→ ["Computer Science Fundamentals", "Computer Networks", ...]Quick Reference
| Feature | Syntax |
|---|---|
| Create search index | db.collection.createSearchIndex(name, definition) |
| Basic text search | { $search: { text: { query, path } } } |
| Multi-field search | path: ["title", "description"] |
| Fuzzy matching | fuzzy: { maxEdits: 1, prefixLength: 2 } |
| Relevance score | { $meta: "searchScore" } |
| Autocomplete field | type: "autocomplete", tokenization: "edgeGram" |
| Highlighting | highlight: { path: "field" } → { $meta: "searchHighlights" } |
Start with $text for simple keyword search — it works everywhere and requires no extra setup. Move to Atlas Search when your users need typo tolerance, autocomplete, or when search relevance quality becomes a real product concern. The aggregation pipeline structure stays familiar — $search is just a new first stage that plugs into everything else you already know.
Time Series Collections
Learn how MongoDB time series collections work, when to use them, and how they improve on the manual bucket pattern for storing time-stamped data.
Sharding
Learn what sharding is, how MongoDB distributes data across multiple servers using shard keys, and when your application actually needs it.