Core Concepts¶
Nodes, Relationships, and Selections¶
Nodes have three built-in fields — type (label), title (display name), id (unique within type) — plus arbitrary properties. Each node has exactly one type.
Relationships connect two nodes with a type (e.g., :KNOWS) and optional properties. The Cypher API calls them “relationships”; the fluent API calls them “connections” — same thing.
Selections (fluent API) are lightweight views — a set of node indices that flow through chained operations like select().where().traverse(). They don’t copy data.
Atomicity. Each cypher() call is atomic — if any clause fails, the graph remains unchanged. For multi-statement atomicity, use graph.begin() transactions. Durability only via explicit save() (which is itself atomic + fsync — no torn file on a crash).
Single-owner. A KnowledgeGraph is owned by one thread at a time: concurrent reads are fine, but a read overlapping a mutation on the same instance raises a clear RuntimeError. For multi-threaded use: give each worker its own copy(), share a read-only graph.freeze() snapshot for lock-free reads, or — when threads need shared reads and writes — graph.session() (lock-free reads + serialized composing writes). See Concurrency.
How It Works¶
KGLite stores nodes and relationships in a Rust graph structure (petgraph). Python only sees lightweight handles — data converts to Python objects on access, not on query.
Cypher queries parse, optimize, and execute entirely in Rust, then return a
ResultView(lazy — rows convert to Python dicts only when accessed)Fluent API chains build a selection (a set of node indices) — no data is copied until you call
collect(),to_df(), etc.Persistence is via
save()/load()binary snapshots — there is no WAL or auto-save
Storage Modes¶
KGLite has three storage backends. The Python API is identical across all three; the trade-off is in-memory speed vs. on-disk scalability.
Mode |
Construct |
Where data lives |
Best for |
|---|---|---|---|
Default (in-memory) |
|
Heap |
Small / medium graphs (<5 M nodes), prototyping, fastest queries |
Mapped |
|
mmap-backed columnar files |
RAM-friendly as the graph grows; same query speed as in-memory for typed lookups (O(log N) property index) |
Disk |
|
mmap CSR + segments |
100 M+ nodes (Wikidata-scale); kept lazy-loaded so the OS pages in only what queries touch |
Save/load works for all three. For disk mode, save() consolidates
segment artifacts into a top-level disk_graph_meta.json so
kglite.load(path) can reconstitute the graph.
When optimizing, in-memory wins. Disk and mapped exist for
data that’s too big to keep on the heap; they’re not “faster”
backends. For Wikidata-scale workflows, see the load_ntriples
section of Data Loading.
Choosing a storage mode¶
Start in-memory — it is the core product and the fastest path for
everything that fits in RAM. Reach for mapped only when the graph
stops fitting comfortably on the heap, and for disk only at the
Wikidata scale where you want the OS to page data in lazily.
If your graph is… |
…and you want |
Use |
|---|---|---|
Up to a few million nodes |
Lowest latency, simplest setup |
memory (default) |
Large but you still query it interactively |
RAM headroom without giving up typed-lookup speed |
mapped |
100 M+ nodes / won’t fit in RAM |
Lazy, page-on-demand access to a huge graph |
disk |
When in doubt, stay in-memory; switch only once you hit a real RAM ceiling. Both larger-than-RAM modes keep the identical Python and Cypher API, so moving up is a one-line constructor change.
Return Types¶
All node-related methods use a consistent key order: type, title, id, then other properties.
Cypher¶
Query type |
Returns |
|---|---|
Read ( |
|
Read with |
|
Mutation ( |
|
|
|
Spatial return types: point() values are returned as {'latitude': float, 'longitude': float} dicts.
ResultView¶
ResultView is a lazy result container returned by cypher(), centrality methods, collect(), and sample(). Data stays in Rust and is only converted to Python objects when you access it — making cypher() calls fast even for large result sets.
result = graph.cypher("MATCH (n:Person) RETURN n.name, n.age ORDER BY n.age")
len(result) # row count (O(1), no conversion)
result[0] # single row as dict (converts that row only)
result[-1] # negative indexing works
for row in result: # iterate rows as dicts (one at a time)
print(row)
result.head() # first 5 rows → new ResultView
result.head(3) # first 3 rows → new ResultView
result.tail(2) # last 2 rows → new ResultView
result.to_list() # all rows as list[dict] (full conversion)
result.to_df() # pandas DataFrame (full conversion)
result.columns # column names: ['n.name', 'n.age']
result.stats # mutation stats (None for read queries)
Because ResultView supports iteration and indexing, it works anywhere you’d use a list of dicts — existing code that iterates over cypher() results continues to work unchanged.
Node dicts¶
Every method that returns node data uses the same dict shape:
{'type': 'Person', 'title': 'Alice', 'id': 1, 'age': 28, 'city': 'Oslo'}
# ^^^^ ^^^^^ ^^^ ^^^ other properties
Retrieval methods (cheapest to most expensive)¶
Method |
Returns |
Notes |
|---|---|---|
|
|
No materialization |
|
|
Raw graph indices |
|
|
Flat list of IDs |
|
|
Flat list (see below) |
|
|
Flat list (see below) |
|
|
Full node dicts |
|
|
Columns: |
|
|
O(1) hash lookup |
Flat vs. grouped results¶
titles(), get_properties(), and collect() automatically flatten when there is only one parent group (the common case). After a traversal with multiple parent groups, they return grouped dicts instead:
# No traversal (single group) → flat list
graph.select('Person').titles()
# ['Alice', 'Bob', 'Charlie']
# After traversal (multiple groups) → grouped dict
graph.select('Person').traverse('KNOWS').titles()
# {'Alice': ['Bob'], 'Bob': ['Charlie']}
# Override with flatten_single_parent=False to always get grouped
graph.select('Person').titles(flatten_single_parent=False)
# {'Root': ['Alice', 'Bob', 'Charlie']}
Centrality methods¶
All centrality methods (pagerank, betweenness_centrality, closeness_centrality, degree_centrality) return:
Mode |
Returns |
|---|---|
Default |
|
|
|
|
|