Design decisions¶
These are the current product and architecture choices. Historical phase documents explain how the repository arrived here, but they are not the authority for present behavior.
Embedded first, with protocol adapters¶
The primary experience is still an in-process library: pip install kglite
or cargo add kglite, load a graph, and call it directly. This removes a
mandatory service hop and keeps graph state deployable with the application.
Embedded-first does not mean “Python-only” or “no protocols.” The same core is wrapped by bundled MCP and Bolt servers, and non-Rust bindings can use the C ABI. Those adapters are useful integration boundaries, but they do not turn KGLite into a replicated, horizontally scaled database service.
The storage modes also change the old “must fit in RAM” limit. mapped spills
property columns during construction, and disk uses CSR plus mmap for large
graphs. The remaining tradeoffs are explicit: no built-in replication or high
availability, writes serialize, and in-memory performance remains the design
centre.
Cypher first¶
Cypher is the common query surface for people, agents, and every binding. New per-query capabilities normally land as Cypher functions or procedures, which makes them immediately available through Python, Rust, C, MCP, and Bolt. Direct API methods remain appropriate for lifecycle, storage configuration, dataset loading, embedder registration, and other operations Cypher cannot express.
KGLite implements a documented subset rather than pretending to implement all of openCypher. Unsupported syntax should fail clearly. Supported semantics are protected by an independently authored differential corpus and optional Neo4j comparison. Silent wrong rows are worse than a declared gap.
One primary type plus secondary labels¶
A node has one immutable primary type and may have secondary labels. The primary type anchors schema sharing, ID lookup, property layout, and the most important candidate indexes. Secondary labels are additive tags with their own index. See Multi-label rationale for the full contract.
Backend traits instead of one concrete graph¶
The memory backend uses petgraph’s StableDiGraph, whose stable indices are a
good fit for incremental mutation and algorithms. Mapped mode retains a graph
topology while moving property storage toward mmap. Disk mode cannot pretend
to be a StableDiGraph: it uses CSR and persistent mmap stores.
GraphRead and GraphWrite are therefore the reusable contract. Algorithms
that truly require a concrete petgraph representation must gate that path and
provide a backend-appropriate alternative. This keeps large-graph storage
from distorting the default in-memory design.
Explicit indexes and planner fallbacks¶
Indexes cost memory and mutation work, so property/range/composite indexes are explicit rather than created for every field. The planner uses index and cardinality information when present and retains correct scan paths when it is not. Type and ID routing remain foundational because the data model makes them high-value across workloads.
Result values are lazy where the shape permits it¶
The executor may return a lazy descriptor for eligible read projections. A
Python ResultView then materializes and caches cells row by row while keeping
the graph snapshot alive. More complex operators still materialize Rust rows
as required. The contract is bounded Python conversion and stable result
ownership, not universal operator streaming.
Per-query R-tree for spatial joins¶
The spatial join builds an R-tree from the current query’s indexed side. This
provides subquadratic candidate discovery without maintaining another graph
index through every mutation or persistence transition. Precise geo
predicates still decide the result after envelope filtering.
The tradeoff is rebuild cost per eligible query. A persistent spatial index would be a different design with mutation, format, and backend implications; it should be justified by measurements rather than assumed to be free.
Two persistence products, two lifecycles¶
Memory and mapped graphs save portable .kgl snapshots. The RGF v4 container
uses JSON metadata and independently compressed zstd sections so topology,
columns, embeddings, and timeseries data can evolve with explicit version
checks.
Disk graphs are directories of immutable generations. A writer completes a
new generation before atomically replacing CURRENT; readers keep using the
generation they opened. This provides crash-safe publication and stable
reader snapshots, not multi-writer transactions or replication.
Persisted user data outlives the code that wrote it. Consequently, explicit read compatibility or a clear hard-break/rebuild error is required even though obsolete source APIs are removed rather than deprecated.
In-memory performance wins conflicts¶
Mapped and disk modes exist for exploration beyond RAM, but the in-memory engine is the core product. Shared planner or executor changes must be checked on small in-memory graphs. Disk workarounds belong behind storage-mode or scale gates when a general solution would slow the default path.