Skip to content

MongoDB Vector Search — Python and Atlas Modes

MongoVectorBackend exposes two vector-search modes:

Mode Switch When to use
Python (default) vector_search_mode="python" (or omit) Development, demos, small corpora (~ < 100K vectors per tenant). Cosine similarity computed in application code over every tenant vector.
Atlas vector_search_mode="atlas" Production. Server-side approximate-nearest-neighbour search via MongoDB Atlas's $vectorSearch aggregation stage. Scales sublinearly.

The default python mode is byte-identical to v7.20.0 behaviour. Existing adopters keep their code unchanged on upgrade.


Python mode

The constructor's defaults pick the Python mode:

from symfonic.memory.backends.mongodb import MongoVectorBackend

backend = MongoVectorBackend(uri="mongodb://localhost:27017")
# Default: vector_search_mode="python"

backend.search(scope, query_embedding, top_k=5) fetches every vector matching {"tenant_id": scope.tenant_id}, computes cosine similarity in Python, sorts, and returns the top k. Tenant isolation is enforced by the {"tenant_id": ...} filter on the find() call.

Cost profile. Linear in the per-tenant corpus size. A 1M-vector tenant incurs 1M cosine evaluations per query. Acceptable for development. Painful in production once a tenant's corpus crosses ~100K vectors.


Atlas mode

from symfonic.memory.backends.mongodb import MongoVectorBackend

backend = MongoVectorBackend(
    uri="mongodb+srv://...mongodb.net/",
    vector_search_mode="atlas",
    atlas_index_name="symfonic_vector_idx",
    atlas_num_candidates_multiplier=10,  # default
    atlas_num_candidates_min=100,        # default
)

backend.search(...) issues an aggregation pipeline that pushes the ANN search to the Atlas server. The pipeline has three stages — every stage is load-bearing for tenant isolation. See Guide 15 — MongoDB Atlas Vector Search for the full set-up, including the load-bearing $match defence-in-depth nuance.

Atlas-mode parameters

Parameter Default Purpose
vector_search_mode "python" Set to "atlas" to enable. Unknown values raise ValueError at boot.
atlas_index_name "vector_index" Name of the Atlas vector-search index. Must exist on the collection or be created via ensure_vector_search_index().
atlas_num_candidates_multiplier 10 Multiplied by top_k to compute Atlas's numCandidates. Higher values trade query latency for recall.
atlas_num_candidates_min 100 Floor for numCandidates so small top_k values still query enough candidates to surface relevant hits.

numCandidates is computed as max(min, multiplier × top_k). For top_k=5 with defaults, Atlas considers at least 100 candidates; for top_k=100, it considers 1000. Atlas's docs recommend numCandidates >= 10 × limit for good recall on production-sized corpora.


Bootstrapping the Atlas index

await backend.ensure_vector_search_index(dimensions=1536, similarity="cosine")

Idempotent — safe to call on every boot. Reads list_search_indexes() and short-circuits if atlas_index_name already exists. When the index is missing, the helper creates it with two field definitions:

  1. A vector field on embedding carrying the embedding dimension and similarity function.
  2. A filter field on tenant_id.

The filter field is what enables the $vectorSearch.filter clause inside _search_atlas to actually pre-filter at the index level. Without it, the filter clause is a silent no-op — see the load-bearing-$match note in Guide 15.


Migration path

  1. Dev / staging. Keep vector_search_mode="python". No changes needed.
  2. Add Atlas index. Provision a MongoDB Atlas cluster (M10 or higher supports Vector Search). Either run ensure_vector_search_index from your boot script or create the index in the Atlas UI per Guide 15.
  3. Switch the flag. Set vector_search_mode="atlas" in your backend construction. Restart. Same backend.search(...) calls now run server-side.
  4. Tune num_candidates. Watch query latency vs recall. If latency tolerable but quality low, raise atlas_num_candidates_multiplier.

The switch is observable. Cache prefixes computed from search results stay stable as long as the embedding model and corpus are unchanged. Adopters that require byte-identical scores across modes should leave the mode at python; Atlas's ANN scoring is approximate by construction.