We use cookies to enhance your experience on the site
CodeWorlds
Back to collections
Guide12 min read

MongoDB Atlas Vector Search, vectors inside your database

MongoDB Atlas Vector Search adds vectors to a document database. The $vectorSearch stage, quantisation, Voyage models, and a comparison with dedicated stores.

MongoDB Atlas Vector Search, vectors inside your database

MongoDB Atlas Vector Search adds similarity search to the document database already holding your application data. A vector is an ordinary document field and a query is an aggregation pipeline stage, so there is no second system to keep in sync. Since 2025 the platform also ships its own embedding models, acquired along with Voyage AI.

The main advantage: no second system

A typical semantic search architecture puts production data in one database and vectors in another. That sounds harmless until you count what follows. You maintain a synchronisation path, handle the case where the write to one database succeeded and the other failed, and accept that search results point at documents whose content needs a separate fetch.

That problem does not exist here, because the vector is a field on the same document as everything else. There is one write, and it either fully succeeds or does not. A search result already carries every field, so there is no second round trip for content.

The second consequence is filtering. Since metadata lives in the same document, a condition like "only this user's documents from the last thirty days" is an ordinary filter rather than a mechanism bolted on the side. Dedicated vector databases require duplicating metadata, and every change to it means updating two places.

It is only fair to describe the self hosted situation, since a great deal of material on it is out of date. Search is served by a separate process, mongot, running beside the database itself and kept in step with it through change streams. Since September 2025 that process can also run outside the managed service: full text and vector search are available in the free Community Edition from version 8.2 and in the Enterprise release, so the claim that vectors require the cloud stopped being true. The caveats are serious, though. It remains a public preview, designated by the vendor for development and evaluation rather than production, and it runs on Linux only. If production vector search on your own servers today is a project requirement, Qdrant or Milvus is the sounder pick.

Index and query

An index is defined as a JSON document where each field has type vector or filter. The first describes the embedding itself, the second the fields you will narrow results by.

Code
JSON
{
  "fields": [
    {
      "type": "vector",
      "path": "embedding",
      "numDimensions": 1536,
      "similarity": "cosine"
    },
    { "type": "filter", "path": "owner" },
    { "type": "filter", "path": "created_at" }
  ]
}

The dimension count has to match your model's output exactly. The similarity measure comes in three variants: cosine, dotProduct, and euclidean. Semantic search defaults to cosine, since it looks at a vector's direction rather than its magnitude. Dot product yields the same ranking for normalised vectors and is cheaper to compute in that case.

The query is an aggregation pipeline stage:

Code
JavaScript
db.documents.aggregate([
  {
    $vectorSearch: {
      index: "embedding_index",
      path: "embedding",
      queryVector: questionVector,
      numCandidates: 200,
      limit: 10,
      filter: { owner: userId }
    }
  },
  { $project: { title: 1, content: 1, score: { $meta: "vectorSearchScore" } } }
])

Three constraints on this stage are worth memorising, since each can block a project midway. It must be the first stage of any pipeline it appears in. It cannot be used in a view definition, in a lookup sub-pipeline, or in a faceting stage. Results can be passed onward, including into a lookup, but the search itself always starts the pipeline.

numCandidates, the parameter that matters most

This parameter governs the trade-off between recall and latency, and it is also the one most often set at random.

The mechanism works like this: approximate search walks a neighbourhood graph while maintaining a candidate queue of the size given in numCandidates. The larger the queue, the better the odds that the genuinely nearest document lands in the results, and the longer the query takes.

The documentation gives a concrete rule: set numCandidates at least twenty times higher than limit. For ten results that means two hundred candidates, which yields roughly ninety to ninety five percent overlap with exact search. That is a sensible starting point and not worth changing without measurement.

If you need complete accuracy, setting exact to true scans every vector instead of using the graph. On small sets or under a very narrow filter it can beat approximate search outright, since little remains after filtering. That is an underused option: when a filter leaves a few hundred documents, an exhaustive scan usually beats tuning the candidate queue.

Quantisation, or fitting into memory

Memory is the main cost here, because the vector index has to live in it. Quantisation stores coordinates at lower precision and comes in two variants.

VariantCoordinate storageVector size reductionSearch process memory reduction
No quantisation32 bitsnonenone
Scalar8 bits4 times3.75 times
Binary1 bit32 times24 times

Scalar quantisation is the safe default. A fourfold saving at a slight recall cost is usually the best return on risk any single setting will give you here.

Binary is more radical and requires understanding one thing. At one bit per coordinate, comparing two vectors reduces to counting differing bits, which is extremely fast and informationally poor. That is why binary quantisation gains accuracy as the candidate count rises, while scalar reaches its ceiling earlier. The vendor's measurements report that at high candidate counts binary catches up with scalar and sometimes passes it, at the price of markedly higher latency above a thousand candidates.

The practical rule: start with scalar. Reach for binary when the set is large enough that memory becomes a real budget constraint, and when you are prepared to pay for it with a higher candidate count.

Embedding models inside the platform

In February 2025 MongoDB acquired Voyage AI for roughly 220 million dollars, and the reason was embedding and reranking models. The consequences of that decision are visible in the product today.

The most important is automated embedding, in public preview in the Community Edition since January 2026 and on Atlas since 11 May 2026. Instead of computing a vector on your side and storing it with the document, you point at a text field and a model, and the database computes embeddings on write and refreshes them when content changes.

That removes a whole class of bugs nobody usually designs for. A document modified without recomputing its vector stays in the index with a stale representation and quietly degrades results. A historical batch processed with a different model than new documents produces a set where old and new records stop competing meaningfully. Automated embedding takes responsibility for both cases.

The price is binding yourself to one vendor's models. The Voyage 4 family covers several sizes and the quality is high, but moving to a model from outside the platform means returning to computing vectors by hand. There is more on the models themselves in the Voyage AI writeup.

Comparison with dedicated stores

FeatureAtlas Vector SearchMilvusPineconepgvector
Data and vectors togetheryesnonoyes
Self hostingpublic preview, Community 8.2+yesnoyes
Metadata filteringnatural, in the documentseparate fieldsseparate fieldsnatural, in the row
Automated embeddingsyes, Voyage modelsnopartialno
Target scaletens of millionsbillionsbillionsmillions
Cost on a small setlow if you already run Atlashighmediumlowest

The selection rule is simpler here than in other comparisons. If your application data already sits in MongoDB, adding a separate vector database is a decision that needs justifying rather than a default path. If it sits in PostgreSQL, the same logic leads to the pgvector extension. Dedicated stores start winning at the scale where memory costs exceed the cost of maintaining an extra system, and that usually means hundreds of millions of vectors.

Cost and when it stops paying off

The bill for this is not a separate line item next to the database but a consequence of cluster size, which is both an advantage and a trap. An advantage because on a small set vector search costs practically nothing beyond what you already pay. A trap because the vector index has to fit in memory, so at some point it forces a move to a larger cluster tier, and that raises the bill for everything at once, including the data that has nothing to do with vectors.

A simple calculation is worth doing before deciding. Take the document count, multiply by your model's dimension count and by four bytes, then divide by the quantisation factor you intend to enable. A million documents at fifteen hundred dimensions is roughly six gigabytes uncompressed and roughly one and a half with scalar quantisation. That single number tells you which cluster tier you will land on, and it is more trustworthy than any calculator because it reflects your model.

You will recognise the moment to consider moving vectors out when you find yourself enlarging the cluster solely because of the index rather than because of traffic or data volume. At that point a dedicated vector database, billed separately and scaled independently, starts coming out cheaper despite the cost of running a second system. As long as the cluster grows for mixed reasons, though, splitting data across two places usually makes matters worse rather than better.

Common mistakes

The first is omitting filter fields from the index definition. A condition applied in a query to a field never declared as filter will not behave as expected, and the message does not always point at the cause directly. Declare filter fields alongside the vector, from the index's first version.

The second is too low a candidate count combined with filtering. A filter narrows the set, so the candidate queue depletes sooner and results can be worse than the twentyfold rule alone would suggest. Under narrow filters, either raise the candidate count or switch to exact search.

The third is holding vectors from two different models in one field. This happens when the embedding provider changes mid project and produces results that look random. Changing models requires recomputing the whole set, not only new documents.

The fourth is forgetting about document size. A thousand dimensional vector stored as an array of floats takes considerable room in a document, and MongoDB's document size limit is hard. With several vectors in one document it is worth doing the arithmetic before a batch load stalls.

The fifth is using vector search where text matching would do. A query for an invoice number or an exact product name is served by an ordinary index, faster and cheaper. Vectors answer descriptively phrased questions, not identifier lookups.

Fitting it into an application stack

In a typical retrieval augmented generation system this database plays both roles at once: it stores documents and serves search. Integrations exist on the LangChain and LlamaIndex side, so wiring it up reduces to naming a collection and an index.

One design decision deserves attention because it recurs in every such deployment: whether to split documents into chunks stored as separate documents, or to keep an array of chunks inside one. Separate documents are simpler to work with and align naturally with the search stage, at the cost of duplicated metadata. An array in a single document saves space but complicates queries and approaches the size limit faster. For most workloads the first approach ages better, and duplicated metadata is cheaper than a complicated pipeline.

FAQ

Does Atlas Vector Search run on my own server?

Yes, with caveats. Search is provided by a separate process running beside the database, and since September 2025 it can also run in the Community Edition from version 8.2 and in the Enterprise release. It remains a public preview, designated by the vendor for development and evaluation rather than production, and it runs on Linux only. Where production requirements demand keeping data on your own infrastructure, a dedicated vector database fits better today.

How do I choose numCandidates?

Start at twenty times the result limit, since that ratio yields roughly ninety percent overlap with exact search. Raise it if you filter narrowly or if recall measured on your own queries comes out poor.

Scalar or binary quantisation?

Scalar as the default, since it gives a fourfold memory saving at a slight recall cost. Binary only once memory is a genuine constraint, and only with a willingness to raise the candidate count, without which recall drops noticeably.

Do I have to compute embeddings myself?

You do not. Automated embedding with Voyage models is in public preview, on Atlas since May 2026, and generates and refreshes vectors as documents are written. Manual computation remains an option for anyone wanting a model from outside the platform.

Can I combine vector search with text search?

Yes, by merging the results of two queries within one aggregation pipeline and ranking them together. It takes more work than the ready made rank fusion found in full text engines, but it gives complete control over how the two lists are weighted.

Syntax details are covered in the $vectorSearch stage documentation, and the quantisation variants in the vector quantisation page.