Atlas Search & Vector Search

MongoDB · Lesson 22 of 28 8 min read Updated

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:

StagePurpose
$searchFull-text search with relevance scores, fuzzy matching, autocomplete, phrases, highlighting
$searchMetaCounts and facets (for example, how many matches per category) without the documents
$vectorSearchSemantic 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 mongot binary. 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 command atlas deployments setup, starts a single-node replica set with mongot included, 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:

MongoDB Shell
db.products.createSearchIndex('default', { mappings: { dynamic: true } })
db.products.getSearchIndexes()
Output
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.

Now the misspelled search works. The text operator searches the listed paths, and fuzzy allows each word to differ by one character:

MongoDB Shell
db.products.aggregate([
  { $search: {
      index: 'default',
      text: { query: 'wireles mous', path: ['name', 'tags'], fuzzy: { maxEdits: 1 } }
  } },
  { $limit: 5 },
  { $project: { name: 1 } }
])
Output
[
  { _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:

MongoDB Shell
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:

MongoDB Shell
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 best limit. More candidates give better accuracy at some cost in speed; a common starting point is about 20 times limit. It can be at most 10,000.
  • limit: how many results to return.
  • filter: an optional pre-filter on fields indexed as filter, 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 $search or $vectorSearch after 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 numCandidates lower than limit. It must be at least limit, and larger values improve recall.
  • Expecting search on a plain local install. Self-managed search needs the separate mongot process (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 mongot process.
  • 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, $searchMeta and $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.
  • $vectorSearch needs index, path, queryVector, limit and usually numCandidates, 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
MongoDB Shell
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?

2. Where must $vectorSearch appear in a pipeline?

3. What is a vector embedding?

4. What does numCandidates control?

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