symfonic.memory.backends.mongodb¶
mongodb ¶
MongoGraphBackend + MongoVectorBackend -- async MongoDB backends using motor.
Requires the mongodb optional dependency group:
pip install symfonic-core[mongodb]
Both backends are natively async via motor's AsyncIOMotorClient. Tenant isolation is enforced via per-tenant collection namespacing (graph) and a tenant_id field filter (vectors).
MongoGraphBackend ¶
MongoGraphBackend(
uri: str,
database: str = "symfonic",
collection_prefix: str = "graph",
server_selection_timeout_ms: int = 5000,
connect_timeout_ms: int = 5000,
socket_timeout_ms: int = 10000,
)
GraphBackend implementation backed by MongoDB via motor.
Tenant isolation: each tenant gets a dedicated collection pair.
Collections: {prefix}_{tenant_id}_nodes, {prefix}_{tenant_id}_edges
The constructor is intentionally lazy -- no network I/O occurs at init time.
Motor's AsyncIOMotorClient defers all server-selection to the first
awaited operation, so placing it here is safe. Use ping() to verify
connectivity from an async context before trusting the backend.
Source code in src/symfonic/memory/backends/mongodb.py
add_edge
async
¶
Persist an edge document. Sets tenant_id from scope.
Source code in src/symfonic/memory/backends/mongodb.py
add_node
async
¶
Persist a node document. Sets tenant_id + materialises scope_path.
Source code in src/symfonic/memory/backends/mongodb.py
delete_edge
async
¶
Delete a single edge by its ID.
Source code in src/symfonic/memory/backends/mongodb.py
delete_node
async
¶
Delete a node, optionally cascading to connected edges.
Source code in src/symfonic/memory/backends/mongodb.py
get_neighbors
async
¶
get_neighbors(
scope: TenantScope,
node_id: NodeId,
relationship: str | None = None,
) -> list[MemoryNode]
Return nodes connected to node_id by edges, filtered by relationship.
Source code in src/symfonic/memory/backends/mongodb.py
get_node
async
¶
Fetch a node by ID within the scope's visible prefix chain.
Source code in src/symfonic/memory/backends/mongodb.py
ping
async
¶
Return True if the MongoDB server is reachable, False otherwise.
Call this from an async context (e.g. FastAPI startup) to verify connectivity rather than discovering failures on the first data operation.
Source code in src/symfonic/memory/backends/mongodb.py
query_edges
async
¶
query_edges(
scope: TenantScope,
filters: dict[str, Any] | None = None,
limit: int = 50,
offset: int = 0,
) -> list[MemoryEdge]
Query edges from the tenant edge collection with pagination.
Mongo collections are already tenant-scoped, so no tenant_id filter is needed in the query document itself.
Source code in src/symfonic/memory/backends/mongodb.py
query_nodes
async
¶
query_nodes(
scope: TenantScope,
filters: dict[str, Any],
limit: int | None = 50,
) -> list[MemoryNode]
Filter nodes by arbitrary field criteria within the scope.
v8.0: ANDs the prefix-isolation filter (always-on) onto the field
query. Combined under $and so a $or in either part never
clobbers the other.
v8.7.2: limit=None returns all matching nodes (no truncation).
Source code in src/symfonic/memory/backends/mongodb.py
traverse
async
¶
BFS traversal from start node up to max_depth hops.
Source code in src/symfonic/memory/backends/mongodb.py
update_node
async
¶
Partial update of node fields. Raises KeyError if node not found.
Source code in src/symfonic/memory/backends/mongodb.py
upsert_edge
async
¶
Insert edge or increment weight by 1 if (source, target, relationship) matches.
Uses MongoDB $inc with upsert=True so the operation is atomic.
On insert the full document is written; on match only weight changes.
Source code in src/symfonic/memory/backends/mongodb.py
MongoVectorBackend ¶
MongoVectorBackend(
uri: str,
database: str = "symfonic",
collection: str = "vectors",
server_selection_timeout_ms: int = 5000,
connect_timeout_ms: int = 5000,
socket_timeout_ms: int = 10000,
*,
vector_search_mode: str = "python",
atlas_index_name: str = "vector_index",
atlas_num_candidates_multiplier: int = 10,
atlas_num_candidates_min: int = 100,
)
VectorBackend backed by MongoDB.
Stores document vectors with tenant-scoped metadata. Similarity search loads all tenant vectors and computes cosine similarity in Python.
For production workloads, migrate to MongoDB Atlas Vector Search ($vectorSearch) which runs ANN search server-side.
The constructor is intentionally lazy -- no network I/O occurs at init time.
Use ping() to verify connectivity from an async context.
Construct the MongoDB vector backend.
T-7.21.C1 -- adds Atlas $vectorSearch mode. The default
vector_search_mode='python' keeps the v7.20 behaviour byte-
identical: Python cosine similarity over all tenant vectors --
known-bad for large corpora, documented for prod replacement.
vector_search_mode='atlas' switches the search method
to issue a server-side ANN query via MongoDB Atlas's
$vectorSearch aggregation stage.
Atlas-mode parameters:
atlas_index_name: name of the Atlas vector-search index. Must be pre-created on the Atlas collection (or created via :meth:ensure_vector_search_index).atlas_num_candidates_multiplier: numCandidates is computed asmax(atlas_num_candidates_min, multiplier * top_k). Atlas's docs recommend numCandidates >= 10*limit for good recall on production-sized corpora.atlas_num_candidates_min: floor for the numCandidates computation so small top_k values still query enough candidates to surface relevant hits.
Raises ValueError for unknown vector_search_mode values
so misconfiguration fails fast at boot rather than silently
falling through to the Python path.
Source code in src/symfonic/memory/backends/mongodb.py
add
async
¶
add(
scope: TenantScope,
ids: list[str],
embeddings: list[list[float]],
metadatas: list[dict[str, Any]],
documents: list[str],
) -> None
Upsert vector documents scoped to the tenant.
Source code in src/symfonic/memory/backends/mongodb.py
count
async
¶
Return the number of stored vectors visible to the tenant scope.
Without this method EpisodicLayer.count_events() falls back to 0, which silently disables every consolidation phase gated on episodic volume for Mongo-backed deployments. Round-2 fix.
Source code in src/symfonic/memory/backends/mongodb.py
delete
async
¶
Delete vectors by ID, scoped to the tenant.
Source code in src/symfonic/memory/backends/mongodb.py
ensure_vector_search_index
async
¶
Idempotent helper: create the Atlas vector-search index if missing.
T-7.21.C4 -- so adopters can bootstrap a fresh Atlas collection
from code instead of hand-rolling the index in the Atlas UI.
Reads the existing search-indexes via list_search_indexes()
and short-circuits if the index named
self._atlas_index_name already exists (idempotent semantics:
safe to call on every boot).
The created index definition includes tenant_id as a filter
field -- this is the index-side counterpart to the
$vectorSearch.filter clause used by _search_atlas.
Without registering tenant_id as a filter field, the Atlas
filter clause silently turns into a no-op (the same class
as the load-bearing $match defence-in-depth at
_search_atlas); the helper here closes the index-side leg
by stamping the filter dimension at create-time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dimensions
|
int
|
embedding vector dimension (e.g. 1536 for OpenAI text-embedding-3-small). Atlas requires this at index-create time. |
required |
similarity
|
str
|
similarity function -- |
'cosine'
|
Source code in src/symfonic/memory/backends/mongodb.py
ping
async
¶
Return True if the MongoDB server is reachable, False otherwise.
search
async
¶
Dispatch to the configured vector-search backend.
T-7.21.C2 -- the body is split into mode-specific helpers so
adopters can opt in to Atlas $vectorSearch by passing
vector_search_mode='atlas' at construction time without
touching this method. Default mode is byte-identical to v7.20.