The Big Picture
GraphStore — Writing Nodes and Relationships
GraphStore is the single write path for all graph data. It uses parameterized Cypher to prevent injection and batched UNWIND for performance.
Batched UNWIND MERGE
Nodes and relationships are written in batches of 500 items per query. The pattern:Label Hints
Relationship MATCH queries use label hints to speed up endpoint lookups. Instead of searching all nodes, FalkorDB only looks at nodes with the specified label:
Unknown edge types default to
(__Entity__, __Entity__).
Per-Item Fallback
If a batch upsert fails (e.g., a single malformed property causes the whole batch to error), GraphStore falls back to per-item upserts. The behavior differs slightly: for nodes, the first per-item failure raises aDatabaseError (remaining items in that batch are not attempted); for relationships, failures are logged as warnings and processing continues through the batch.
None-ID Guard
Before writing, nodes withNone or empty IDs are filtered out. These come from bad LLM extraction (the LLM sometimes returns entities without proper names). The guard prevents phantom nodes from polluting the graph.
Property Cleaning
All properties go through_clean_properties() before writing:
Cypher Safety
Node labels and relationship types are sanitized viasanitize_cypher_label() to prevent Cypher injection. Properties are applied via parameter maps (e.g., SET n += item.properties), so property keys and values are never interpolated into the Cypher string.
VectorStore — Embeddings, Indexes, and Search
VectorStore handles everything related to vector and fulltext operations.
Index Creation
Vector indexes use FalkorDB’s native vector index syntax:Chunk Indexing (index_chunks)
When chunks are ingested, their text is embedded and stored:- Batch embed: All chunk texts are passed to the embedder via a single
aembed_documentscall. The underlying provider controls how these are internally batched (e.g., via a configurablebatch_size) and may split them across multiple API requests. - Batch write: Vectors are written to Chunk nodes using UNWIND (500 per batch):
- Fallback: If batch embedding fails, chunks are embedded one at a time. If batch writing fails, items are written individually.
Entity Embedding Backfill (backfill_entity_embeddings)
After all documents are ingested, entity nodes need embeddings for vector search. This is done duringfinalize():
- Query: Find entities missing embeddings:
WHERE e.embedding IS NULL - Embed: Batch-embed entity names via
aembed_documents - Write: Store vectors using UNWIND:
- Loop: Repeat until no more entities with NULL embeddings remain. Each batch naturally returns the next un-embedded set.
Relationship Embedding (embed_relationships)
RELATES edges with afact property but no embedding are batch-embedded:
- Query: Find edges with
r.embedding IS NULL AND r.fact IS NOT NULL - Embed: Batch-embed the
facttext - Write: Store vectors on each edge individually (using internal FalkorDB edge IDs):
Search Methods
Vector search on Chunk nodes:Stored Embedding Optimization
During retrieval, thererank_chunks() function uses stored embeddings when possible. Instead of re-embedding all candidate chunks (which would require an expensive API call), it fetches the vectors already stored on Chunk nodes and computes cosine similarity locally. This makes reranking instant when stored embedding coverage is >= 90%.
ensure_indices()
Creates all 5 standard indexes in one call. Tracks state internally (_indices_ensured) to avoid redundant creation. Called automatically after each ingest() call, and re-run during finalize() (which resets the flag).
EntityDeduplicator — Merging Duplicate Entities
After ingesting multiple documents, the same real-world entity might exist as multiple nodes (e.g., “Alice” from doc 1 and “Alice” from doc 5). The deduplicator merges them.Phase 1: Exact Name Match (Always Runs)
- Fetch all
__Entity__nodes with their primary label (the non-__Entity__label) - Group by
(normalized_name.lower(), label)— grouping by label prevents cross-type merging (Person “Paris” and Location “Paris” stay separate) - For each group with duplicates:
- Survivor: The entity with the longest description
- Remap edges: All RELATES and MENTIONED_IN edges from duplicates are redirected to the survivor:
- Delete the duplicate node:
MATCH (e:__Entity__ {id: $dup_id}) DETACH DELETE e
Phase 2: Fuzzy Embedding Match (Optional)
Enabled viafuzzy=True. Catches near-duplicates that have slightly different names (e.g., “J. Doe” and “Jane Doe”):
- Fetch all surviving entities with their labels
- Batch-embed entity names
- Normalize vectors and compute pairwise cosine similarity in blocks (1000 entities per block to avoid OOM)
- Merge pairs above the similarity threshold (default: 0.9), but only within the same label (no cross-type merging)
- Remap and delete as in Phase 1
FalkorDB-Specific Notes
Vector Storage
FalkorDB stores vectors asvecf32 — a native 32-bit float vector type. All vectors in the SDK are stored via:
Vector Search API
Node vector search uses 4 arguments:Graph Deletion
For fast graph deletion, useGRAPH.DELETE (the Redis-level command):
MATCH (n) DETACH DELETE n on large graphs.
Retry Behavior
The connection layer retries transient query failures up to 3 times using exponential backoff with jitter (retry_delay * 2^attempt * random(0.5, 1.5)) and employs a circuit breaker to short-circuit repeated failures. Non-transient errors (containing “already indexed”, “already exists”, or “unknown index”) are raised immediately.