FalkorDB: edge_fulltext_search Node-By-Label-Scan per hit (redundant endpoint labels defeat the edge-uuid index) → add_episode times out at scale
Summary
On the FalkorDB backend, edge_fulltext_search emits:
CALL db.idx.fulltext.queryRelationships('RELATES_TO', $query) YIELD relationship AS rel, score
MATCH (n:Entity)-[e:RELATES_TO {uuid: rel.uuid}]->(m:Entity)
...FalkorDB's planner does not recognize that pinning the edge by its indexed uuid already determines the endpoints, so the (n:Entity) / (m:Entity) labels trigger a Node By Label Scan over all :Entity nodes — once per fulltext hit. With a broad fulltext query that matches many edges this is O(hits × entities), and add_episode fails with Query timed out as the graph grows.
Evidence (GRAPH.EXPLAIN, ~3,300 RELATES_TO edges / ~3,100 Entity nodes)
With the endpoint labels:
Limit → Sort → Project → Filter
→ Edge By Index Scan | [e:RELATES_TO]
→ Node By Label Scan | (n:Entity) ← scans every Entity, per hitWithout the endpoint labels (MATCH (n)-[e:RELATES_TO {uuid: rel.uuid}]->(m)):
Limit → Sort → Project → Edge By Index Scan | [e:RELATES_TO]Measured on the live graph: 25s+ timeout with labels → 0.007s without. Dropping the labels takes add_episode from 1/20 episodes persisting to 20/20.
Why it's FalkorDB-specific
graphiti is Neo4j-first and the FalkorDB path reuses the identical Cypher — the labelled MATCH is byte-identical in graphiti_core/search/search_utils.py, graphiti_core/driver/falkordb/operations/search_ops.py, and graphiti_core/driver/neo4j/operations/search_ops.py. Neo4j's planner uses the relationship index and skips the redundant label check; FalkorDB's does not. The endpoint labels are semantically redundant — a RELATES_TO edge always connects two Entity nodes.
Note: on FalkorDB the FalkorDriver leaves search_interface = None, so the actual path add_episode runs is the module-level search_utils.edge_fulltext_search fallback (the FalkorSearchOperations.edge_fulltext_search class method is not on the call path).
Proposed fix
Drop the redundant endpoint labels in the FalkorDB edge fulltext path:
- MATCH (n:Entity)-[e:RELATES_TO {uuid: rel.uuid}]->(m:Entity)
+ MATCH (n)-[e:RELATES_TO {uuid: rel.uuid}]->(m)at search_utils.edge_fulltext_search (primary, FalkorDB call path) and the FalkorSearchOperations class-method copy. The Neo4j path can keep the labels.
Smaller, separate item
_build_falkor_fulltext_query does not escape group_id, so a non-alphanumeric id (the default '_', or an id containing :) yields an invalid (@group_id:"...") RediSearch filter → Syntax error near group_id. v0.29.2's _escape_fulltext_group_id fixes that — but it does not touch the endpoint-label issue above (the edge_fulltext_search Cypher is unchanged between 0.29.0 and 0.29.2).
Environment
- graphiti-core 0.29.0 (also verified the Cypher is unchanged in 0.29.2)
- FalkorDB v4.18.9
Source: getzep/graphiti