Atlas Search & Vector Search
A shopper types "wireles mous" into the search box. A plain query finds nothing, because no product name contains those exact words. Another shopper types "something to keep my phone charged on a long train journey", which does not mention a single product name, yet the Power Bank is clearly the right answer. The first problem needs full-text search, which understands words, typos and relevance. The second needs vector search, which understands meaning.
MongoDB has both built in. They began as Atlas features called Atlas Search and Atlas Vector Search; MongoDB now calls them MongoDB Search and MongoDB Vector Search, because since 2026 they also run on self-managed servers. This lesson gives you an overview of how they work and what the queries look like.
How search differs from normal queries
The text index from Indexes and $regex can find words, but they cannot rank results by relevance, forgive spelling mistakes, suggest completions as you type or count matches by category. MongoDB Search uses a separate search engine based on Apache Lucene, the library behind many well-known search engines. It runs in a separate process called mongot next to the database, keeps its own search indexes in sync with your collections automatically, and is queried with aggregation stages:
| Stage | Purpose |
|---|---|
$search | Full-text search with relevance scores, fuzzy matching, autocomplete, phrases, highlighting |
$searchMeta | Counts and facets (for example, how many matches per category) without the documents |
$vectorSearch | Semantic similarity search over vector embeddings |
Both $search and $vectorSearch must be the first stage of a pipeline.
Where it runs
- Atlas: available on Atlas clusters, including the Free tier with a limited number of search indexes. Create indexes in the Atlas UI, with the Atlas CLI or from
mongosh. - Self-managed: generally available for Community and Enterprise servers running MongoDB 8.2 or later, using the separate
mongotbinary. It is not part of the normal Windows installer; you run it with Docker or on Linux. - Local development: the Docker image
mongodb/mongodb-atlas-local, or the Atlas CLI commandatlas deployments setup, starts a single-node replica set withmongotincluded, which is the easiest way to practise on your own computer.
Creating a search index
A search index tells mongot which fields to index and how. With dynamic mapping, every string field is indexed automatically, which is fine for learning:
db.products.createSearchIndex('default', { mappings: { dynamic: true } })
db.products.getSearchIndexes()default
The first command prints the new index's name. Building happens in the background, so the index is not usable at once; getSearchIndexes() (output not shown) lists each index with a status, and you wait until it says READY. For production you usually write a static mapping that lists only the fields you search, with a type for each, such as autocomplete for the name field behind a search box.
Full-text search with $search
Now the misspelled search works. The text operator searches the listed paths, and fuzzy allows each word to differ by one character:
db.products.aggregate([
{ $search: {
index: 'default',
text: { query: 'wireles mous', path: ['name', 'tags'], fuzzy: { maxEdits: 1 } }
} },
{ $limit: 5 },
{ $project: { name: 1 } }
])[
{ _id: 101, name: 'Wireless Mouse' },
{ _id: 105, name: 'Bluetooth Earbuds' }
]Results come back ordered by relevance score: the mouse matches both words, while the earbuds only match wireless in their tags. Add score: { $meta: 'searchScore' } to the $project stage to see the scores. Other operators inside $search include autocomplete (prefix matching while typing), phrase (words in order), range (numbers and dates) and compound, which combines must, should, filter and mustNot clauses, for example "match the words, but only in stock".
What is a vector embedding?
An embedding is a list of numbers, often 512 to 2,048 of them, produced by a machine learning model from a piece of text (or an image). The model is trained so that texts with similar meaning get similar lists of numbers. "Power bank 20000 mAh for travel" and "keep my phone charged on a train journey" share few words but produce vectors that are close together, while "mechanical keyboard" lands far away.
Vector search finds the stored vectors closest to the vector of the user's question. Closeness is measured with a similarity function: cosine, euclidean or dotProduct. Use the one your embedding model recommends. You create the embeddings with a model from an AI provider (MongoDB's own Voyage AI models, or others), store them in a field such as embedding in each product document, and create the same kind of embedding for each search query.
Vector search with $vectorSearch
A vector search index lists the vector field, its number of dimensions (which must match your model) and the similarity function. Fields you want to pre-filter on are added with type filter:
db.products.createSearchIndex('product_vectors', 'vectorSearch', {
fields: [
{ type: 'vector', path: 'embedding', numDimensions: 1024, similarity: 'cosine' },
{ type: 'filter', path: 'category' }
]
})The query passes the embedding of the user's question as queryVector. Here questionVector stands for an array of 1,024 numbers that your application got from the embedding model:
db.products.aggregate([
{ $vectorSearch: {
index: 'product_vectors',
path: 'embedding',
queryVector: questionVector,
numCandidates: 100,
limit: 5,
filter: { category: { $in: ['Mobile', 'Accessories'] } }
} },
{ $project: { name: 1, price: 1, score: { $meta: 'vectorSearchScore' } } }
])The important fields are:
numCandidates: how many nearest neighbours to consider before picking the bestlimit. More candidates give better accuracy at some cost in speed; a common starting point is about 20 timeslimit. It can be at most 10,000.limit: how many results to return.filter: an optional pre-filter on fields indexed asfilter, applied before the similarity search.exact: true: compare against every vector instead of using the fast approximate method. Accurate but slow; useful for small collections and testing.
Scores run from 0 to 1, with 1 the most similar. MongoDB also offers automated embedding (in preview at the time of writing), where the index creates embeddings for you and the query can pass plain text instead of a vector.
Common mistakes
- Querying before the index is READY. Search index builds are asynchronous; an index that is still building returns no results.
- Putting
$searchor$vectorSearchafter another stage. They must be first in the pipeline. - Mixing embedding models. Documents and queries must use the same model and the same number of dimensions, or the similarity scores are meaningless.
- Setting
numCandidateslower thanlimit. It must be at leastlimit, and larger values improve recall. - Expecting search on a plain local install. Self-managed search needs the separate
mongotprocess (8.2 or later), or the Atlas Local Docker image.
Key points
- MongoDB Search (formerly Atlas Search) adds relevance-ranked, typo-tolerant full-text search using search indexes kept by the
mongotprocess. - It runs on Atlas and, since 2026, generally on self-managed Community and Enterprise servers from 8.2 with
mongot. - Create indexes with
createSearchIndex()and query with$search,$searchMetaand$vectorSearch, always as the first stage. - Embeddings are number vectors that capture meaning; vector search finds the stored vectors most similar to a query vector.
$vectorSearchneedsindex,path,queryVector,limitand usuallynumCandidates, and can pre-filter on indexed fields.- Hybrid search (
$rankFusion,$scoreFusion) combines keyword and vector results, and vector search powers RAG chatbots.
Exercise
Plan search for the shop. (1) Write the $search stage for a search box that matches the typed text against product names with one typo allowed, but only returns products that are in stock. (2) Write the vector search index definition you would need if the embedding model produces 1,536-dimension vectors and you want to pre-filter by category and price.
Show solution
db.products.aggregate([
{ $search: {
index: 'default',
compound: {
must: [{ text: { query: 'blutooth earbud', path: 'name', fuzzy: { maxEdits: 1 } } }],
filter: [{ range: { path: 'stock', gt: 0 } }]
}
} },
{ $limit: 10 },
{ $project: { name: 1, price: 1, score: { $meta: 'searchScore' } } }
])
db.products.createSearchIndex('product_vectors', 'vectorSearch', {
fields: [
{ type: 'vector', path: 'embedding', numDimensions: 1536, similarity: 'cosine' },
{ type: 'filter', path: 'category' },
{ type: 'filter', path: 'price' }
]
})compound lets the text clause decide relevance while the filter clause only removes out-of-stock products without affecting the score. The vector index's numDimensions must equal the model's output size, and every field used in a $vectorSearch filter must be declared with type filter.
Quick Quiz
1. Which process performs MongoDB Search queries alongside the database?
mongot builds and queries the Lucene-based search indexes next to mongod.
2. Where must $vectorSearch appear in a pipeline?
Both $search and $vectorSearch must be the first stage of their pipeline.
3. What is a vector embedding?
Embedding models turn text or images into vectors whose distance reflects similarity of meaning.
4. What does numCandidates control?
More candidates improve accuracy at some cost; it must be at least limit and at most 10,000.
Learn MongoDB with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
