Storage Backends¶
PyGraphDB separates graph logic from storage. GraphDB receives a key-value
store instance and a serializer instance.
LMDB Backend¶
Use LMDBStore for a mature embedded backend with named sub-databases.
from pygraphdb.graphdb import GraphDB
from pygraphdb.kvstores import LMDBStore
from pygraphdb.serializers import PickleSerializer
store = LMDBStore(path="graph_lmdb", map_size=2**30)
graph_db = GraphDB(store, PickleSerializer())
LMDB keeps separate databases for nodes, edges, adjacency, typed adjacency, and
sorted indexes. Increase map_size when loading large graphs.
LevelDB Backend¶
Use LevelDBStore when you want LevelDB through plyvel.
from pygraphdb.graphdb import GraphDB
from pygraphdb.kvstores import LevelDBStore
from pygraphdb.serializers import PickleSerializer
store = LevelDBStore(path="graph_leveldb")
graph_db = GraphDB(store, PickleSerializer())
plyvel requires compatible CPython wheels or local LevelDB build tooling. If
installation fails on Python 3.14 or a free-threaded interpreter, create a Python
3.12 environment and install pygraphdb[leveldb] there.
RocksDB Backend¶
Use PyRexStore for RocksDB through the optional pyrex-rocksdb package.
This backend uses one physical RocksDB database with prefixed keys and exposes
several RocksDB tuning knobs.
from pygraphdb.graphdb import GraphDB
from pygraphdb.kvstores import PyRexStore
from pygraphdb.serializers import PickleSerializer
store = PyRexStore(
path="graph_rocksdb",
parallelism=4,
max_background_jobs=4,
write_buffer_size=64 * 1024 * 1024,
bloom_bits_per_key=10,
)
graph_db = GraphDB(store, PickleSerializer())
disable_wal=True can be useful for bulk-loading experiments, but it weakens
durability and should not be used as a safe default.
When installed with pyrex-rocksdb>=0.3.0a0, PyRexStore can use PyRex’s
native write_columnar_batch API through GraphDB.ingest_nodes_arrow and
GraphDB.ingest_edges_arrow. The columnar methods currently require
caller-provided serialized node_value and edge_value payloads and edge
ingestion is append-only.
Indexes¶
All backends implement sorted index primitives used by labels, relationship type
catalogs, property lookups, and range scans. The high-level indexes maintained by
GraphDB are:
label indexes for
Node.labelsandGraphDB.nodes_by_labelrelationship type indexes for
edge.properties["type"]andGraphDB.edges_by_typeexplicit node and edge property indexes
composite label/property and type/property indexes
scalar range indexes for indexed string and numeric properties
Property indexes are intentionally explicit. Register them only for predicates you expect to use frequently:
graph_db.create_node_property_index("name")
graph_db.create_edge_property_index("score")
graph_db.nodes_by_property("name", "Aspirin")
graph_db.edges_by_property_range("score", 0.8, None)
Cypher uses these indexes when possible for label/property scans and typed relationship predicates. Index definitions are persisted in backend metadata, so reopened databases continue maintaining the configured property indexes.
Columnar ingestion keeps label, relationship type, property, composite, and range indexes current for configured indexed properties.
Backend Index Interface¶
Backend implementations expose lower-level sorted index methods such as
put_index_entry, delete_index_entry, iter_index_prefix, and range
index equivalents. Most users should prefer the GraphDB helpers above.
Backend Selection Pattern¶
from pathlib import Path
from pygraphdb.graphdb import GraphDB
from pygraphdb.kvstores import LMDBStore, LevelDBStore, PyRexStore
from pygraphdb.serializers import PickleSerializer
def open_graph(path: str, backend: str = "lmdb") -> GraphDB:
Path(path).parent.mkdir(parents=True, exist_ok=True)
if backend == "lmdb":
store = LMDBStore(path=path, map_size=2**30)
elif backend == "leveldb":
store = LevelDBStore(path=path)
elif backend == "rocksdb":
store = PyRexStore(path=path)
else:
raise ValueError(f"unknown backend: {backend}")
return GraphDB(store, PickleSerializer())
Cleanup¶
Always close stores when a script or notebook cell is finished with them.
graph_db = GraphDB(LMDBStore(path="example_lmdb"), PickleSerializer())
try:
graph_db.put_node(Node(node_id="n1"))
finally:
graph_db.close()