Graph store interface
cvcdocdb.graph_store defines GraphStore,
the abstract interface both cvcdocdb.networkx_graph and
cvcdocdb.neo4j_graph implement. Code written against this interface
(insert/update/delete, FK validation, cascade-delete strategies, bulk import,
schema introspection, vector indexing) works unchanged against either
backend.
Note
Neo4jGraph does not currently subclass GraphStore directly (it
duck-types the same interface) — see the abstract methods below for the
full contract either backend is expected to satisfy.
See also cvcdocdb.networkx_graph and cvcdocdb.neo4j_graph for
the concrete backends, and cvcdocdb.migration for a generic tool that
copies an entire graph from one GraphStore implementation to another
using only this interface.
Abstract graph store interface for the DRM model.
Both Neo4jGraph and NetworkXGraph implement this ABC so that
code in entities.py and elsewhere can accept any graph store
without knowing the backing technology.
Usage:
from cvcdocdb.graph_store import GraphStore
from cvcdocdb.networkx_graph import NetworkXGraph
def process(store: GraphStore) -> None:
node = Node(pk={"id": 1}, main_label="Test")
store.insertNode(node)
store.close()
process(NetworkXGraph())
- class cvcdocdb.graph_store.GraphStore[source]
Bases:
ABCAbstract interface for graph-backed stores (Neo4j, NetworkX, etc.).
Every concrete implementation must support the same core operations: insert, update, replace, delete nodes and relations, bulk import, and existence checks.
Subclasses must implement all abstract methods.
- abstractmethod insertNode(node, insert_parent=True, update=False, replace=False, **kwargs)[source]
Insert a node into the graph store.
- Parameters:
node (Node) – The node to insert.
insert_parent (bool) – If the node is a WeakNode, insert its parent first. Defaults to True.
update (bool) – If True, MERGE the node and update attributes without deleting it. Defaults to False.
replace (bool) – If True and the node already exists, delete it (with detach) and create a fresh one. Defaults to False.
**kwargs (Any) – Additional implementation-specific parameters.
- Returns:
The internal node identifier assigned by the store.
- Return type:
- abstractmethod insertRelation(rel, update=False, replace=False, **kwargs)[source]
Insert a directed relation (edge) between two nodes.
- Parameters:
- Returns:
The internal relation identifier.
- Return type:
- abstractmethod deleteNode(node, propagation=False, detach=False, on_delete='cascade')[source]
Delete a node from the graph store.
- Parameters:
- Returns:
True if the node was deleted, False otherwise.
- Return type:
- abstractmethod create(migration, update=False, replace=False)[source]
Bulk import nodes and relations.
- query(filter_dict=None, projection=None, sort=None, limit_val=None, params=None)[source]
Query nodes using MongoDB-style dictionary filters (NetworkX) or execute raw Cypher (Neo4j).
Hybrid API — the first argument accepts either:
dict — MongoDB-style filter matching against
main_labeland node attributes. Supports operators$eq,$ne,$gt,$gte,$lt,$lte,$in,$nin,$exists,$regex.str — a Cypher query string (
MATCH,CREATE,DELETE,SET,RETURN,ORDER BY,LIMIT, aggregations, parameter substitution via$name).
- Parameters:
filter_dict (Dict[str, Any] | str | None) – Filter dict for MongoDB-style queries, or Cypher string for Cypher-style queries.
projection (Dict[str, int] | None) – Dict of fields to include (1) or exclude (0). Ignored for Cypher queries (use
RETURNaliases).sort (Tuple[str, int] | None) – Tuple of
(field_name, direction)where direction is1(ascending) or-1(descending). Ignored for Cypher queries (useORDER BY).limit_val (int | None) – Maximum number of results to return. Ignored for Cypher queries (use
LIMIT).params (Dict[str, Any] | None) – Optional parameter dict for
$namesubstitution in Cypher queries. Ignored for MongoDB-style queries.
- Returns:
A list of dicts, one per matching node (MongoDB-style) or one per result record (Cypher-style).
- Raises:
NotImplementedError – If the backend does not support queries.
- Return type:
- enable_vector_index(property_name, dimensions, space='cosine', **kwargs)[source]
Enable vector indexing for one property if the backend supports it.
Backends without ANN/vector support should keep the default behavior and raise
NotImplementedError.
- query_vector_index(property_name, vector, top_k=10)[source]
Query nearest nodes for a vector if the backend supports it.
- list_vector_indexes()[source]
Return metadata for every enabled vector index, as
[{"property_name": ..., "dimensions": ..., "space": ...}, ...].Used by
cvcdocdb.migration.migrate()to recreate vector indexes on a target backend. Backends without vector index support (the default) return an empty list.
- get_node(node_id)[source]
Retrieve a node by its internal id.
Default implementation returns None. Subclasses may override.
- get_node_ids()[source]
Return all internal node ids in the graph.
Returns the backend-specific internal ids (Neo4j
id(n), NetworkX node keys). These are opaque identifiers and must not be compared with primary keys.Default implementation returns an empty list.
- get_node_pks()[source]
Return all primary keys of nodes in the graph.
Each element is a dict with
main_labelandpkkeys matching theNodeinterface.Default implementation returns an empty list.
- get_edges()[source]
Return all edges as
(src_node_id, dst_node_id, rel_type)tuples.The node ids match what
get_node_ids()returns (backend- specific internal ids).Default implementation returns an empty list.
- get_node_attrs(node_id)[source]
Return attributes stored for a node.
Default implementation returns None.
- get_edge_attrs(u, v, key)[source]
Return attributes stored for an edge.
Default implementation returns None.
- get_dependency_value(node_id, relation_type)[source]
Return the
nameprimary key of the single node reachable from node_id via an outgoing relation_type edge.Used to resolve
be_value_properties(seeIndividu): a property such asnomis materialised at insert time as a separateAtribut/Valornode connected via aNOMedge instead of being stored directly on the node. This is a targeted, single-hop lookup — it must not scan the whole graph.- Parameters:
- Returns:
The connected node’s
namepk value, or None if there is no such edge.- Return type:
str | None
Default implementation returns None.
- debug()[source]
Return a human-readable snapshot of the graph state.
Default implementation returns an empty dict.
- create_group(strong_node, weak_nodes=None, weak_relations=None, **kwargs)[source]
Create a strong node together with its WeakNodes and WeakRelations atomically.
All nodes and relations are inserted in a single isolated transaction. If any step fails the entire group is rolled back so the graph is never left in a partially-created state.
- Parameters:
strong_node (Node) – The root (non-weak) node of the group.
weak_nodes (List[Node] | None) – Optional list of WeakNode instances belonging to this group.
weak_relations (List[Relation] | None) – Optional list of WeakRelation instances that connect the strong node (or other nodes) to the WeakNodes.
kwargs (Any)
- Returns:
The internal id of the
strong_node.- Raises:
RuntimeError – If any part of the group creation fails — the transaction is rolled back automatically.
- Return type:
- init_propagation(background=False, progress_callback=None)[source]
Scan the backend graph and initialize propagation properties on nodes and edges that are missing them.
This method inspects every node and edge, determines whether it participates in a WeakNode / WeakRelation hierarchy, and sets the corresponding
_propagate,is_weak,parent_relation, and_dependenciesproperties.Lazy + background approach: the first call runs synchronously and marks the graph as initialized. Subsequent calls return
Falseimmediately. For large graphs setbackground=Trueto run the scan in a background thread (the method still returnsTrueonce initialization completes).- Parameters:
background (bool) – If True, run the scan in a background thread. The method returns immediately after starting the thread.
progress_callback (callable | None) – Optional callable
(processed, total)called periodically during the scan.
- Returns:
Trueif initialization was performed (or is running in the background),Falseif already initialized.- Return type:
- abstractmethod schema_yaml(db_name)[source]
Introspect the database and return a YAML schema description.
The YAML contains labels, properties, relationship types, counts, and Python class names derived from the schema.
- get_subdocuments(strong_node)[source]
Return all subdocuments (WeakNodes) reachable from a strong node through
_propagateedges.This method follows edges in their declared direction (
parent → child) and returns every descendant WeakNode as a dict withlabel,pk, andpropertieskeys.- Parameters:
strong_node (Node) – The root (non-weak) node whose subdocuments should be retrieved.
- Returns:
{ "label": "Section", "pk": {"section": "intro"}, "properties": {"title": "Introduction", ...}, }
- Return type:
A list of dicts, one per subdocument
- Raises:
NotImplementedError – If the backend does not support subdocument queries.