MongoDB $search Stage

Beginner
⏱️ 12 min read
📚 Updated: Jul 2026
🎯 5 Examples
Aggregation

What You’ll Learn

The $search stage brings Atlas Search full-text queries into aggregation pipelines—ranked results, fuzzy matching, and compound filters for product catalogs, knowledge bases, and in-app search bars.

01

Atlas Search

Index required.

02

text query

Basic search.

03

compound

Must / filter.

04

searchScore

Relevance rank.

05

fuzzy

Typo tolerance.

06

vs $match

Not regex scan.

Definition and Usage

$search is an aggregation stage that queries an Atlas Search index on a collection. Unlike $match, which filters by exact predicates, $search finds documents by text relevance—similar to typing into a search box on an e-commerce or documentation site.

💡
Beginner tip

$match: { title: /widget/i } = scan and regex match.
$search: { text: { query: "widget", path: "title" } } = index lookup with relevance ranking.
For user-facing search, prefer $search on Atlas.

Prerequisites: a MongoDB Atlas cluster (or Atlas Search-enabled deployment), sample data in a collection, and a search index whose field mappings include the paths you query. Create the index in the Atlas UI or with createSearchIndex in mongosh—then wait until status is READY before running pipelines.

📝 Syntax

$search wraps Atlas Search operators inside the aggregation stage:

mongosh
{
  $search: {
    index: <string>,           // search index name, e.g. "default"
    <operator>: { ... }        // text, compound, autocomplete, phrase, etc.
  }
}

Syntax Rules

  • index — optional but recommended. Name of the Atlas Search index. Defaults to default when omitted on a single-index collection.
  • text — most common operator. Requires query (search string) and path (field name or array of fields).
  • compound — combine sub-clauses with must, should, mustNot, and filter.
  • First stage — place $search at the start of the pipeline for best performance and correct semantics.
  • Relevance score — expose with { $meta: "searchScore" } in a later $project stage.
  • Atlas only — not available on plain Community MongoDB without Atlas Search.
mongosh
db.articles.aggregate([
  {
    $search: {
      index: "default",
      text: {
        query: "mongodb aggregation",
        path: "title"
      }
    }
  }
])

⚡ Quick Reference

QuestionAnswer
What it doesFull-text search via Atlas Search index inside aggregation
PlatformMongoDB Atlas (Atlas Search)
PrerequisiteSearch index on collection (createSearchIndex)
Basic operatortext: { query, path }
Relevance score{ $meta: "searchScore" } in $project
vs $match regex$search uses index + ranking; regex scans
Basic
{ $search: {
  text: {
    query: "widget",
    path: "name"
  }
}}

Single-field text

Multi-field
path: [
  "title",
  "body"
]

Search many fields

Score
score: {
  $meta: "searchScore"
}

Rank results

Compound
compound: {
  must: [...],
  filter: [...]
}

Boolean logic

🧰 $search Options & Operators

The stage body is an Atlas Search query document. These are the pieces beginners use most often:

index Stage option

Names the search index to query. Must match an index created on the collection. Use getSearchIndexes() to list available indexes.

{ $search: {
  index: "default",
  text: { ... }
}}
text Operator

Full-text match on one or more string fields. query is the user search string; path is the field name or array of field names.

text: {
  query: "wireless keyboard",
  path: "title"
}
compound Operator

Combine clauses: must (required), should (boost), mustNot (exclude), filter (no score impact).

compound: {
  must: [{ text: {...} }],
  filter: [{ equals: {...} }]
}
fuzzy text option

Inside text, add fuzzy: { maxEdits: 1 } to tolerate typos—“mongdb” can still match “mongodb”.

text: {
  query: "mongdb",
  path: "title",
  fuzzy: { maxEdits: 1 }
}

Examples Gallery

Articles collection on Atlas—create a search index, run basic and multi-field text search, build a compound filter, add fuzzy matching, and return ranked results with scores.

📚 Getting Started

Sample articles and Atlas Search index for the labs below.

Example 1 — Articles collection and search index

mongosh
// Requires MongoDB Atlas with Atlas Search enabled
db.articles.drop()

db.articles.insertMany([
  {
    title: "MongoDB Aggregation Pipeline Guide",
    description: "Learn $match, $group, $project, and more stages.",
    category: "database",
    tags: ["mongodb", "aggregation", "tutorial"],
    publishedYear: 2024
  },
  {
    title: "Introduction to Atlas Search",
    description: "Full-text search with $search and relevance scoring.",
    category: "database",
    tags: ["mongodb", "search", "atlas"],
    publishedYear: 2025
  },
  {
    title: "Node.js REST API Tutorial",
    description: "Build a CRUD API with Express and MongoDB.",
    category: "backend",
    tags: ["nodejs", "express", "api"],
    publishedYear: 2023
  },
  {
    title: "Indexing Strategies in MongoDB",
    description: "B-tree, compound, and text indexes explained.",
    category: "database",
    tags: ["mongodb", "indexes", "performance"],
    publishedYear: 2024
  },
  {
    title: "React Hooks for Beginners",
    description: "useState, useEffect, and custom hooks.",
    category: "frontend",
    tags: ["react", "hooks", "javascript"],
    publishedYear: 2025
  }
])

// Create Atlas Search index (wait until status is READY)
db.articles.createSearchIndex(
  "default",
  {
    mappings: {
      dynamic: false,
      fields: {
        title:       { type: "string" },
        description: { type: "string" },
        category:    { type: "string" },
        tags:        { type: "string" },
        publishedYear: { type: "number" }
      }
    }
  }
)

// Verify index is ready before running $search pipelines
db.articles.getSearchIndexes()

How It Works

Atlas Search indexes are separate from regular MongoDB B-tree indexes. The mapping lists which fields are searchable and their types. Until the index status is READY, $search queries fail.

Example 2 — Basic text search on title

mongosh
db.articles.aggregate([
  {
    $search: {
      index: "default",
      text: {
        query: "aggregation pipeline",
        path: "title"
      }
    }
  },
  {
    $project: {
      _id: 0,
      title: 1,
      category: 1
    }
  }
])

// Matches title containing "aggregation" and "pipeline"
// e.g. "MongoDB Aggregation Pipeline Guide"
// Does NOT match articles where terms appear only in description

How It Works

The simplest $search: one text operator, one field path. Atlas tokenizes the query and matches indexed terms in title only.

Example 3 — Multi-field search (title + description)

mongosh
db.articles.aggregate([
  {
    $search: {
      index: "default",
      text: {
        query: "mongodb search",
        path: ["title", "description"]
      }
    }
  },
  {
    $project: {
      _id: 0,
      title: 1,
      description: 1,
      score: { $meta: "searchScore" }
    }
  }
])

// Finds docs where "mongodb" OR "search" appear in title or description
// e.g. "Introduction to Atlas Search" (description mentions $search)
// e.g. "Indexing Strategies in MongoDB" (title has MongoDB)

How It Works

Pass an array to path to search across multiple fields—typical for “search title and body” UX. Add searchScore early so you can sort or filter by relevance.

📈 Practical Patterns

Compound filters, fuzzy typos, and ranked top results.

Example 4 — Compound search (category filter + text query)

mongosh
// Search "mongodb" only in database-category articles
db.articles.aggregate([
  {
    $search: {
      index: "default",
      compound: {
        must: [
          {
            text: {
              query: "mongodb",
              path: ["title", "description"]
            }
          }
        ],
        filter: [
          {
            equals: {
              path: "category",
              value: "database"
            }
          }
        ]
      }
    }
  },
  {
    $project: {
      _id: 0,
      title: 1,
      category: 1
    }
  }
])

// Returns database articles mentioning mongodb
// Excludes "Node.js REST API Tutorial" (backend category)
// filter clauses do not affect score; must clauses do

How It Works

compound mirrors boolean search UI: text box + sidebar filters. Use filter for exact category/year constraints and must for required text terms.

Example 5 — Fuzzy search, score, and top 3 results

mongosh
// User typo: "mongdb" instead of "mongodb"
db.articles.aggregate([
  {
    $search: {
      index: "default",
      text: {
        query: "mongdb tutorial",
        path: ["title", "description", "tags"],
        fuzzy: { maxEdits: 1 }
      }
    }
  },
  {
    $project: {
      _id: 0,
      title: 1,
      tags: 1,
      score: { $meta: "searchScore" }
    }
  },
  { $sort: { score: -1 } },
  { $limit: 3 }
])

// fuzzy maxEdits: 1 tolerates one character difference
// $sort score: -1 → best matches first
// $limit 3 → paginate-like top results for UI

How It Works

Production search bars combine fuzzy matching, relevance scores, and limits. This pattern maps directly to “show top 3 results for user query” API endpoints.

🧠 How $search Works

1

Search index

Atlas Search maintains a Lucene index over mapped fields—tokenized, analyzed text separate from MongoDB’s regular indexes.

Index
2

Query parsed

$search sends operators (text, compound, etc.) to the search index—not a full collection scan.

Query
3

Documents ranked

Matching documents return with relevance scores. Later stages sort, limit, or project fields for the client.

Rank
=

Search results

The pipeline continues with ranked documents—ready for APIs, dashboards, or further aggregation.

📝 Notes

  • $search requires Atlas Search—not available on every MongoDB deployment.
  • Create and verify a search index before querying; use getSearchIndexes() to check status.
  • Field paths in queries must appear in the index mappings—unmapped fields are not searched.
  • Place $search first in the pipeline whenever possible.
  • Use { $meta: "searchScore" } only in stages after $search.
  • Previous topic: $sample. Next: $set.

Conclusion

$search connects Atlas Search to aggregation pipelines: create an index, query with text or compound, expose searchScore, and cap results with $limit.

For exact filters use $match; for search-box UX on Atlas, use $search. Next: $set to add or overwrite fields in pipeline documents.

💡 Best Practices

✅ Do

  • Create explicit search index mappings for fields users will query
  • Put $search first and follow with $project, $sort, $limit
  • Return searchScore when building ranked result lists
  • Use compound.filter for category, date, or status constraints
  • Enable fuzzy sparingly for typo tolerance on short queries

❌ Don’t

  • Assume $search works on self-hosted Community without Atlas Search
  • Query field paths omitted from the search index mapping
  • Replace $search with regex $match for large collections
  • Forget to wait for index status READY after createSearchIndex
  • Put stages before $search unless documentation explicitly allows it

Key Takeaways

Knowledge Unlocked

Five things to remember about $search

Use these points when adding full-text search to aggregation pipelines.

5
Core concepts
📝 02

text query

query + path.

Operator
🔗 03

compound

must + filter.

Pattern
04

searchScore

Rank results.

Score
📈 05

First stage

Start here.

Placement

❓ Frequently Asked Questions

$search runs full-text and relevance-ranked queries inside aggregation pipelines using Atlas Search. It matches documents by meaning and ranking—not just exact field equality—so you can build product search, article lookup, and autocomplete features directly in MongoDB.
Yes. $search is powered by Atlas Search (Lucene-based). It works on MongoDB Atlas clusters and Atlas Search-enabled deployments—not on a standard self-hosted MongoDB Community server without Atlas Search.
The collection needs an Atlas Search index (created with createSearchIndex or the Atlas UI). The index defines which fields are searchable and how they are analyzed. Queries reference the index by name—commonly "default".
Put $search first whenever possible. Atlas Search executes the query before other stages filter or reshape results. Follow with $project, $match on scores, $sort, $limit, or $facet as needed.
$match with $regex scans documents and has no built-in relevance ranking. $search uses a search index, supports fuzzy matching, compound boolean logic, and returns relevance scores via $meta: "searchScore"—much better for user-facing search boxes.
In a $project stage after $search, add score: { $meta: "searchScore" }. Higher scores mean stronger matches. Sort by that field to show best results first.
Did you know?

Atlas Search is built on Apache Lucene—the same technology family behind Elasticsearch and Solr. That means $search supports analyzers, tokenizers, and relevance tuning far beyond what legacy MongoDB $text indexes offer. See the official $search docs.

Continue the Stages Series

Search with $search, then reshape documents with $set.

Next: $set →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful