kglite.okf¶
Type stubs for kglite.okf — Open Knowledge Format bundle ingestion.
Classes¶
A high-performance knowledge graph with typed nodes, connections, and |
Functions¶
|
Build a |
|
Read a concept's markdown body on demand (frontmatter stripped). |
Package Contents¶
- class kglite.okf.KnowledgeGraph¶
A high-performance knowledge graph with typed nodes, connections, and a fluent query API backed by Rust.
Single-owner / threading. A
KnowledgeGraphis single-owner: it is not safe to share one instance across threads while any thread mutates it (doing so raises a clearRuntimeError). For concurrent access, don’t share the graph — take a thread-safe handle off it:session()(shared reads + serialized writes, plusSession.cursor()for per-thread fluent chains) orfreeze()(a lock-free read-only snapshot). Seedocs/concepts/concurrency.md.Create an empty KnowledgeGraph.
- Parameters:
storage – Storage mode.
None(default) uses heap-resident storage, optimal for small-to-medium graphs."mapped"uses mmap-backed columnar storage from the start, designed for large graphs that may approach or exceed available RAM."disk"uses fully disk-backed storage for very large graphs (100M+ nodes). Requirespath.path – Directory path for disk-mode storage. Required when
storage="disk". The directory IS the graph — data is written directly to disk via mmap. Load withkglite.load(path).
- add_connections(data: pandas.DataFrame | None, connection_type: str, source_type: str, source_id_field: str, target_type: str, target_id_field: str, source_title_field: str | None = None, target_title_field: str | None = None, columns: list[str] | None = None, skip_columns: list[str] | None = None, conflict_handling: str | None = None, column_types: dict[str, str] | None = None, query: str | None = None, extra_properties: dict[str, Any] | None = None) dict[str, Any]¶
Add connections (edges) between existing nodes.
Two modes — supply either
data(a pandas DataFrame) orquery(a Cypher string whose RETURN columns provide source/target IDs).Example (from DataFrame):
graph.add_connections(df, 'KNOWS', 'Person', 'src_id', 'Person', 'tgt_id')
Example (from Cypher query):
graph.add_connections( None, 'ENCLOSES', 'Play', 'play_id', 'StructuralElement', 'struct_id', query=""" MATCH (p:Play), (s:StructuralElement) WHERE contains(p, s) RETURN DISTINCT p.id AS play_id, s.id AS struct_id """, )
Example (query with extra properties):
graph.add_connections( None, 'HC_IN_FORMATION', 'Discovery', 'src', 'Stratigraphy', 'tgt', query='MATCH ... RETURN d.id AS src, s.id AS tgt', extra_properties={'hc_rank': 1}, )
- Parameters:
data – DataFrame containing edge data, or
Nonewhen usingquery.connection_type – Label for this edge type (e.g.
'KNOWS'). Any characters are accepted here — including hyphens/dots/spaces ('supports-claim'). Such a type only needs backtick-quoting when named inside a Cypher query ([r:`supports-claim`]).source_type – Node type of source nodes.
source_id_field – Column with source node IDs (must appear in DataFrame or query RETURN).
target_type – Node type of target nodes.
target_id_field – Column with target node IDs (must appear in DataFrame or query RETURN).
source_title_field – Optional title column for source nodes.
target_title_field – Optional title column for target nodes.
columns – Whitelist of property columns to include (data mode only). Unlike
add_nodes(), edge ingestion keeps only id/title columns unless this whitelist is given — so any other edge property column is dropped whencolumnsis omitted. That drop now emits aUserWarningnaming the dropped columns; passcolumns=[...]to keep them.skip_columns – Columns to exclude (data mode only).
conflict_handling –
'update'(default),'replace','skip','preserve', or'sum'.'sum'adds numeric edge properties (Int64+Int64, Float64+Float64; mixed promotes to Float64). Non-numeric properties overwrite like'update'.column_types – Override column dtypes (data mode only).
query – Cypher query string (alternative to
data). Must be a read-only query whose RETURN clause includes columns matchingsource_id_fieldandtarget_id_field.extra_properties – Dict of static properties to add to every edge created from the query results (query mode only).
- Returns:
Operation report dict with
connections_created,connections_skipped, etc.
- add_connections_bulk(connections: list[dict[str, Any]]) dict[str, int]¶
Add multiple connection types at once.
Each dict must contain
source_type,target_type,connection_name, anddata(DataFrame withsource_id/target_idcolumns).- Returns:
Mapping of
connection_nameto count of connections created.
- add_connections_from_source(connections: list[dict[str, Any]]) dict[str, int]¶
Add connections, auto-filtering to types already loaded in the graph.
Same spec format as
add_connections_bulk(), but silently skips connection specs whose source or target type is not in the graph.- Returns:
Mapping of
connection_nameto count of connections created.
- add_embeddings(node_type: str, text_column: str, embeddings: dict[Any, list[float]], metric: str | None = None) dict[str, Any]¶
Add or update embeddings without discarding the existing store.
Differs from
set_embeddings()(which replaces the store) by upserting entries into an existing(node_type, "{text_column}_emb")store. If no store exists yet, behaves likeset_embeddings— the first call creates one; subsequent calls extend it.Use this for incremental ingest workflows where multiple
add_nodes+ embedding batches need to coexist without a read-merge-write cycle through the user’s process.- Parameters:
node_type – The node type (e.g.
'Article').text_column – Source text column name (e.g.
'summary').embeddings – Dict mapping node IDs to embedding vectors.
metric – Only used when the store doesn’t exist yet. Ignored otherwise (the existing store’s metric is preserved).
- Returns:
Dict with
embeddings_stored(total in store after the upsert),dimension,skipped(unknown ids), andstore_created(Trueiff this call created the store).
- add_label(node_type: str, ids: list[Any], label: str) dict[str, int]¶
Add a secondary label to a batch of nodes by id.
Secondary labels are queryable via Cypher (
MATCH (n:Label)) and surfaced bylabels(n). The primary type (set viaadd_nodes(node_type=...)) is immutable — to retype a node, useSET n.type = 'NewType'.- Parameters:
node_type – Primary type of the nodes.
ids – Node ids (the
unique_id_fieldvalues).label – Secondary label to add.
- Returns:
Dict with
labelled(newly added) andskipped(unknown ids, or label already present).
Example:
graph.add_label('Agent', ['ag_001', 'ag_002'], 'Reviewer') graph.cypher('MATCH (a:Reviewer) RETURN a.id').to_list()
- add_nodes(data: pandas.DataFrame, node_type: str, unique_id_field: str, node_title_field: str | None = None, columns: list[str] | None = None, conflict_handling: str | None = None, skip_columns: list[str] | None = None, column_types: dict[str, str] | None = None, timeseries: dict[str, Any] | None = None, nullable_int_downcast: bool = False, labels: list[str] | None = None, managed_reload: bool = False) dict[str, Any]¶
Add nodes from a DataFrame.
String and integer IDs are auto-detected from the DataFrame dtype. Non-contiguous DataFrame indexes (e.g. from filtering) are handled automatically.
When
timeseriesis provided, the DataFrame may contain multiple rows per unique ID (one per time step). Rows are deduplicated automatically — the first occurrence per ID provides static node properties, and all rows contribute to the timeseries channels.- Parameters:
data – DataFrame containing node data.
node_type – Label for this set of nodes (e.g.
'Person').unique_id_field – Column used as unique identifier.
node_title_field – Column used as display title. Defaults to
unique_id_field.columns – Whitelist of columns to include.
None= all.conflict_handling –
'update'(default),'replace','skip','preserve', or'sum'.'sum'acts as'update'for nodes.Partial-update guarantee:
'update'writes only the columns present in this call’s data — properties of an existing node that are not in the incoming columns are left untouched. This is a stable contract: it lets a batch reload re-assert a subset of fields (e.g. identity + links) without clobbering fields another writer owns (e.g. an agent’sstatus/notes).'replace'instead reconciles the node to the incoming record — it overwrites the whole node, so a property absent from the new data is dropped (set to null). Use'replace'(not'update') when the source data is the single source of truth and you want field deletions to propagate on rebuild.skip_columns – Columns to exclude.
column_types – Override column dtypes, e.g.
{'col': 'string'}. Supported:'string','integer','float','datetime','timestamp','uniqueid','list','map'. A column of Python lists/tuples is auto-detected as a native'list'property (stored structurally, not stringified), so'y' IN n.aliasestests membership andUNWIND n.aliasesyields the elements; pass'list'explicitly to force it. A column of Python dicts is auto-detected as a native'map'property (n.meta['k']/n.meta.kread back the value) rather than being stringified. Adatetime64column keeps its full time-of-day when any value has a nonzero time (stored as aTimestamp); a pure-midnight column stays date-only ('datetime'). Pass'timestamp'to force full date+time,'datetime'to force date-only. Also supports spatial types:'location.lat','location.lon','geometry','point.<name>.lat','point.<name>.lon','shape.<name>'.nullable_int_downcast – When
True, Float64 columns whose non-null values are all integer-valued (e.g.pd.NA-bearing ints that pandas auto-promoted to float64) are silently downcast to Int64. DefaultFalse— explicit opt-in protects existing callers.managed_reload – When
True, this call is part of a managed reload (a batch writer rebuilding from source). Ifnode_typedeclareslayer='runtime'in the schema (an agent-owned type), the write is skipped as a no-op and reported — so a research rebuild can never clobber agent-owned nodes. Undeclared orlayer='managed'types are written normally. Pairs with thelayerdeclaration indefine_schemaandconflict_handling.timeseries –
Inline timeseries configuration dict with keys:
time(required): column name containing date strings ('yyyy-mm','yyyy-mm-dd','yyyy-mm-dd hh:mm'), or a dict mappingyear/month/day/hour/minuteto column names (e.g.{'year': 'ar', 'month': 'maned'}).channels(required): list of column names for timeseries data (e.g.['oil', 'gas', 'condensate']).resolution(optional):'year','month','day','hour', or'minute'. Auto-detected from time format if omitted.units(optional): dict mapping channel names to unit strings (e.g.{'oil': 'MSm3'}).
labels – Optional secondary labels to apply to every node in the batch.
add_nodes(df, 'Agent', 'id', 'name', labels=['Reviewer'])createsAgent-typed nodes that also wear theReviewerlabel, queryable viaMATCH (a:Reviewer)orMATCH (a:Agent:Reviewer). For per-row labels, calladd_label()after.
- Returns:
Operation report dict with keys
nodes_created,nodes_updated,nodes_skipped,processing_time_ms,has_errors, and optionallyerrorswith skip reasons.
Example:
graph.add_nodes(df, 'Production', 'field_id', 'field_name', timeseries={ 'time': 'date', 'channels': ['oil', 'gas', 'condensate', 'oe'], })
- add_nodes_bulk(nodes: list[dict[str, Any]]) dict[str, int]¶
Add multiple node types at once.
Each dict in nodes must contain
node_type,unique_id_field,node_title_field, anddata(a DataFrame).- Returns:
Mapping of
node_typeto count of nodes added.
- add_properties(properties: dict[str, list[str] | dict[str, str]], keep_selection: bool | None = None) KnowledgeGraph¶
Enrich selected nodes with properties from ancestor nodes in the traversal chain.
Copies, renames, aggregates, or computes spatial properties from nodes at other levels of the selection hierarchy onto the current leaf nodes.
- Parameters:
properties –
Dict mapping source node type → property spec:
{'B': ['name', 'status']}— copy listed properties as-is{'B': []}— copy all properties from B{'B': {'new_name': 'old_name'}}— copy with rename{'B': {'avg_depth': 'mean(depth)'}}— aggregate functions:count(*),sum(prop),mean(prop),min(prop),max(prop),std(prop),collect(prop){'B': {'dist': 'distance'}}— spatial compute:distance,area,perimeter,centroid_lat,centroid_lon
keep_selection – Preserve current selection. Default
True.
- Returns:
A new KnowledgeGraph with the properties added to selected nodes.
Examples:
# Copy structure name onto wells graph.select('Structure').compare('Well', 'contains') \ .add_properties({'Structure': ['name', 'status']}) # Rename properties graph.select('Structure').compare('Well', 'contains') \ .add_properties({'Structure': {'struct_name': 'name'}}) # Aggregate with Agg helpers (discoverable via autocomplete) from kglite import Agg, Spatial graph.select('Structure').compare('Well', 'contains') \ .add_properties({'Well': { 'well_count': Agg.count(), 'avg_depth': Agg.mean('depth'), }}) # Spatial compute with Spatial helpers graph.select('Structure').compare('Well', 'contains') \ .add_properties({'Structure': { 'dist_to_center': Spatial.distance(), 'parent_area': Spatial.area(), }})
- add_timeseries(node_type: str, *, data: Any, fk: str, time_key: list[str], channels: dict[str, str] | list[str], resolution: str | None = None, units: dict[str, str] | None = None) dict[str, Any]¶
Bulk-load timeseries data from a DataFrame.
Groups rows by
fk, sorts bytime_key, and attaches the resulting timeseries to matching nodes (found by node ID).Time keys are combined into NaiveDate internally: - Single column: parsed as date strings (
'2020-06') - Multiple columns: combined as year + month [+ day] → NaiveDate- Parameters:
node_type – Target node type.
data – Source DataFrame.
fk – Foreign key column in
datalinking to node IDs.time_key – Column(s) for time keys. If single column, values are parsed as date strings. If multiple, combined as year + month [+ day].
channels – Either a list of column names (used as channel names) or a dict mapping
{channel_name: column_name}.resolution – Time granularity (
'year','month','day'). Auto-detected from time_key count if not specified.units – Optional channel→unit map, merged into config.
- Returns:
{'nodes_loaded': N, 'total_records': M, 'total_rows': R}.- Return type:
Summary
- add_ts_channel(node_id: Any, channel_name: str, values: list[float]) None¶
Add a timeseries channel to a node.
The node must already have a time index set (via
set_time_indexoradd_timeseries). The values length must match the time index length. Usefloat('nan')for missing values.- Parameters:
node_id – The node’s unique ID.
channel_name – Channel name (e.g.
'oil','temperature').values – Float values aligned with the time index.
- all_paths(source_type: str, source_id: Any, target_type: str, target_id: Any, max_hops: int | None = None, max_results: int | None = None, connection_types: list[str] | None = None, via_types: list[str] | None = None, timeout_ms: int | None = None) list[dict[str, Any]]¶
Find all paths between two nodes.
- Parameters:
source_type – Source node type.
source_id – Source node ID.
target_type – Target node type.
target_id – Target node ID.
max_hops – Maximum path length. Default
5.max_results – Stop after finding this many paths. Default unlimited. Use to prevent OOM on dense graphs.
connection_types – Only traverse edges of these types. Default all.
via_types – Only traverse through nodes of these types. Default all.
timeout_ms – Abort after this many milliseconds, returning partial results.
- Returns:
List of path dicts, each with
path,connections,length.
- are_connected(source_type: str, source_id: Any, target_type: str, target_id: Any) bool¶
Check if two nodes are connected (directly or indirectly).
- begin(timeout_ms: int | None = None) Transaction¶
Begin a read-write transaction — returns a Transaction with a working copy.
Creates a snapshot (deep-clone) of the current graph state. All mutations within the transaction are isolated until
commit()is called. If rolled back (or dropped without committing), no changes are applied.Uses optimistic concurrency control:
commit()will raiseRuntimeErrorif the graph was modified by another transaction sincebegin()was called.- Parameters:
timeout_ms – Optional transaction-level timeout in milliseconds. If set, all operations after the deadline raise
TimeoutError.
Can be used as a context manager:
with graph.begin() as tx: tx.cypher("CREATE (n:Person {name: 'Alice', age: 30})") tx.cypher("CREATE (n:Person {name: 'Bob', age: 25})") # auto-commits on success, auto-rollbacks on exception
- begin_read(timeout_ms: int | None = None) Transaction¶
Begin a read-only transaction — O(1) cost, zero memory overhead.
Returns a Transaction backed by an Arc reference to the current graph state. Mutations (CREATE, SET, DELETE, REMOVE, MERGE) are rejected.
Ideal for concurrent read-heavy workloads (e.g. MCP server agents) where you want a consistent snapshot without the cost of a full clone.
- Parameters:
timeout_ms – Optional transaction-level timeout in milliseconds.
Can be used as a context manager:
with graph.begin_read() as tx: result = tx.cypher("MATCH (n:Person) RETURN n.name") # auto-closes on exit (no commit needed)
- betweenness_centrality(normalized: bool | None = None, sample_size: int | None = None, connection_types: str | list[str] | None = None, top_k: int | None = None, as_dict: bool | None = None, timeout_ms: int | None = None, to_df: bool | None = None) ResultView | dict[Any, float] | pandas.DataFrame¶
Calculate betweenness centrality.
- Parameters:
normalized – Normalise scores to
[0, 1]. DefaultTrue.sample_size – Sample source nodes for faster computation on large graphs.
connection_types – Only traverse these relationship types (str or list).
top_k – Return only the top K nodes.
as_dict – Return
{id: score}dict instead of list of dicts.timeout_ms – Abort after this many milliseconds, returning partial results.
to_df – Return a pandas DataFrame with columns
type,title,id,score.
- Returns:
List of dicts with
type,title,id,score, sorted by score descending. Or a DataFrame ifto_df=True.
- bounds(lat_field: str | None = None, lon_field: str | None = None, as_shapely: bool = False) dict[str, float] | Any | None¶
Get geographic bounds of selected nodes.
- Parameters:
lat_field – Latitude property name. Default
'latitude'.lon_field – Longitude property name. Default
'longitude'.as_shapely – If
True, return ashapely.geometry.Polygon(box) instead of a dict.
- Returns:
Dict with
min_lat,max_lat,min_lon,max_lon, or a shapely box polygon whenas_shapely=True, orNoneif no valid coordinates found.
- bug_report(query: str, result: str, expected: str, description: str, path: str | None = None) str¶
File a Cypher bug report to
reported_bugs.md.Appends a timestamped, version-tagged report to the top of the file (creating it if needed). All inputs are sanitised against code injection (HTML tags,
javascript:URIs, triple-backtick breakout).- Parameters:
query – The Cypher query that triggered the bug.
result – The actual result you got.
expected – The result you expected.
description – Free-text explanation.
path – Optional file path (default:
reported_bugs.mdin cwd).
- Returns:
Confirmation message with the file path.
- Raises:
IOError – If the file cannot be written.
- build_id_indices(node_types: list[str] | None = None) None¶
Pre-build ID lookup indices for fast
node()calls.- Parameters:
node_types – Types to index.
Noneindexes all types.
- build_vector_index(node_type: str, text_column: str, m: int | None = None, ef_construction: int | None = None, ef_search: int | None = None, metric: str | None = None) dict[str, Any]¶
Build an HNSW approximate-nearest-neighbour index over an embedding store so vector search scales sub-linearly on large stores.
Opt-in, like
create_index(): without it, vector search is an exact brute-force scan. Once built,vector_search()/search_text()auto-use the index for whole-corpus queries on large stores; passexact=Trueto force an exact scan. The index is dropped automatically whenever the store’s vectors change (add_embeddings/embed_texts) or slots are remapped (vacuum) — rebuild it after.- Parameters:
node_type – The node type (e.g.
'Article').text_column – Source column name (e.g.
'summary'; the store is'{text_column}_emb').m – Max neighbours per node on upper layers (default 16). Higher → better recall, larger index.
ef_construction – Build-time search width (default 200). Higher → better graph, slower build.
ef_search – Default query-time search width (default 64). Higher → better recall, slower query.
metric –
'cosine'(default),'dot_product', or'euclidean'.'poincare'is unsupported (stays exact). If omitted, uses the store’s metric, else'cosine'.
- Returns:
{'indexed': int, 'metric': str, 'm': int}.- Return type:
dict
- Raises:
ValueError – if the store doesn’t exist or the metric is unsupported.
Example:
g.embed_texts("Article", "summary") g.build_vector_index("Article", "summary") # opt in hits = g.select("Article").search_text("summary", "AI", top_k=10)
- calculate(expression: str, level_index: int | None = None, store_as: str | None = None, keep_selection: bool | None = None, aggregate_connections: bool | None = None) Any¶
Evaluate a mathematical expression on selected nodes.
Supports property references, arithmetic operators, and aggregate functions (
sum,mean,std,min,max,count).- Parameters:
expression – Expression string, e.g.
'price * quantity'or'mean(age)'.level_index – Target level in the hierarchy.
store_as – If set, stores results as this property on nodes.
keep_selection – Preserve selection after store. Default
False.aggregate_connections – Aggregate over connected nodes.
- Returns:
Computation results, or a KnowledgeGraph if
store_asis set.
- centroid(lat_field: str | None = None, lon_field: str | None = None, as_shapely: bool = False) dict[str, float] | Any | None¶
Get the geographic centroid (average lat/lon) of selected nodes.
- Parameters:
lat_field – Latitude property name. Default
'latitude'.lon_field – Longitude property name. Default
'longitude'.as_shapely – If
True, return ashapely.geometry.Pointinstead of a dict.
- Returns:
Dict with
latitudeandlongitude, or a shapely Point whenas_shapely=True, orNone.
- clear() None¶
Clear the current selection (resets to empty).
- clear_schema() KnowledgeGraph¶
Remove the schema definition from the graph.
- close() None¶
Persist the graph to its remembered origin path (the file it was opened from via
kglite.open()/kglite.load(), or last saved to). No-op if the graph has no associated path. The graph stays usable afterclose().
- closeness_centrality(normalized: bool | None = None, sample_size: int | None = None, connection_types: str | list[str] | None = None, top_k: int | None = None, as_dict: bool | None = None, timeout_ms: int | None = None, to_df: bool | None = None) ResultView | dict[Any, float] | pandas.DataFrame¶
Calculate closeness centrality.
- Parameters:
normalized – Adjust for disconnected components. Default
True.sample_size – Approximate by sampling N source nodes (faster for large graphs). If
None, uses all nodes.connection_types – Filter to specific relationship types.
top_k – Return only the top K nodes.
as_dict – Return
{id: score}dict instead of list of dicts.timeout_ms – Abort after this many milliseconds, returning partial results.
to_df – Return a pandas DataFrame with columns
type,title,id,score.
- Returns:
List of dicts with
type,title,id,score.
- collect(limit: int | None = None) ResultView¶
Materialise selected nodes as a flat
ResultView.For grouped output by parent type, use
collect_grouped()instead.- Parameters:
limit – Maximum number of nodes to return.
- Returns:
A
ResultViewcontainingid,title,type, and all stored properties for each selected node.
- collect_children(property: str | None = None, where: dict[str, Any] | None = None, sort: str | list[tuple[str, bool]] | None = None, limit: int | None = None, store_as: str | None = None, max_length: int | None = None, keep_selection: bool | None = None) Any¶
Collect child-node property values into comma-separated lists.
- Parameters:
property – Child property to collect. Default
'title'.where – Filter conditions for children.
sort – Sort children.
limit – Limit children per parent.
store_as – If set, stores the list as this property on parent nodes.
max_length – Max string length when storing.
keep_selection – Preserve selection after store. Default
False.
- Returns:
Dict of
{parent_title: 'val1, val2, ...'}or a KnowledgeGraph ifstore_asis set.
- collect_grouped(group_by: str, *, parent_info: bool = False, flatten_single_parent: bool = True, limit: int | None = None) dict[str, Any]¶
Materialise selected nodes grouped by a parent type in the traversal hierarchy.
- Parameters:
group_by – Parent node type to group by (must exist in the traversal chain).
parent_info – Include parent metadata (
type,id,title) in each group. DefaultFalse.flatten_single_parent – If only one parent group exists, return a flat list instead of a single-key dict. Default
True.limit – Maximum number of nodes to return.
- Returns:
Dict mapping parent title → list of node dicts. If
flatten_single_parentisTrueand there is only one parent, returns a flat list.
Examples:
# Group wells by their parent field graph.select('Field').traverse('HAS_WELL') \ .collect_grouped('Field') # → {'TROLL': [...], 'EKOFISK': [...]} # Include parent metadata graph.select('Field').traverse('HAS_WELL') \ .collect_grouped('Field', parent_info=True)
- compact() int¶
Compact a disk-mode graph: merge overflow edges back into CSR arrays.
Overflow edges accumulate when edges are added after the initial CSR build (e.g., after loading a graph and adding new connections). Compaction rebuilds the CSR to include all overflow edges, restoring optimal query performance.
- Returns:
Number of overflow edges that were merged. Returns 0 if there are no overflow edges or the graph is not in disk mode.
- compare(target_type: str | list[str], method: str | dict[str, Any], *, filter: dict[str, Any] | None = None, sort: str | list[tuple[str, bool]] | None = None, limit: int | None = None, level_index: int | None = None, new_level: bool | None = None) KnowledgeGraph¶
Compare selected nodes against a target type using spatial, semantic, or clustering methods.
- Parameters:
target_type – Node type to compare against (e.g.
'Well').method –
Comparison method — a string shorthand or a dict with settings:
Spatial methods:
'contains'— point-in-polygon or polygon containment'intersects'— polygon-polygon intersection{'type': 'distance', 'max_m': 5000}— geodesic distance
Semantic methods:
{'type': 'text_score', 'property': 'name'}— embedding similarity{'type': 'text_score', 'threshold': 0.7}— with threshold{'type': 'text_score', 'metric': 'poincare'}— Poincaré distance (hierarchical data)
Clustering methods:
{'type': 'cluster', 'k': 5}— K-means clustering{'type': 'cluster', 'algorithm': 'dbscan', 'eps': 0.5}— DBSCAN
Common dict keys:
resolve:'centroid','closest', or'geometry'max_m: Maximum distance in meters (distance method)threshold: Minimum similarity score (semantic methods)k: Number of clusters (K-means)features: Properties to cluster on
filter – Property conditions for target nodes.
sort – Sort results. Field name or
[(field, ascending)]list.limit – Max target nodes per source.
level_index – Source level in the hierarchy (advanced).
new_level – Add targets as new hierarchy level. Default
True.
- Returns:
A new KnowledgeGraph with comparison results selected.
Examples:
# Spatial containment: find wells inside structures graph.select('Structure').compare('Well', 'contains') # Distance: wells within 5km graph.select('Well').compare('Well', {'type': 'distance', 'max_m': 5000}) # Semantic similarity graph.select('Document').compare('Document', {'type': 'text_score', 'property': 'summary', 'threshold': 0.7}) # Clustering graph.select('Well').compare('Well', {'type': 'cluster', 'k': 5, 'features': ['latitude', 'longitude']})
- composite_index_stats(node_type: str, properties: list[str]) dict[str, Any] | None¶
Get statistics for a composite index. Returns
Noneif not found.
- connected_components(weak: bool | None = None, titles_only: bool | None = None) list[list[dict[str, Any]]]¶
Find connected components in the graph.
- Parameters:
weak – If
True(default), find weakly connected components. IfFalse, find strongly connected components.titles_only – If
True, return lists of node titles instead of full dicts.
- Returns:
List of components (largest first), each a list of node info dicts.
- connection_types() list[dict[str, Any]]¶
Return all connection types with counts and endpoint type sets.
- Returns:
List of dicts with
type,count,source_types,target_types.
- connections(indices: list[int] | None = None, parent_info: bool | None = None, include_node_properties: bool | None = None, flatten_single_parent: bool = True) dict[str, Any]¶
Get connections for selected nodes.
- Parameters:
indices – Specific node indices to query.
parent_info – Include parent info in output.
include_node_properties – Include properties of connected nodes. Default
True.flatten_single_parent – Flatten when only one parent. Default
True.
- Returns:
Nested dict
{title: {node_id, type, incoming, outgoing}}.
- contains_point(lat: float, lon: float, geometry_field: str | None = None) KnowledgeGraph¶
Filter nodes whose WKT polygon contains a point.
- Parameters:
lat – Query point latitude.
lon – Query point longitude.
geometry_field – WKT geometry property name. Default
'geometry'.
- Returns:
A new KnowledgeGraph with containing nodes.
- context(name: str, node_type: str | None = None, hops: int | None = None) dict[str, Any]¶
Get the full neighborhood of a code entity.
Returns the node’s properties and all related entities grouped by relationship type. If the name is ambiguous, returns the matches so you can refine with a qualified name.
- Parameters:
name – Entity name or qualified name.
node_type – Optional node type hint.
hops – Max traversal depth (default 1).
- Returns:
Dict with
"node"(properties),"defined_in"(file path), and relationship groups (e.g."HAS_METHOD","CALLS","called_by").
- copy() KnowledgeGraph¶
Create an independent deep copy of this graph.
Returns a new
KnowledgeGraphthat shares no mutable state with the original. Useful for running mutations without affecting the source graph.
- copy_embeddings_from(other: KnowledgeGraph) dict[str, int]¶
Copy every embedding store from
otherinto this graph, by node id.The one-call answer to the “rebuild a fresh graph from a source of truth on each load, keep the vectors” workflow: build the new graph, then
new.copy_embeddings_from(old). Vectors land on the new nodes that share an id, carrying each store’s dimension, metric, model id, and per-node text hashes — so a followingembed_texts(mode='changed')re-embeds only genuinely new/changed text. Vectors whose id has no matching node here are skipped (counted). Replaces the manualembeddings()→add_embeddings()→embed_texts()carry.- Returns:
Dict with
stores_copied,vectors_copied,vectors_skipped.
Example:
new = build_graph_from_source() # fresh, no vectors new.copy_embeddings_from(old) # carry vectors by id new.embed_texts("Doc", "summary", mode="changed") # fill only the new/changed
- count(level_index: int | None = None, group_by_parent: bool | None = None, store_as: str | None = None, keep_selection: bool | None = None, group_by: str | None = None) Any¶
Count nodes, optionally grouped by parent or by a property.
- Parameters:
level_index – Target level in the hierarchy.
group_by_parent – Group counts by parent node.
store_as – Store count as a property on parent nodes.
keep_selection – Preserve selection after store. Default
False.group_by – Group counts by this property instead of by parent. Returns
{group_value: count}.
- Returns:
An integer count, grouped counts, or a KnowledgeGraph if
store_asis set.
- create_composite_index(node_type: str, properties: list[str]) dict[str, Any]¶
Create a composite index on multiple properties.
- Parameters:
node_type – Node type to index.
properties – List of property names for the composite key.
- Returns:
Dict with
type,properties,unique_combinations.
- create_connections(connection_type: str, keep_selection: bool | None = None, conflict_handling: str | None = None, properties: dict[str, list[str]] | None = None, source_type: str | None = None, target_type: str | None = None) KnowledgeGraph¶
Create connections from the traversal hierarchy.
By default, creates edges from the top-level ancestor (first traversal level) to the leaf nodes (last level). Use
source_type/target_typeto choose different levels.- Parameters:
connection_type – Label for the new edges (e.g.
'A_TO_C').keep_selection – Preserve selection. Default
False.conflict_handling –
'update'(default),'replace','skip','preserve', or'sum'.properties – Copy properties from intermediate nodes onto the new edges. Dict mapping node type to property names:
{'TypeB': ['score', 'weight']}. An empty list copies all properties from that type.source_type – Node type to use as source (default: first level).
target_type – Node type to use as target (default: last level).
- Returns:
A new KnowledgeGraph with the connections added.
Example:
# After traversal A → B → C, create direct A → C edges # with B's 'score' property copied onto each edge graph.select('A') \ .traverse('REL_AB') \ .traverse('REL_BC') \ .create_connections('A_TO_C', properties={'B': ['score']})
- create_global_index(property: str) dict[str, Any]¶
Build a cross-type global index on property.
Unlike
create_index, which is keyed by(node_type, property), this indexes EVERY node whose value at property is a non-empty string — regardless of node type. Enables:MATCH (n {label: 'Norway'})— untyped Cypher lookups, routed through the global index in O(log N).graph.search(text)— top-k helper that returns the nodes whose label (or any indexed property) matches.
Disk-backed graphs only. On memory/mapped graphs this is a no-op that returns
unique_values=0— per-typecreate_indexalready covers the use case at in-memory scale.- Returns:
Dict with
property,unique_values(count of nodes indexed), andcreated.
- create_index(node_type: str, property: str) dict[str, Any]¶
Create an index on a property for O(1) equality filter lookups.
Indexes are automatically maintained by Cypher mutations (CREATE, SET, REMOVE, DELETE, MERGE).
Idempotent — re-creating an existing index rebuilds it without error;
createdis thenFalse. It isTrueonly when this call made a new index.- Parameters:
node_type – Node type to index.
property – Property name to index.
- Returns:
Dict with
node_type,property,unique_values,persistent, andcreated(Falseif the index already existed).
- create_range_index(node_type: str, property: str) dict[str, Any]¶
Create a range index (B-Tree) on a property for efficient range queries.
Enables fast
>,>=,<,<=, andBETWEENqueries infilter()calls.- Parameters:
node_type – Node type to index.
property – Property name to index.
- Returns:
Dict with
type,property,unique_values,created.
Example:
graph.create_range_index('Person', 'age') old = graph.select('Person').where({'age': {'>': 60}}).collect()
- cypher(query: str, *, to_df: bool = False, params: dict[str, Any] | None = None, timeout_ms: int | None = None, max_rows: int | None = None, streaming: bool = True, disable_optimizer: bool = False, disabled_passes: list[str] | None = None, write_scope: list[str] | None = None, git_sha: str | None = None, modified_by: str | None = None) ResultView | pandas.DataFrame | str¶
Execute a Cypher query.
Supports MATCH, WHERE, RETURN, ORDER BY, LIMIT, SKIP, WITH, OPTIONAL MATCH, UNWIND, UNION, CREATE, SET, DELETE, DETACH DELETE, REMOVE, MERGE (with ON CREATE SET / ON MATCH SET), HAVING, CASE expressions, WHERE EXISTS, shortestPath(), list comprehensions, CALL { … } read subqueries (uncorrelated + correlated; v1 excludes writes / UNION / unit subqueries in the body), CALL…YIELD (graph algorithms: pagerank, betweenness, degree, closeness, louvain, label_propagation, connected_components), parameters ($param),
!=operator, aggregation functions, window functions (row_number(),rank(),dense_rank()withOVER (PARTITION BY ... ORDER BY ...)), and date arithmetic (date + N,date - date,date_diff(d1, d2)).Mutation queries (CREATE, SET, DELETE, REMOVE, MERGE) store statistics on
graph.last_mutation_statswith keysnodes_created,relationships_created,properties_set,nodes_deleted,relationships_deleted,properties_removed.Each call is atomic: if any clause fails, the graph is unchanged. Property and composite indexes are automatically maintained.
FORMAT CSV: Append
FORMAT CSVto any query to get results as a CSV string instead of a ResultView. Good for large result transfers and token-efficient LLM consumption in MCP servers.Before execution, the query is validated against the graph schema (known node types, connection types, and properties). Unknown identifiers raise
ValueErrorwith aDid you mean '...'?suggestion — catches typos before any scan runs.- Parameters:
query – Cypher query string.
to_df – If
True, return a pandas DataFrame.params – Optional parameter dict for
$paramsubstitution.timeout_ms – Deadline in milliseconds. If omitted, uses
set_default_timeout()when set, otherwise the built-in default of 180_000 ms (3 min). Pass0to disable the deadline for this call. Independently of the deadline, a long-running read can be interrupted withCtrl-C(raisesKeyboardInterrupt) on POSIX — the query aborts promptly instead of waiting for the deadline.max_rows – Cap on intermediate rows; defaults to
set_default_max_rows().streaming – When
True(default), the executor absorbs compatible clause runs (currentlyWITH/RETURN(group, agg) [ORDER BY ... LIMIT k]) into a streaming pipeline that builds aggregate state inline and replaces full sort + truncate with a heap-pruned top-K (O(n log k) instead of O(n log n)). PassFalseto force the materialized executor — useful for debugging parity issues; behaviour should otherwise be identical.disable_optimizer – When
True, run the query with all optimizer passes skipped — schema validation still applies, but no predicate pushdown, fusion, reordering, or LIMIT-pushdown happens. Diagnostic / testing knob; normal use should leave thisFalse. Used by the differential test harness to assert optimized and naive executions produce identical results.disabled_passes – Skip a specific subset of optimizer passes by name. Names must come from
kglite.cypher_pass_names()— typos raiseValueError. Useful for bisecting which pass introduces a divergence.write_scope – Role-scoped write whitelist. When given, a
CREATE/SET/MERGEthat creates or mutates a node whose type is not in the list is rejected (integrity, not secrecy — e.g. a coding role may write["Plan", "Task"]but not research-ownedAlgorithmnodes). Creating an edge between already-existing (matched) nodes is allowed even when an endpoint’s type is out of scope — linking to a node does not mutate it — but creating a new out-of-scope endpoint node is still rejected.None(default) = unrestricted. Applies per-call; also onSession.execute()andTransaction.cypher.
- Returns:
ResultView by default, DataFrame when
to_df=True, or CSV string when the query ends withFORMAT CSV.- Raises:
KeyboardInterrupt – If a long-running read is interrupted with
Ctrl-C(POSIX only). The graph is left unchanged.kglite.CypherSyntaxError / kglite.SchemaError / ... – Typed
kglite.KgErrorsubclasses for query / schema faults.
Example:
rows = graph.cypher(''' MATCH (p:Person)-[:KNOWS]->(f:Person) WHERE p.age > $min_age RETURN p.name, count(f) AS friends ORDER BY friends DESC LIMIT 10 ''', params={'min_age': 25}) for row in rows: print(row['name'], row['friends']) # As DataFrame df = graph.cypher('MATCH (n:Person) RETURN n.name, n.age', to_df=True) # As CSV string (good for large data transfers) csv = graph.cypher('MATCH (n:Person) RETURN n.name, n.age FORMAT CSV') # CREATE nodes and edges graph.cypher("CREATE (n:Person {name: 'Alice', age: 30})") print(graph.last_mutation_stats['nodes_created']) # 1 # SET properties graph.cypher(''' MATCH (n:Person) WHERE n.name = 'Alice' SET n.city = 'Oslo', n.age = 31 ''') # Semantic search with text_score (requires set_embedder + embed_texts) results = graph.cypher(''' MATCH (n:Article) RETURN n.title, text_score(n, 'summary', 'machine learning') AS score ORDER BY score DESC LIMIT 10 ''', to_df=True) # text_score with parameter graph.cypher(''' MATCH (n:Article) WHERE text_score(n, 'summary', $query) > 0.8 RETURN n.title ''', params={'query': 'artificial intelligence'}) # CALL graph algorithms top = graph.cypher(''' CALL pagerank() YIELD node, score RETURN node.title, score ORDER BY score DESC LIMIT 10 ''') # Community detection graph.cypher(''' CALL louvain() YIELD node, community RETURN community, count(*) AS size ORDER BY size DESC ''')
- date(date_str: str | None = None, end_str: str | None = None) KnowledgeGraph¶
Set the temporal context for auto-filtering.
Returns a new KnowledgeGraph. All subsequent
select()andtraverse()calls on the returned graph use this context for temporal filtering.- Modes:
date('2013')— point-in-time (valid at 2013-01-01).date('2010', '2015')— range: include everything valid at any point during 2010-01-01 to 2015-12-31 (overlap check).date('all')— disable temporal filtering entirely.date()— reset to today (default).
- Parameters:
date_str – Date string,
'all', orNoneto reset.end_str – Optional end date for range mode. End dates expand to period end (
'2015'→ 2015-12-31,'2015-06'→ 2015-06-30).
- Returns:
A new KnowledgeGraph with the given temporal context.
- define_schema(schema_dict: dict[str, Any]) KnowledgeGraph¶
Define the expected schema for the graph.
Replaces the entire schema — this is not a merge. Any previous
define_schemacall is fully superseded, so a call with a subset of types drops the types it omits. Redefine all types each call (or read the current schema viaschema_definition()and merge yourself).- Parameters:
schema_dict –
Schema definition with
nodesandconnectionskeys. Each node entry may setrequired/optional/typesand, optionally,primary_keyto declare an enforced PRIMARY KEY:g.define_schema({"nodes": {"Person": {"primary_key": "id"}}})
Declaring
primary_keymakes the write path enforce uniqueness on that key — aCREATEthat would duplicate it is rejected (useMERGEto upsert). It is opt-in: types without a declaredprimary_keykeep the permissive default. For now the key must be"id"(the identity field); an enforced PK on an arbitrary property is not yet supported.A node entry may also set
layerto'managed'(rebuilt from source by a batch writer) or'runtime'(owned/mutated live by another writer, e.g. an agent). With layers declared, anadd_nodes(..., managed_reload=True)call refuses to write aruntimetype — enforcing disjoint ownership in a two-writer contract graph:g.define_schema({"nodes": { "AlgorithmSpec": {"layer": "managed"}, "Task": {"layer": "runtime"}, }})
A node entry may also set
auto_timestamp: Trueto opt that type into freshness provenance: every write (CypherCREATE/SET/MERGEandadd_nodes) auto-stamps anupdated_attimestamp, plus the caller-suppliedgit_sha/modified_bywhen provided. It is off by default (writes stay deterministic) and independent oflayer/lock_schema:g.define_schema({"nodes": {"Task": {"auto_timestamp": True}}})
- Returns:
Self with schema defined.
- degree_centrality(normalized: bool | None = None, connection_types: str | list[str] | None = None, top_k: int | None = None, as_dict: bool | None = None, timeout_ms: int | None = None, to_df: bool | None = None) ResultView | dict[Any, float] | pandas.DataFrame¶
Calculate degree centrality.
- Parameters:
normalized – Normalise by
(n-1). DefaultTrue.connection_types – Only count these relationship types (str or list).
top_k – Return only the top K nodes.
as_dict – Return
{id: score}dict instead of list of dicts.timeout_ms – Abort after this many milliseconds, returning partial results.
to_df – Return a pandas DataFrame with columns
type,title,id,score.
- Returns:
List of dicts with
type,title,id,score. Or a DataFrame ifto_df=True.
- degrees() dict[str, int]¶
Get connection count for each selected node.
- Returns:
{node_title: degree}.
- describe(types: list[str] | None = None, type_search: str | None = None, connections: bool | list[str] | None = None, cypher: bool | list[str] | None = None, fluent: bool | list[str] | None = None, max_pairs: int | None = None, sample_truncate: int | None = 40) str¶
Return an XML description of this graph for AI agents.
Five independent axes for progressive disclosure:
Node types (
typesparameter):describe()— Inventory overview with compact type descriptors and connections with edge property names. Adapts to graph scale: small graphs get full inline detail, extreme-scale graphs get a statistical summary with search hints.describe(types=['Field', 'Well'])— Focused detail for specific types with properties, connections, and samples. Each type carries a schema-adapted<example>query anchored on its real identifier property (its id alias, elseid) with a concrete sampled value, so an agent copies a query that matches that type’s key shape.
Type search (
type_searchparameter):describe(type_search='software')— Find types by name (case-insensitive substring match) with neighborhood fan-out. Returns matching types with their connections, plus one layer of connected types. Ideal for exploring large/extreme-scale graphs where listing all types is impractical.
Connections (
connectionsparameter):describe(connections=True)— All connection types with count, source/target node types, and property names.describe(connections=['BELONGS_TO'])— Deep-dive with per-pair counts, property stats with sample values, and sample edges. Use this to discover what data edges carry.
Cypher (
cypherparameter):describe(cypher=True)— Compact Cypher reference: all clauses, operators, functions, and procedures with 1-line descriptions.describe(cypher=['cluster', 'MATCH'])— Detailed docs with parameters and examples for specific topics.
Fluent API (
fluentparameter):describe(fluent=True)— Compact fluent API reference: all methods grouped by area with signatures and descriptions.describe(fluent=['traverse', 'where', 'spatial'])— Detailed docs with parameters and examples for specific topics.
When
type_search,connections,cypher, orfluentis set, only those tracks are returned (no node inventory).- Parameters:
types – Node type names for focused detail.
type_search – Case-insensitive substring pattern to search type names. Returns matching types with connections + 1 layer of connected types for neighborhood discovery.
connections – True for overview, list for deep-dive into specific types.
cypher – True for compact reference, list for detailed topic docs.
fluent – True for compact reference, list for detailed topic docs.
max_pairs – Cap on
(src_type, tgt_type)pairs rendered in thedescribe(connections=['T'])deep-dive. Defaults to 50. Sorted by count desc, so the head covers the dominant relationships; a trailing<more pairs="…" edges="…"/>marker reports the hidden tail. Raise to drill into wide fan-out connection types (e.g. WikidataP31has 191k distinct pairs).sample_truncate – Max chars for string values in
vals=attributes, sample-node ids/titles, and sample-edge attributes. Defaults to 40. PassNoneto disable truncation entirely — useful when you want full titles in an LLM prompt and have context-window budget for it. Has no effect on stored data; only the rendered XML.
- Raises:
ValueError – If any type/connection/topic is not found.
TypeError – If connections, cypher, or fluent has wrong type.
- difference(other: KnowledgeGraph) KnowledgeGraph¶
Keep nodes in
selfbut not inother(set difference).- Returns:
A new KnowledgeGraph with the difference.
- disable_columnar() None¶
Convert columnar properties back to compact per-node storage.
This is the inverse of
enable_columnar(). Useful before saving to.kglformat or when columnar storage is no longer needed.
- drop_composite_index(node_type: str, properties: list[str]) bool¶
Remove a composite index. Returns
Trueif it existed.
- drop_index(node_type: str, property: str) bool¶
Remove an index. Returns
Trueif it existed.
- drop_range_index(node_type: str, property: str) bool¶
Remove a range index. Returns
Trueif it existed.
- drop_vector_index(node_type: str, text_column: str) bool¶
Drop the HNSW index for an embedding store (search reverts to exact).
Returns
Trueif an index was dropped,Falseif none existed.
- embed_texts(node_type: str, text_column: str, batch_size: int = 256, show_progress: bool = True, replace: bool = False, mode: str | None = None) dict[str, int]¶
Embed a text column for all nodes of a given type.
Uses the model registered via
set_embedder(). Reads each node’stext_columnproperty, callsmodel.embed()in batches, and stores the resulting vectors as{text_column}_emb. Nodes with missing or non-string text are skipped.The store also records, per node, a hash of the embedded text and (when the embedder names it) the model id — so a later
embed_texts(mode='changed')can re-embed exactly the nodes whose text changed, andembedding_info()can report provenance.Shows a tqdm progress bar by default (requires
tqdm).- Parameters:
node_type – The node type to embed (e.g.
'Article').text_column – The node property containing text to embed.
batch_size – Number of texts per
model.embed()call (default 256).show_progress – Show a tqdm progress bar (default
True). Silently falls back to no bar iftqdmis not installed.replace – Legacy alias for
mode.True→mode='all',False→mode='missing'. Ignored whenmodeis given.mode – Which nodes to embed —
'missing'(default): only nodes without an embedding;'changed': nodes missing an embedding or whose text changed since the last embed (via the stored content hash) — the incremental re-embed;'all': re-embed every node, rebuilding the store fresh.
- Returns:
Dict with
embedded,skipped,skipped_existing,reembedded_changed, anddimension.
Example:
g.set_embedder(my_model) g.embed_texts("Article", "summary") # Embedding Article.summary: 100%|████████| 1000/1000 [00:05<00:00] # Add/edit articles, then re-embed only what changed: g.embed_texts("Article", "summary", mode="changed")
- embedding(node_type: str, text_column: str, node_id: Any) list[float] | None¶
Retrieve a single node’s embedding vector.
- Parameters:
node_type – The node type (e.g. ‘Article’).
text_column – Source text column name (e.g. ‘summary’).
node_id – The node ID to look up.
- Returns:
The embedding vector as a list of floats, or None if not found.
- embedding_diagnostics(node_type: str | None = None) list[dict[str, Any]]¶
Diagnose embedding coverage per (node_type, text_column).
Companion to
list_embeddings(). Surfaces three states:"embedded": a store exists and at least one node has the underlying property."embeddable": nodes have a string-typed property but no embedding store has been created or restored."store_orphan": a store exists but no node in the current graph has the underlying property — the symptomimport_embeddings()warns about when keys mismatch.
Each row also carries a
length_statsdict so callers can filter on string-length distribution + cardinality before committing to embed a column. ISO timestamps, status enums, and fully-unique identifiers show up withstatus="embeddable"but theirlength_statsdistinguishes them from real candidates:# keep columns averaging ≥ 20 chars and not fully-unique candidates = [d for d in g.embedding_diagnostics() if d["length_stats"]["mean_length"] >= 20 and d["length_stats"]["distinct_ratio"] < 1.0]
- Parameters:
node_type – Optional filter. When set, only that node type is scanned. When
None, every type in the graph is scanned — may be expensive on graphs with millions of nodes.- Returns:
node_type,text_column,embedding_key(=f"{text_column}_emb"),nodes_with_property,nodes_embedded,dimension(orNone),metric(orNone),status, andlength_statswithmean_length/max_length/distinct_count/distinct_ratio.- Return type:
List of dicts with
- embedding_dim(node_type: str, text_column: str) int | None¶
The vector dimension of the
(node_type, text_column)embedding store, orNoneif none exists.A cheap, direct way to detect an embedder/model change without bookkeeping: compare it against your model’s dimension before re-embedding.
embed_texts/add_embeddingsreject a dimension mismatch (re-embed withreplace=Trueto rebuild at a new dimension).Example:
if g.embedding_dim("Article", "summary") not in (None, model.dimension): g.embed_texts("Article", "summary", replace=True) # model changed
- embedding_info(node_type: str, text_column: str) dict[str, Any] | None¶
Provenance for the
(node_type, text_column)embedding store, orNoneif no store exists.Returns a dict with
dimension,count(vectors stored),model(the embedder id stamped atembed_textstime, orNonefor vectors supplied directly viaadd_embeddings),metric, andhashed(how many vectors carry a source-text hash, used byembed_texts(mode='changed')for change-detection). Detect a model swap or a partially-hashed store without external bookkeeping.metricis the store’s effective distance metric: the one set viaset_embeddings(metric=...)if any, else'cosine'(the default search applies). It is neverNonefor an existing store — a store with no explicit metric reports'cosine'(consistent withlist_embeddings()).The
modelis populated when the embedder exposes amodel_id/model_nameattribute (or is the built-in fastembed backend).
- embeddings(node_type: str, text_column: str) dict[Any, list[float]]¶
- embeddings(text_column: str) dict[Any, list[float]]
Retrieve embeddings for nodes in the current selection.
- Parameters:
text_column – Source text column name (e.g. ‘summary’).
- Returns:
Dict mapping node IDs to embedding vectors.
- enable_columnar() None¶
Convert node properties to columnar storage.
Properties are moved from per-node storage into per-type column stores, reducing memory usage for homogeneous typed columns (int64, float64, string, etc.). Automatically compacts properties first if not already compacted.
Example:
graph.enable_columnar() assert graph.is_columnar
- enable_disk_mode() None¶
Convert the graph to disk-backed storage mode.
Enables columnar storage first (if not already), then builds CSR (Compressed Sparse Row) edge arrays on disk. Nodes stay in memory (~40 bytes each), edges are mmap’d from disk.
This reduces memory usage to ~10% of the in-memory graph for edge-heavy graphs. Best called after all data is loaded.
All query methods (Cypher, fluent API, algorithms) work identically in disk mode.
Example:
graph.enable_disk_mode()
- exists(node_type: str, unique_id: Any) bool¶
Return
Trueif a node ofnode_typewith that id exists. O(1).Uses the same hash index as
node()(no scan), and mirrors its id-coercion semantics: ids are integers in every storage mode, so a Pythonintand the stored id normalize to the same key.- Parameters:
node_type – The node type (e.g.
'User').unique_id – The unique ID value.
- Returns:
Trueif a matching node exists,Falseotherwise.
- expand(hops: int | None = None) KnowledgeGraph¶
Expand the selection by N hops (breadth-first, undirected).
- Parameters:
hops – Number of hops to expand. Default
1.- Returns:
A new KnowledgeGraph with the expanded selection.
- explain() str¶
Return a human-readable execution plan for the current query chain.
Example output:
SELECT Person (500 nodes) -> WHERE (42 nodes)
- static explain_mcp() str¶
Return a self-contained XML quickstart for setting up a KGLite MCP server.
Covers the bundled
kglite-mcp-serverconsole script (the default on-ramp — no fork required), the YAML manifest for adding custom tools (source_root:for file access, inline Cypher tools, and trust-gated Python hooks), and Claude Desktop / Claude Code registration config.Example:
print(KnowledgeGraph.explain_mcp())
- explore(query: str, max_entities: int = 10, max_depth: int = 2, include_source: bool = True, source_roots: list[str] | None = None) str¶
One-call codebase exploration over a code-tree graph.
Lexically ranks
Function/Class/Interface/Struct/Trait/Protocol/Enumnodes againstquery(matched against name + signature + docstring), takes the topmax_entities, 2-hop traverses CALLS / USES_TYPE / HAS_METHOD / DEFINES / REFERENCES_FN, and returns a markdown report with entry points, a relationship map, and grouped source slices for the entry points.Designed for the “how does X work in this codebase” question that would otherwise require a chain of grep + read calls. Composes FTS + traversal + source-slicing into a single Rust- side call.
- Parameters:
query – Free-text topic. Matched against name + signature + docstring. Exact name matches rank highest.
max_entities – Top N entry points after ranking (default 10).
max_depth – Hops for the neighborhood traversal (default 2).
include_source – Whether to include source slices for entry points (default
True). Set False for a smaller, faster response when the entity list is all you need.source_roots – Filesystem roots to resolve
file_pathproperties against. Files matched literally are tried first; roots are searched in order. Default: cwd only.
- Returns:
A markdown string with
## Query/## Entry points/## Related/## Sourcesections. Empty queries and graphs with no matching entities return a clear no-match message rather than raising.
- export(path: str, format: str | None = None, selection_only: bool | None = None) None¶
Export the graph to a file.
Supported formats:
graphml,gexf,d3/json,csv. Format is inferred from the file extension if not specified.- Parameters:
path – Output file path.
format – Export format. Default: inferred from extension.
selection_only – Export only selected nodes. Default:
Trueif selection exists.
- export_csv(path: str, selection_only: bool | None = None, verbose: bool = False) dict[str, Any]¶
Export to an organized CSV directory tree with blueprint.
Creates a directory structure with one CSV per node type and one per connection type, plus a
blueprint.jsonfor round-trip re-import viafrom_blueprint().Output structure:
path/ ├── nodes/ │ ├── Person.csv │ ├── Company.csv │ └── Person/ # sub-nodes nested under parent │ └── Skill.csv ├── connections/ │ ├── WORKS_AT.csv │ └── KNOWS.csv └── blueprint.json
Node CSVs have columns:
id,title, then all properties. Connection CSVs:source_id,source_type,target_id,target_type, then edge properties.Only connections where both endpoints are in the selection are exported.
- Parameters:
path – Output directory (created if it doesn’t exist).
selection_only – Export only selected nodes. Default:
Trueif a selection exists,Falseotherwise.verbose – Print progress information during export.
- Returns:
Summary dict with keys
output_dir,nodes(type → count),connections(type → count),files_written.
Example:
graph.export_csv('output/') graph.select('Person').export_csv('output/', verbose=True)
- export_embeddings(path: str, node_types: list[str] | dict[str, list[str]] | None = None) dict[str, int]¶
Export embeddings to a standalone
.kglefile, keyed by node ID.- Parameters:
path – Output
.kglefile path.node_types –
Optional filter.
None(default): export all stores.list[str]: only stores whosenode_typeis in the list.dict[str, list[str]]: per-type list of text columns to export.
- Returns:
Dict with
stores(count of stores written) andembeddings(total embedding vectors written).
- export_string(format: str, selection_only: bool | None = None) str¶
Export the graph to a string.
Supported formats:
graphml,gexf,d3/json.- Parameters:
format – Export format.
selection_only – Export only selected nodes.
- extend(other: KnowledgeGraph, conflict_handling: str | None = None) dict[str, Any]¶
Merge another KnowledgeGraph into this one, in place.
A native alternative to round-tripping through CSV export/import when building a graph incrementally from multiple sources (or merging two
.kglfiles loaded into memory). The other graph is read-only and never mutated. Both graphs must use the default in-memory storage.Example:
g1 = kglite.load('source_a.kgl') g2 = kglite.load('source_b.kgl') report = g1.extend(g2) # g2 folded into g1 in place report = g1.extend(g2, 'preserve') # existing g1 values win
Semantics:
Node identity is
(node_type, id)— the key the id index uses.idis the canonical integer node id in every storage mode. When a node in other matches an existing node here, the conflict is resolved byconflict_handling(same vocabulary asadd_nodes):'update'(default) — merge properties, other wins on conflicts; title is overwritten.'replace'— replace all properties and the title with other’s.'skip'— leave the existing node untouched.'preserve'— merge properties, existing values win; title kept unless currently null.'sum'— adds numeric property values on edges; for node properties it acts asupdate(matchesadd_nodes/add_connections'sum'semantics).
Secondary labels (multi-label, since 0.10.5) are unioned onto the matched/created node — never removed. Idempotent.
Property schemas merge: a property present in other but not here extends this graph’s type schema (the same path
add_nodesuses for new columns).Edges dedup on
(connection_type, source, target): an edge that already exists here is not duplicated — its properties merge perconflict_handling. Exact-duplicate edges present in both graphs are created once, not twice (mirrorsadd_connections’ dedup so a merge never silently doubles shared edges).
Scope limits (v1):
In-memory only. Both graphs must use the default in-memory storage;
storage='mapped'/'disk'graphs raise an error suggesting the export/import path.Embeddings are NOT merged. If other has any embedding stores a warning is emitted — re-run
set_embeddings/add_embeddingsafter the merge to rebuild them here.Self-extend (
g.extend(g)) is a no-op for creation: every node/edge already matches itself, so the result is a property merge against self (a no-op under every mode but'replace').Locks. Like
add_nodes/add_connections, this bulk path does not consultschema_locked/read_only(those gate the Cypher write path only).
- Parameters:
other – KnowledgeGraph to merge into this one (read-only).
conflict_handling – ‘update’ (default), ‘replace’, ‘skip’, ‘preserve’, or ‘sum’.
- Returns:
Operation report dict with
nodes_created,nodes_updated,nodes_skipped,edges_created,edges_skipped,node_types_merged,connection_types_merged,labels_unioned,processing_time_ms,has_errors, and optionallyerrors.
- find(name: str, node_type: str | None = None, match_type: str | None = None) list[dict[str, Any]]¶
Find code entities by name, with disambiguation context.
⚠ Code-entity search only.
find()searches nodes of typeFunction,Struct,Class,Enum,Trait,Protocol,Interface,Module, orConstant— the types produced bykglite.code_tree. On graphs that don’t contain these types (e.g. a social graph withPersonnodes),find()returns an empty list. For general name lookup on other node types, useselect(type).where({"name": ...})orcypher("MATCH (n:Type) WHERE n.name = $n RETURN n", params={"n": ...}).- Parameters:
name – Entity name to search for (e.g.
"execute").node_type – Optional filter — only search this node type (e.g.
"Function","Struct").match_type – Matching strategy:
"exact"(default),"contains"(case-insensitive substring), or"starts_with"(case-insensitive prefix).
- Returns:
type, name, qualified_name, file_path, line_number, and optionally signature and visibility.
- Return type:
List of dicts with
- freeze() FrozenGraph¶
Take an immutable, concurrently-readable snapshot of the graph.
Returns a
FrozenGraphthat shares this graph’s data (an O(1) clone — no deep copy) and exposes only read methods. A liveKnowledgeGraphis single-owner and raises if a second thread touches it while another mutates it; aFrozenGraphhas no mutating method, so any number of threads can runcypher()against the same snapshot in parallel, lock-free.The snapshot is stable: mutating the source graph afterwards copy-on-writes a fresh copy, leaving the frozen view on the original data — the “build → freeze → share → swap” model for serving concurrent readers while a new snapshot is built in the background.
- get_default_max_rows() int | None¶
Get the current default max rows limit, or None.
- get_default_timeout() int | None¶
Get the current default query timeout in milliseconds, or None.
- get_properties(properties: list[str], limit: int | None = None, indices: list[int] | None = None, flatten_single_parent: bool | None = None) list[tuple[Any, Ellipsis]] | dict[str, list[tuple[Any, Ellipsis]]]¶
Get specific properties for selected nodes.
Without traversal (single parent group), returns a flat list of tuples. After traversal (multiple parent groups), returns
{parent_title: [tuples]}.- Parameters:
properties – List of property names to retrieve.
limit – Maximum number of nodes.
indices – Specific node indices.
flatten_single_parent – Flatten single-group results to a list. Default
True.
- Returns:
list[tuple]when flattened,dict[str, list[tuple]]when grouped.
- graph_info() dict[str, Any]¶
Get diagnostic information about graph storage health.
Returns a dictionary with storage metrics useful for deciding when to call
vacuum()orreindex().- Returns:
node_count: Number of live nodesnode_capacity: Upper bound of node indices (includes tombstones)node_tombstones: Number of wasted slots from deletionsedge_count: Number of live edgesfragmentation_ratio: Ratio of wasted storage (0.0 = clean)type_count: Number of distinct node typesproperty_index_count: Number of single-property indexescomposite_index_count: Number of composite indexescolumnar_heap_bytes: Heap-resident bytes in columnar storescolumnar_is_mapped: Whether any columnar data is file-backedmemory_limit: Configured memory limit (None if unset)columnar_total_rows: Total rows in columnar stores (includes orphaned)columnar_live_rows: Rows backed by live nodes
- Return type:
dict with keys
Example:
info = graph.graph_info() if info['fragmentation_ratio'] > 0.3: graph.vacuum()
- has_composite_index(node_type: str, properties: list[str]) bool¶
Check if a composite index exists.
- has_index(node_type: str, property: str) bool¶
Check if an index exists.
- has_schema() bool¶
Check if a schema has been defined.
- has_vector_index(node_type: str, text_column: str) bool¶
Whether an HNSW index is currently built over the
(node_type, text_column)embedding store.
- ids() list[Any]¶
Return a flat list of ID values from the current selection.
The lightest retrieval method — no dict wrapping.
- import_embeddings(path: str) dict[str, int]¶
Import embeddings from a
.kglefile.Matches embeddings to nodes by
(node_type, node_id). Embeddings whose node ID doesn’t exist in the current graph are skipped.When all embeddings — or whole per-type stores — fail to match, a
UserWarningis emitted to surface the silent-drop case. This most commonly indicates the.kglefile was exported from a different graph, or that the node ID schema has drifted (e.g. thecode_treequalified-name format changed across kglite versions).- Parameters:
path – Path to a
.kglefile previously created byexport_embeddings().- Returns:
stores: number of stores actually inserted.imported: total embedding vectors matched to current nodes.skipped: total entries in the file that didn’t match.dropped_stores: number of per-type stores in the file that contained entries but had zero matches (so the store was not inserted).
- Return type:
Dict with
- index_stats(node_type: str, property: str) dict[str, Any] | None¶
Get statistics for an index. Returns
Noneif not found.
- indexes() list[dict[str, Any]]¶
Return a unified list of all indexes.
- Returns:
node_type: the indexed node typeproperty: property name (for equality indexes)properties: list of property names (for composite indexes)type:'equality'or'composite'
- Return type:
List of dicts, each with
- indices() list[int]¶
Return raw graph indices for selected nodes.
- intersection(other: KnowledgeGraph) KnowledgeGraph¶
Keep only nodes present in both selections (set intersection).
- Returns:
A new KnowledgeGraph with only shared nodes.
- intersects_geometry(query_wkt: str | Any, geometry_field: str | None = None) KnowledgeGraph¶
Filter nodes whose geometry intersects a WKT geometry.
- Parameters:
query_wkt – WKT string or shapely geometry object.
geometry_field – Geometry property name. Default
'geometry'.
- Returns:
A new KnowledgeGraph with intersecting nodes.
- label_pair_counts() list[tuple[str, str, str, int]]¶
Return the label-pair edge-count cardinality cache.
Lists every
(src_type, edge_type, tgt_type)triple present in the graph along with its edge count. Backs the Cypher planner’s 0.9.35 selectivity-awarereorder_match_clausespass — picking the more-selective driving side on label-skewed patterns by consulting per-triple counts instead of edge-type totals.Computed lazily: first access walks every edge once (O(E)); subsequent reads return a cached snapshot in O(triples) (typically <100 entries). Edge mutations — Cypher
CREATE/DELETE, Pythonadd_connections— invalidate the cache.- Returns:
A list of 4-tuples
[(src_type, edge_type, tgt_type, count), ...]. Order is hash-arbitrary; sort the result if a deterministic row order matters.
Example
```python for src, edge, tgt, count in graph.label_pair_counts():
print(f”{src} -[:{edge}]-> {tgt}: {count}”)
- label_propagation(max_iterations: int | None = None, connection_types: list[str] | None = None, timeout_ms: int | None = None) dict[str, Any]¶
Detect communities using label propagation.
- Parameters:
max_iterations – Maximum iterations. Default
100.connection_types – Only consider edges of these types. Default all edge types.
timeout_ms – Abort after this many milliseconds, returning partial results.
- Returns:
Dict with
communities,modularity, andnum_communities.
- last_report() dict[str, Any]¶
Get the most recent operation report as a dict.
Returns an empty dict if no operations have been performed.
- len() int¶
Count selected nodes without materialising them.
Much faster than
len(collect()). Also available vialen(graph).
- limit(max_per_group: int) KnowledgeGraph¶
Limit the number of nodes per parent group.
- Parameters:
max_per_group – Maximum number of nodes to keep per group.
- Returns:
A new KnowledgeGraph with the limited selection.
- list_composite_indexes() list[dict[str, Any]]¶
List all composite indexes.
- list_embeddings() list[dict[str, Any]]¶
List all embedding stores in the graph.
- Returns:
List of dicts with
node_type,text_column,dimension,count.
- list_indexes() list[dict[str, str]]¶
List all indexes. Each dict has
typeandproperty.
- load_ntriples(path: str, *, predicates: list[str] | None = None, languages: list[str] | None = None, node_types: dict[str, str] | None = None, predicate_labels: dict[str, str] | None = None, max_entities: int | None = None, max_triples: int | None = None, verbose: bool = False, progress: Callable[[dict], None] | None = None) dict¶
Load an N-Triples file into the graph.
Streams the file (supports
.bz2,.gz, or plain.nt) and converts RDF triples into a property graph. Designed for Wikidata truthy dumps but works with any N-Triples file.RDF → property graph mapping:
Each unique
Q-entity subject becomes a node.rdfs:label(language-filtered) → node title.schema:description→descriptionproperty.prop/direct/P*with aQ-entity object → edge.prop/direct/P*with a literal object → node property.P31(instance of) determines the node type via node_types.
- Parameters:
path – Path to the N-Triples file (
.nt,.nt.bz2,.nt.gz).predicates – Wikidata P-codes to import (e.g.
["P31", "P279"]).Noneimports all predicates. Labels and descriptions are always imported regardless of this filter.languages – Language codes for label/description literals (e.g.
["en"]).Nonekeeps all languages.node_types – Maps a P31 target Q-code to a human-readable node type name (e.g.
{"Q5": "Person", "Q6256": "Country"}). Entities whose P31 value is not in this map use the raw Q-code as their type. Entities without P31 get type"Entity".predicate_labels – Maps P-codes to human-readable edge/property names (e.g.
{"P31": "instance_of", "P17": "country"}). Unmapped predicates use the raw P-code.max_entities – Stop after creating this many nodes. Useful for exploratory loading of large dumps.
max_triples – Stop after scanning this many triples (entity and non-entity lines alike). Applied alongside
max_entities; whichever fires first wins. Useful for benchmarking the bz2/gz/plain decompression + parser pipeline at a fixed wall-time-friendly slice.verbose – Print progress to stderr every 5M triples.
progress – Optional callable receiving structured per-phase events. The callback is invoked with a single dict whose
"kind"is"start","update", or"complete"and whose"phase"is one of"phase1","phase1b","phase2","phase3","finalising". Seekglite.progress.TqdmBuildProgressfor a tqdm-backed reporter. Errors raised by the callback are swallowed.
- Returns:
A dict with load statistics:
{"entities": int, "edges": int, "edges_skipped": int, "triples_scanned": int, "seconds": float}
Example:
graph = KnowledgeGraph() stats = graph.load_ntriples( "latest-truthy.nt.bz2", predicates=["P31", "P279", "P17", "P106"], languages=["en"], node_types={"Q5": "Person", "Q6256": "Country"}, predicate_labels={"P31": "instance_of", "P17": "country"}, max_entities=1_000_000, verbose=True, )
- lock_schema() KnowledgeGraph¶
Lock the schema: future Cypher mutations must conform to current types.
When locked, CREATE, SET, and MERGE operations are validated against the graph’s known node types, connection types, and property types. Invalid writes return descriptive errors with ‘did you mean?’ suggestions.
- Returns:
Self for method chaining.
Example:
graph.lock_schema() graph.cypher("CREATE (p:Typo {name: 'x'})") # raises RuntimeError
See also
- louvain_communities(weight_property: str | None = None, resolution: float | None = None, connection_types: list[str] | None = None, timeout_ms: int | None = None) dict[str, Any]¶
Detect communities using the Louvain algorithm.
- Parameters:
weight_property – Edge property to use as weight. Default all edges weight
1.0.resolution – Resolution parameter (higher = more communities). Default
1.0.connection_types – Only consider edges of these types. Default all edge types.
timeout_ms – Abort after this many milliseconds, returning partial results.
- Returns:
Dict with
communities(dict of community_id to member list),modularity, andnum_communities.
- match_pattern(pattern: str, max_matches: int | None = None) list[dict[str, Any]]¶
Match a Cypher-like pattern against the graph.
Supports node patterns
(a:Type {prop: val}), directed edges-[:TYPE]->,<-[:TYPE]-, and undirected-[:TYPE]-.- Parameters:
pattern – Pattern string, e.g.
'(a:Person)-[:KNOWS]->(b:Person)'.max_matches – Maximum results to return.
- Returns:
List of match dicts with variable bindings.
- near_point(center_lat: float, center_lon: float, max_distance: float, lat_field: str | None = None, lon_field: str | None = None) KnowledgeGraph¶
Filter nodes within a distance (in degrees) of a point.
- Parameters:
center_lat – Center latitude.
center_lon – Center longitude.
max_distance – Maximum distance in degrees.
lat_field – Latitude property name. Default
'latitude'.lon_field – Longitude property name. Default
'longitude'.
- Returns:
A new KnowledgeGraph with nearby nodes.
- near_point_m(center_lat: float, center_lon: float, max_distance_m: float, lat_field: str | None = None, lon_field: str | None = None) KnowledgeGraph¶
Filter nodes within a distance (in meters) using geodesic calculation.
Uses WGS84 ellipsoid for accurate Earth-surface distances. Falls back to geometry centroid when lat/lon fields are missing but a WKT geometry is configured via
set_spatial.- Parameters:
center_lat – Center latitude.
center_lon – Center longitude.
max_distance_m – Maximum distance in meters.
lat_field – Latitude property name. Default from spatial config or
'latitude'.lon_field – Longitude property name. Default from spatial config or
'longitude'.
- Returns:
A new KnowledgeGraph with nearby nodes.
- neighbors_schema(node_type: str) dict[str, list[dict[str, Any]]]¶
Return connection topology for a node type.
- Parameters:
node_type – The node type to inspect.
- Returns:
outgoing: list of{connection_type, target_type, count}incoming: list of{connection_type, source_type, count}
- Return type:
Dict with
- Raises:
KeyError – If node_type does not exist.
- node(node_type: str, node_id: Any) dict[str, Any] | None¶
Look up a single node by type and ID. O(1) via hash index.
- Parameters:
node_type – The node type (e.g.
'User').node_id – The unique ID value.
- Returns:
Node property dict, or
Noneif not found.
- node_type_counts() dict[str, int]¶
Get node counts per type without materialising nodes.
- Returns:
Dict mapping node type name to count.
- offset(n: int) KnowledgeGraph¶
Skip the first n nodes per parent group (pagination).
Combine with
limit()for pagination:graph.sort('name').offset(20).limit(10)- Parameters:
n – Number of nodes to skip.
- Returns:
A new KnowledgeGraph with the offset selection.
- operation_index() int¶
Get the sequential index of the last operation.
- pagerank(damping_factor: float | None = None, max_iterations: int | None = None, tolerance: float | None = None, connection_types: str | list[str] | None = None, top_k: int | None = None, as_dict: bool | None = None, timeout_ms: int | None = None, to_df: bool | None = None) ResultView | dict[Any, float] | pandas.DataFrame¶
Calculate PageRank centrality.
- Parameters:
damping_factor – Probability of following a link. Default
0.85.max_iterations – Maximum iterations. Default
100.tolerance – Convergence threshold. Default
1e-6.connection_types – Only traverse these relationship types (str or list).
top_k – Return only the top K nodes.
as_dict – Return
{id: score}dict instead of list of dicts.timeout_ms – Abort after this many milliseconds, returning partial results.
to_df – Return a pandas DataFrame with columns
type,title,id,score.
- Returns:
List of dicts with
type,title,id,score. Or a DataFrame ifto_df=True.
- properties(node_type: str, max_values: int = 20) dict[str, dict[str, Any]]¶
Return property statistics for a node type.
Only properties that exist on at least one node are included.
- Parameters:
node_type – The node type to inspect.
max_values – Include
valueslist when unique count <= this threshold. Set to 0 to never include values. Default: 20.
- Returns:
type: type string (e.g.'str','int','float')non_null: count of non-null valuesunique: count of distinct values (a lower bound whenapproxis True)values: (optional) sorted list of values when unique count <= max_valuesapprox: True whenunique/valuesare not exhaustive — the type was sampled (only huge, Wikidata-scale types) or the distinct-value set hit its cap. Types at or below ~200k nodes are scanned in full and report exact stats (approxFalse).
- Return type:
Dict mapping property name to stats dict with keys
- Raises:
KeyError – If node_type does not exist.
- purge_provisional() dict[str, Any]¶
Delete provisional stub nodes that were never promoted.
When an edge is loaded against a node that doesn’t exist, the node is auto-vivified as a provisional stub (carrying
_provisional) so the edge isn’t lost. A later load of the real node row clears the marker.purge_provisional()deletes whatever is still marked — genuinely dangling references — along with their incident edges.Important: This resets the current selection since node indices change. Call this between query chains, not in the middle of one.
- Returns:
nodes_purged: Number of provisional stub nodes deletededges_removed: Number of incident edges removed with them
- Return type:
dict with keys
Example:
result = graph.purge_provisional() print(f"Dropped {result['nodes_purged']} dangling stubs")
- read_only(enabled: bool | None = None) bool¶
Set or query read-only mode for the Cypher layer.
When enabled, all Cypher mutation queries (CREATE, SET, DELETE, REMOVE, MERGE) are rejected, and
describe()omits mutation docs.- Parameters:
enabled – If
True, enable read-only mode. IfFalse, disable. If omitted, return the current state without changing it.- Returns:
The current read-only state (after applying the change, if any).
Example:
graph.read_only(True) # lock the graph graph.read_only() # -> True graph.read_only(False) # unlock
- rebuild_caches() None¶
Force recomputation of internal caches (edge type counts, etc.).
Call once after bulk mutations to warm the cache before
save()ordescribe(). The cache is persisted bysave()and restored byload(), so this only needs to be called once after building or mutating a graph.Example:
g = KnowledgeGraph() g.add_nodes(...) g.add_connections(...) g.rebuild_caches() # one-time O(E) pass g.save("graph.kgl") # persists warm cache
- rebuild_indexes() int¶
Rebuild all indexes. Returns the number of indexes rebuilt.
- reindex() None¶
Rebuild all indexes from the current graph state.
Reconstructs type_indices, property_indices, and composite_indices by scanning all live nodes. Clears lazy caches (id_indices, connection_types) so they rebuild on next access.
Use after bulk mutations (especially Cypher DELETE/REMOVE) to ensure index consistency.
Example:
graph.reindex()
- remove_embeddings(node_type: str, text_column: str) None¶
Remove an embedding store.
- Parameters:
node_type – The node type.
text_column – Source text column name (e.g.
'summary').
- remove_label(node_type: str, ids: list[Any], label: str) dict[str, int]¶
Remove a secondary label from a batch of nodes by id.
Errors if
labelis the primary type — useSET n.typeto retype a node instead.- Parameters:
node_type – Primary type of the nodes.
ids – Node ids.
label – Secondary label to remove.
- Returns:
Dict with
removedandskipped(unknown ids, or label not present on the node).
- replace_connections(data: pandas.DataFrame | None, connection_type: str, source_type: str, source_id_field: str, target_type: str, target_id_field: str, source_title_field: str | None = None, target_title_field: str | None = None, columns: list[str] | None = None, skip_columns: list[str] | None = None, conflict_handling: str | None = None, column_types: dict[str, str] | None = None, query: str | None = None, extra_properties: dict[str, Any] | None = None) dict[str, Any]¶
Replace a node’s outgoing edges of a given type, then add new ones — an atomic edge upsert.
Unlike
add_connections()(add-only), this prunes first: for every source node present indata(or thequeryresult), its existing edges ofconnection_typeare removed, then the edges the input describes are added. Edges from sources not in the input, and edges of other types from the same sources, are left untouched. The prune and add happen in one call, so there is no clear-then-add window that could leave a node edgeless if a separate re-add step failed.Use it to re-sync a derived edge set idempotently — “the current
MENTIONSof exactly these documents is this list”:# First sync: doc 1 -> [A, B] graph.replace_connections(df_ab, 'MENTIONS', 'Doc', 'doc', 'Entity', 'ent') # Re-sync doc 1 -> [B, C]: the stale 1->A edge is pruned, 1->C added. graph.replace_connections(df_bc, 'MENTIONS', 'Doc', 'doc', 'Entity', 'ent')
Accepts every argument
add_connections()does (includingquerymode andextra_properties), with identical semantics; only the prune-first behaviour differs.- Parameters:
data – DataFrame containing edge data, or
Nonewhen usingquery.connection_type – Edge type to replace (e.g.
'MENTIONS').source_type – Node type of source nodes.
source_id_field – Column with source node IDs.
target_type – Node type of target nodes.
target_id_field – Column with target node IDs.
source_title_field – Optional title column for source nodes.
target_title_field – Optional title column for target nodes.
columns – Whitelist of property columns to include (data mode only).
skip_columns – Columns to exclude (data mode only).
conflict_handling –
'update'(default),'replace','skip','preserve', or'sum'.column_types – Override column dtypes (data mode only).
query – Cypher query string (alternative to
data). Must be read-only.extra_properties – Static properties stamped onto every edge (query mode only).
- Returns:
Operation report dict with
connections_created,connections_skipped, etc.
- report_history() list[dict[str, Any]]¶
Get all operation reports as a list of dicts.
- sample(node_type: str, n: int = 5) ResultView¶
- sample(n: int = 5) ResultView
- save(path: str | None = None, *, fsync: bool = True) None¶
Serialise the graph to disk, atomically and durably.
For default/mapped modes: saves to a
.kglbinary file. For disk mode: saves to a directory containing CSR files and compressed node/edge data. The directory IS the saved graph.Crash-safety (default/mapped modes). The file is written to a sibling temp file and then atomically renamed over the target, so a crash mid-write can never leave a torn/truncated
.kgl— a reader always sees either the previous file or the complete new one. Withfsync=True(default) the file and its directory are flushed to physical storage before returning, so a committed save survives an OS/power crash. The temp name is unique per process, so two processes saving the same path won’t corrupt each other’s in-flight write (last rename wins, cleanly — keep one writer per file).Uses columnar storage internally for efficient compression and larger-than-RAM loading. If the graph is not already in columnar mode,
save()enables it automatically (the graph stays columnar after the call).Load it back with
kglite.load()(accepts both files and directories).- Parameters:
path – Output file path (typically
*.kgl). May be omitted if the graph was opened viakglite.open()orkglite.load(), in which case it defaults to that origin file. Passing a path updates the remembered target (“save as”). RaisesValueErrorif omitted and there is no remembered path.fsync – When
True(default), flush the file and its parent directory to disk before returning (durable against an OS/power crash). SetFalseto skip the flush for speed — the write is still atomic (temp + rename, no torn file), just not guaranteed flushed to physical media when the call returns.
- save_subset(path: str) None¶
Save the current selection as an independent subgraph file.
Equivalent to
kg.to_subgraph().save(path)in a single call. Output is a v3 binary file that reloads viakglite.load(path)(orload(path, storage='disk')for disk mode). All edges between selected nodes are included; node and edge properties round-trip byte-for-byte.- Parameters:
path – Destination path for the subgraph file.
Example
>>> kg.select("Article").expand(hops=1, type="AUTHORED_BY").save_subset( ... "articles_with_authors.kgl" ... )
- schema() dict[str, Any]¶
Return a full schema overview of the graph.
- Returns:
node_types:{type_name: {count, properties: {name: type_str}}}connection_types:{conn_name: {count, source_types: list, target_types: list}}indexes: list of"Type.property"stringsnode_count: total nodesedge_count: total edges
- Return type:
Dict with keys
Note
Scans all edges once (O(m)) to compute accurate connection type stats.
- schema_definition() dict[str, Any] | None¶
Get the current schema definition as a dict, or
None.
- schema_text() str¶
Return a text summary of the graph schema (node types, connections).
- search(text: str, *, property: str = 'title', limit: int = 10) list[dict[str, Any]]¶
Find nodes matching
textonproperty(default ‘title’).Tries exact match first, then prefix match. Returns up to
limitresults as dicts withid(node index),type,title, andid_value(the node’s id-field value, e.g. a Wikidata Q-number).Requires
create_global_index(property)to have been run on a disk-backed graph. Returns an empty list otherwise.Example:
graph.create_global_index('label') hits = graph.search('Norway') # [{'id': 12345, 'type': 'country', 'title': 'Norway', # 'id_value': 'Q20'}]
- search_text(text_column: str, query: str, top_k: int = 10, metric: str = 'cosine', to_df: bool = False, returning: list[str] | None = None, exact: bool = False) list[dict[str, Any]] | pandas.DataFrame¶
Search embeddings using a text query.
Uses the model registered via
set_embedder()to embed the query, then performs vector search within the current selection. Refer to the text column name (e.g."summary"); the graph resolves it to"summary_emb"internally.- Parameters:
text_column – Text column whose embeddings to search (e.g.
'summary').query – The text query to search for.
top_k – Number of results (default 10).
metric –
'cosine'(default),'dot_product','euclidean', or'poincare'.to_df – If True, return a pandas DataFrame.
returning – Optional field projection — see
vector_search(). Omitted → full hit (all properties); given →id+score+ the named fields only.exact – Force an exact brute-force scan even when an HNSW index exists — see
vector_search()andbuild_vector_index().
- Returns:
Same format as
vector_search()— list of dicts or DataFrame.
Example:
results = g.select("Article").search_text( "summary", "find AI articles", top_k=10 )
- select(node_type: str, sort: str | list[tuple[str, bool]] | None = None, limit: int | None = None, temporal: bool | None = None, include_secondary: bool = False) KnowledgeGraph¶
Select all nodes of a given type.
When a temporal config exists for this node type (via
set_temporal()), nodes are auto-filtered to those valid at the reference date (today ordate()context). Passtemporal=Falseto include all nodes.- Parameters:
node_type – The node type to select (e.g.
'Person').sort – Optional sort spec — a property name or list of
(field, ascending)tuples.limit – Limit the number of selected nodes.
temporal – Override temporal filtering.
None= auto (filter if configured),False= disable,True= require (error if not configured).include_secondary – When
True, also select nodes that carrynode_typeas a secondary label (added viaadd_label()), not only nodes whose primary type isnode_type— the fluent equivalent of CypherMATCH (n:node_type). DefaultFalsepreserves primary-type-only selection. On a graph with no secondary labels the two are identical.
- Returns:
A new KnowledgeGraph with the filtered selection.
- selection() str¶
Return a text summary of the current selection state.
- session() Session¶
Seed a thread-safe, shareable
Sessionfrom this graph.Unlike a live
KnowledgeGraph— which is single-owner and trips a borrow guard when shared across threads mid-mutation — aSessionexposes only&selfmethods with synchronisation in an internal lock: concurrentcypher()reads run lock-free, andexecute()writes serialise behind the lock with copy-on-write + atomic swap.The
Sessionis an independent owner: it shares this graph’s data at creation (O(1), no copy), but once either side mutates, copy-on-write forks them and they no longer track each other. The intended model is “build / load with aKnowledgeGraph, then.session()and serve every thread through theSession” — don’t keep mutating the original graph after handing out aSession.
- set_auto_vacuum(threshold: float | None) None¶
Configure automatic vacuum after DELETE operations.
When enabled, the graph automatically compacts itself after Cypher DELETE operations if the fragmentation ratio exceeds the threshold and there are more than 100 tombstones.
- Parameters:
threshold – A float between 0.0 and 1.0, or
Noneto disable. Default is0.3(30% fragmentation triggers vacuum).
Example:
graph.set_auto_vacuum(0.2) # more aggressive — vacuum at 20% graph.set_auto_vacuum(None) # disable auto-vacuum graph.set_auto_vacuum(0.3) # restore default
- set_default_max_rows(max_rows: int | None = None) None¶
Set a default max rows limit for all cypher() calls.
Queries producing more intermediate rows than this will raise an error. Pass None to disable (default). Per-query
max_rowsoverrides this.
- set_default_timeout(timeout_ms: int | None = None) None¶
Set a default query timeout (milliseconds) for all cypher() calls.
None(default): fall through to the built-in default of 180_000 ms (3 min) for every storage mode.0: disable the deadline for every query unless per-calltimeout_msoverrides.Positive integer: use as the default.
Per-query
timeout_msalways overrides this setting.
- set_embedder(model: EmbeddingModel | None) None¶
Register or unbind an embedding model on the graph.
Pass a model object to register; pass
Noneto unbind the currently-registered embedder.After registering,
embed_texts()andsearch_text()use the registered model automatically. The model is not serialized — callset_embedder()again after deserializing.If the model has optional
load()/unload()methods, they are called automatically around each embedding operation.- Parameters:
model – An embedding model with
dimensionandembed()— seeEmbeddingModel. OrNoneto unbind.
Example:
g.set_embedder(my_model) g.set_embedder(None) # unbind
- set_embeddings(node_type: str, text_column: str, embeddings: dict[Any, list[float]], metric: str | None = None) dict[str, int]¶
Store embeddings for nodes of the given type. Replaces any existing store for
(node_type, "{text_column}_emb").Embeddings are stored separately from regular node properties and are invisible to
collect(),to_df(), and other property-based APIs. The embedding store key is auto-derived as{text_column}_emb.Validates that
text_columnexists as a property on the node type (builtinsid,title,typeare always accepted).For incremental ingest where you want to add to an existing store without a read-merge-write round-trip, use
add_embeddings().- Parameters:
node_type – The node type (e.g.
'Article').text_column – Source text column name (e.g.
'summary').embeddings – Dict mapping node IDs to embedding vectors.
metric – Default distance metric for this store. Used when no metric is specified at query time.
'cosine'(default),'dot_product','euclidean', or'poincare'. Persisted withsave().
- Returns:
Dict with
embeddings_stored,dimension, andskipped.
- set_instructions(text: str, *, channel: str | None = None) KnowledgeGraph¶
Set free-text instructions/briefing rendered verbatim at the top of
describe()— so an agent that opens the graph cold reads it first.Unlike sample values in
describe(), this text is shown in full (no truncation). It persists in the.kgl. Pass emptytextto clear.- Parameters:
text – The instructions (plain text / markdown). Shown verbatim.
channel – Reserved for per-audience instructions;
None(default) sets the single graph-level slot.
- Returns:
Self (chainable).
- set_memory_limit(limit_bytes: int | None, spill_dir: str | None = None) None¶
Configure automatic memory-pressure spill for columnar storage.
When a memory limit is set,
enable_columnar()will automatically spill the largest column stores to temporary files on disk when total heap usage exceeds the limit.- Parameters:
limit_bytes – Maximum heap bytes for columnar data, or
Noneto disable the limit.spill_dir – Directory for spill files. Defaults to system temp dir.
Example:
graph.set_memory_limit(500_000_000) # 500 MB limit graph.enable_columnar() # auto-spills if over limit graph.set_memory_limit(None) # disable limit
- set_parent_type(node_type: str, parent_type: str) None¶
Declare a node type as a supporting child of a parent type.
Supporting types are hidden from the
describe()inventory and instead appear in the<supporting>section when the parent type is inspected. Their capabilities (timeseries, spatial, etc.) bubble up to the parent descriptor.- Parameters:
node_type – The supporting (child) node type.
parent_type – The core (parent) node type.
- Raises:
ValueError – If either type does not exist in the graph.
Example:
graph.set_parent_type('ProductionProfile', 'Field') graph.set_parent_type('FieldReserves', 'Field')
- set_spatial(node_type: str, *, location: tuple[str, str] | None = None, geometry: str | None = None, points: dict[str, tuple[str, str]] | None = None, shapes: dict[str, str] | None = None) None¶
Configure spatial properties for a node type.
- Parameters:
node_type – The node type to configure.
location – Primary lat/lon pair as
(lat_field, lon_field). At most one per type.geometry – Primary WKT geometry field name. At most one per type.
points – Named lat/lon points as
{name: (lat_field, lon_field)}.shapes – Named WKT shape fields as
{name: field_name}.
- set_temporal(type_name: str, valid_from: str, valid_to: str) None¶
Configure temporal validity for a node type or connection type.
After configuration,
select()auto-filters temporal nodes andtraverse()auto-filters temporal connections to “current” (today or thedate()context).Auto-detects whether type_name is a node type or connection type.
- Parameters:
type_name – Node type (e.g.
'FieldStatus') or connection type (e.g.'HAS_LICENSEE').valid_from – Property name holding the start date.
valid_to – Property name holding the end date.
- Raises:
ValueError – If type_name is not a known node or connection type.
- set_time_index(node_id: Any, keys: list[str] | list[list[int]]) None¶
Set the sorted time index for a specific node.
If the node already has a timeseries, this replaces its time index and clears all channels.
- Parameters:
node_id – The node’s unique ID.
keys – Sorted list of date strings (e.g.
['2020-01', '2020-02']) or composite integer keys for backwards compat (e.g.[[2020, 1], [2020, 2]]).
- set_timeseries(node_type: str, *, resolution: str, channels: list[str] | None = None, units: dict[str, str] | None = None, bin_type: str | None = None) None¶
Configure timeseries metadata for a node type.
- Parameters:
node_type – The node type to configure.
resolution – Time granularity —
'year','month', or'day'. Determines key depth (year=1, month=2, day=3).channels – Optional list of known channel names.
units – Optional map of channel name to unit string, e.g.
{'oil': 'MSm3', 'temperature': '°C'}.bin_type – What values represent —
'total','mean', or'sample'. None if unspecified.
- shortest_path(source_type: str, source_id: Any, target_type: str, target_id: Any, connection_types: list[str] | None = None, via_types: list[str] | None = None, weight_property: str | None = None, timeout_ms: int | None = None) dict[str, Any] | None¶
Find the shortest path between two nodes.
- Parameters:
source_type – Source node type.
source_id – Source node ID.
target_type – Target node type.
target_id – Target node ID.
connection_types – Only traverse edges of these types. Default all.
via_types – Only traverse through nodes of these types. Default all.
weight_property – Edge property to use as cost. When set, the search uses Dijkstra and minimises total weight; when
None, BFS minimises hop count. Edges missing the property fall back to weight 1.0 (matches Louvain’s weighted-adjacency convention). Negative weights cause the path to be reported as missing.timeout_ms – Abort after this many milliseconds and return
None.
- Returns:
Dict with
path(list of node info dicts),connections(list of edge types), andlength(hop count). Whenweight_propertyis set, also includesweight(sum of edge weights).Noneif no path exists or timeout is reached.
- shortest_path_ids(source_type: str, source_id: Any, target_type: str, target_id: Any, connection_types: list[str] | None = None, via_types: list[str] | None = None, timeout_ms: int | None = None) list[Any] | None¶
Get node IDs along the shortest path.
- Parameters:
connection_types – Only traverse edges of these types. Default all.
via_types – Only traverse through nodes of these types. Default all.
timeout_ms – Abort after this many milliseconds and return
None.
- Returns:
List of node IDs, or
Noneif no path exists or timeout is reached.
- shortest_path_indices(source_type: str, source_id: Any, target_type: str, target_id: Any, connection_types: list[str] | None = None, via_types: list[str] | None = None, timeout_ms: int | None = None) list[int] | None¶
Get raw graph indices along the shortest path.
Fastest path query — no node data lookup.
- Parameters:
connection_types – Only traverse edges of these types. Default all.
via_types – Only traverse through nodes of these types. Default all.
timeout_ms – Abort after this many milliseconds and return
None.
- Returns:
List of integer indices, or
Noneif no path exists or timeout is reached.
- shortest_path_length(source_type: str, source_id: Any, target_type: str, target_id: Any, weight_property: str | None = None) int | float | None¶
Get just the cost of the shortest path.
Faster than
shortest_path()when you only need the distance. Does not supportconnection_typesorvia_typesfiltering.- Parameters:
weight_property – When set, uses Dijkstra and returns total weight (float). When
None, BFS returns hop count (int).- Returns:
Hop count (int) or total weight (float), or
Noneif no path exists.
- show(columns: list[str] | None = None, limit: int = 200) str¶
Display selected nodes with specific properties in a compact format.
Single level (no traversals): one node per line as
Type(val1, val2). Multi-level (after traverse): walks the full chain asType1(vals) -> Type2(vals) -> Type3(vals).- Parameters:
columns – Property names to include. Default
["id", "title"].limit – Maximum output lines. Default
200.
Example:
print(graph.select("Discovery").show(["id", "title"])) # Discovery(123, Johan Sverdrup) print(graph.select("Discovery") .traverse("IN_FIELD") .traverse("DISCOVERY_WELLBORE") .show(["id"])) # Discovery(123) -> Field(456) -> Wellbore(789)
- sort(sort: str | list[tuple[str, bool]], ascending: bool | None = None) KnowledgeGraph¶
Sort the current selection.
- Parameters:
sort – Property name (string) or list of
(field, ascending)tuples.ascending – Direction when sort is a single string. Default
True.
- Returns:
A new KnowledgeGraph with the sorted selection.
- source(name: str, node_type: str | None = None) dict[str, Any]¶
- source(name: list[str], node_type: str | None = None) list[dict[str, Any]]
- spatial(node_type: str | None = None) dict[str, Any] | None¶
Get spatial configuration for a node type or all types.
- Parameters:
node_type – If given, return config for this type only. Otherwise return all.
- Returns:
Dict with spatial config, or
Noneif not configured.
- statistics(property: str, level_index: int | None = None, group_by: str | None = None) Any¶
Compute descriptive statistics for a numeric property.
Returns per-parent stats including count, mean, std, min, max, sum.
- Parameters:
property – Numeric property name.
level_index – Target level in the hierarchy.
group_by – Group results by this property instead of by parent. Returns
{group_value: {count, sum, mean, min, max, std}}.
- subgraph_stats() dict[str, Any]¶
Get statistics about the subgraph that would be extracted.
- Returns:
Dict with
node_count,edge_count,node_types,connection_types.
- symmetric_difference(other: KnowledgeGraph) KnowledgeGraph¶
Keep nodes in exactly one of the selections (symmetric difference).
- Returns:
A new KnowledgeGraph with nodes exclusive to each side.
- time_index(node_id: Any) list[str] | None¶
Get the time index for a node as ISO date strings, or
None.
- timeseries(node_id: Any, channel: str | None = None, start: str | None = None, end: str | None = None) dict[str, Any] | None¶
Extract timeseries data for a node.
If
channelis given, returns{'keys': [...], 'values': [...]}. Otherwise returns{'keys': [...], 'channels': {'name': [...], ...}}.- Parameters:
node_id – The node’s unique ID.
channel – Optional channel name to extract.
start – Optional range start as date string (e.g.
'2020','2020-2').end – Optional range end as date string.
- Returns:
Dict with keys and channel data, or
Noneif no timeseries.
- timeseries_config(node_type: str | None = None) dict[str, Any] | None¶
Get timeseries configuration for a node type or all types.
Returns a dict with
resolution,channels,units,bin_type.
- titles(limit: int | None = None, indices: list[int] | None = None, flatten_single_parent: bool | None = None) list[str] | dict[str, list[str]]¶
Get titles of selected nodes.
Without traversal (single parent group), returns a flat list of titles. After traversal (multiple parent groups), returns
{parent_title: [titles]}.- Parameters:
flatten_single_parent – Flatten single-group results to a list. Default
True.- Returns:
list[str]when flattened,dict[str, list[str]]when grouped.
- to_bytes() bytes¶
Serialise the in-memory graph to a
.kglbyte buffer.Returns the same bytes
save()writes to disk, so a caller can own the write — push to object storage, a pipe, a checksum, or a custom atomic-write routine — instead of being limited to a filesystem path. Round-trips throughkglite.from_bytes().Default/mapped modes only: a
disk-mode graph is a directory, not a single byte stream, so this raisesValueErrorfor disk graphs (usesave('dir/')there).
- to_df(*, include_type: bool = True, include_id: bool = True) pandas.DataFrame¶
Export current selection as a pandas DataFrame.
Each node becomes a row with columns for title, type, id, and all properties. Missing properties across different node types become None.
- Parameters:
include_type – Include
typecolumn. DefaultTrue.include_id – Include
idcolumn. DefaultTrue.
- Returns:
DataFrame with one row per selected node.
- to_networkx() Any¶
Convert the graph to a
networkx.MultiDiGraph.KGLite is a directed multigraph with typed nodes and edges, so
MultiDiGraphis the lossless target. Each node’sidis the networkx node key;node_type,titleand every property are attached as node attributes. Each edge’sconnection_typeis the networkx edge key (so parallel edges of different types between the same pair stay distinct) and is also stored as theconnection_typeedge attribute alongside every edge property.The inverse is
kglite.from_networkx().Requires the
networkxpackage:pip install networkx.- Returns:
A
networkx.MultiDiGraphmirroring the full graph.
Note
v1 always exports the full graph; the active selection is ignored. A future revision may honour selections.
Example:
import networkx as nx nxg = graph.to_networkx() scores = nx.pagerank(nxg)
- to_str(limit: int = 50) str¶
Format the current selection as a human-readable string.
Each node is printed as a block with
[Type] title (id: x)header and indented properties, one per line.- Parameters:
limit – Maximum number of nodes to show. Default
50.
- to_subgraph() KnowledgeGraph¶
Extract selected nodes into a new independent graph.
The new graph contains only selected nodes and the edges between them.
- to_text() str¶
Deterministic, human-readable text projection of the whole graph.
Nodes grouped by type and sorted by id; edges sorted by endpoints — so the output is stable across insert order and across save/load. This is the canonical form behind the
.kglgittextconvdiff filter (alsokglite export-text <file>from the CLI), makinggit diffof two.kglsnapshots show real content changes. Reserved provenance keys (updated_at/git_sha) are omitted so per-write metadata churn doesn’t swamp the diff.- Returns:
The text projection.
- toc(file_path: str) dict[str, Any]¶
Get a table of contents for a file — all code entities defined in it.
Returns entities sorted by line number with a type summary.
- Parameters:
file_path – Path of the file (the File node’s id/path).
- Returns:
Dict with
"file"(path),"entities"(list of entity dicts sorted by line_number, each with type, name, qualified_name, line_number, end_line, and optionally signature), and"summary"(dict of type name to count).
- traverse(connection_type: str, level_index: int | None = None, direction: str | None = None, filter_target: dict[str, Any] | None = None, filter_connection: dict[str, Any] | None = None, sort_target: str | list[tuple[str, bool]] | None = None, limit: int | None = None, new_level: bool | None = None, at: str | None = None, during: tuple[str, str] | None = None, temporal: bool | None = None, target_type: str | list[str] | None = None, where: dict[str, Any] | None = None, where_connection: dict[str, Any] | None = None) KnowledgeGraph¶
Traverse connections to discover related nodes by following graph edges.
For spatial, semantic, or clustering operations, use
compare()instead.- Parameters:
connection_type – Edge type to follow (e.g.
'HAS_LICENSEE').direction –
'outgoing','incoming', orNone(both).target_type – Filter targets to specific node type(s). Accepts a string or list of strings. Useful when a connection type connects to multiple node types.
where – Property conditions for target nodes — same operators as
.where()('>','contains','in', etc.).where_connection – Property conditions for edge properties.
sort_target – Sort targets per source. Field name or
[(field, ascending)]list.limit – Max target nodes per source.
at – Temporal point-in-time filter (e.g.
'2005').during – Temporal range filter (e.g.
('2000', '2010')).temporal – Override temporal filtering.
False= disable.level_index – Source level in the hierarchy (advanced).
new_level – Add targets as new hierarchy level. Default
True.filter_target – Alias for
where.filter_connection – Alias for
where_connection.
- Returns:
A new KnowledgeGraph with traversal results selected.
Examples:
# Follow edges graph.select('Field').traverse('HAS_LICENSEE') # Filter to specific target type graph.select('Field').traverse('OF_FIELD', direction='incoming', target_type='ProductionProfile') # Multiple target types graph.select('Field').traverse('OF_FIELD', direction='incoming', target_type=['ProductionProfile', 'FieldReserves']) # Filter target node properties graph.select('Field').traverse('HAS_LICENSEE', where={'title': 'Equinor Energy AS'}) # Filter edge properties graph.select('Person').traverse('RATED', where_connection={'score': {'>': 4}}) # Temporal filtering graph.select('Field').traverse('HAS_LICENSEE', at='2005') graph.select('Field').traverse('HAS_LICENSEE', during=('2000', '2010'))
- union(other: KnowledgeGraph) KnowledgeGraph¶
Combine selections from both graphs (set union).
- Returns:
A new KnowledgeGraph with nodes from either selection.
- unique_values(property: str, group_by_parent: bool | None = None, level_index: int | None = None, indices: list[int] | None = None, store_as: str | None = None, max_length: int | None = None, keep_selection: bool | None = None) Any¶
Get unique values of a property, optionally storing results.
- Parameters:
property – Property name to extract unique values from.
group_by_parent – Group by parent node. Default
True.level_index – Target level in the selection hierarchy.
indices – Specific node indices.
store_as – If set, stores comma-separated unique values as this property on parents.
max_length – Max string length when storing.
keep_selection – Preserve selection after store. Default
False.
- Returns:
Dict of unique values per parent, or a KnowledgeGraph if
store_asis set.
- unlock_schema() KnowledgeGraph¶
Unlock the schema: allow any Cypher mutations without validation.
- Returns:
Self for method chaining.
- unspill() None¶
Move mmap-backed columnar data back to heap memory.
Useful after deleting nodes when you want data back in RAM for faster access. Internally rebuilds columnar stores from scratch with the memory limit temporarily suspended to prevent re-spilling.
No-op if the graph is not in columnar mode.
Example:
graph.unspill() info = graph.graph_info() assert not info['columnar_is_mapped']
- update(properties: dict[str, Any], keep_selection: bool | None = None) dict[str, Any]¶
Batch-update properties on all selected nodes.
- Parameters:
properties – Mapping of property names to new values.
keep_selection – Preserve the current selection in the returned graph. Default
False.
- Returns:
Dict with
graph(updated KnowledgeGraph),nodes_updated(int), andreport_index(int).
- vacuum() dict[str, Any]¶
Compact the graph by removing tombstones left by node/edge deletions.
With StableDiGraph, deletions leave holes in the internal storage. Over time, this wastes memory and degrades iteration performance.
vacuum()rebuilds the graph with contiguous indices, then rebuilds all indexes. If columnar storage is active, column stores are also rebuilt to eliminate orphaned rows from deleted nodes.Important: This resets the current selection since node indices change. Call this between query chains, not in the middle of one.
- Returns:
nodes_remapped: Number of nodes that were remappedtombstones_removed: Number of tombstone slots reclaimedcolumnar_rebuilt: Whether columnar stores were rebuilt
- Return type:
dict with keys
Example:
info = graph.graph_info() if info['fragmentation_ratio'] > 0.3: result = graph.vacuum() print(f"Reclaimed {result['tombstones_removed']} slots")
- valid_at(date: str | None = None, date_from_field: str | None = None, date_to_field: str | None = None) KnowledgeGraph¶
Filter nodes valid at a specific date.
Keeps nodes where
date_from <= date <= date_to.If field names are not specified, auto-detects from
set_temporal()config. If date is not specified, uses thedate()context or today.- Parameters:
date – Date string (e.g.
'2024-01-15'). Defaults to reference date or today.date_from_field – Name of the start-date property. Auto-detected if temporal config exists.
date_to_field – Name of the end-date property. Auto-detected if temporal config exists.
- Returns:
A new KnowledgeGraph with the filtered selection.
- valid_during(start_date: str, end_date: str, date_from_field: str | None = None, date_to_field: str | None = None) KnowledgeGraph¶
Filter nodes whose validity period overlaps a date range.
If field names are not specified, auto-detects from
set_temporal()config.- Parameters:
start_date – Start of the query range.
end_date – End of the query range.
date_from_field – Name of the start-date property. Auto-detected if temporal config exists.
date_to_field – Name of the end-date property. Auto-detected if temporal config exists.
- Returns:
A new KnowledgeGraph with the filtered selection.
- validate_schema(strict: bool | None = None) list[dict[str, Any]]¶
Validate the graph against the defined schema.
- Parameters:
strict – Report undefined types in the graph. Default
False.- Returns:
List of validation error dicts. Empty list means valid.
- vector_search(text_column: str, query_vector: list[float], top_k: int = 10, metric: str = 'cosine', to_df: bool = False, returning: list[str] | None = None, exact: bool = False) list[dict[str, Any]] | pandas.DataFrame¶
Vector similarity search within the current selection.
Searches for nodes most similar to the query vector among the currently selected nodes. Results are ordered by similarity (most similar first).
- Parameters:
text_column – Source text column name (e.g.
'summary').query_vector – The query embedding vector.
top_k – Number of results to return (default 10).
metric –
'cosine','dot_product','euclidean', or'poincare'. If omitted, uses the metric stored viaset_embeddings(metric=...), or falls back to'cosine'.to_df – If
True, return a pandas DataFrame instead of list of dicts.returning – Optional list of fields to project onto each hit. Omitted (default) → each hit carries
id,title,type,scoreand all node properties. Given → each hit carriesid+scoreplus only the named fields (a property name, or a structural field liketitle/type). Use it to trim the payload on wide nodes or ranking-only paths.exact – Force an exact brute-force scan even when an HNSW index exists (see
build_vector_index). DefaultFalse→ a whole-corpus query on a large indexed store uses the approximate index; setTruefor guaranteed-exact results. Heavily-filtered selections and the'poincare'metric are always exact regardless.
- Returns:
List of dicts (or a DataFrame if
to_df=True). By default a hit hasid,title,type,score, and all node properties —scorealways present, properties read live so a hit is identical before/aftersave()+ reload (no follow-upMATCH ... WHERE id IN [...]join needed). Withreturning=[...]a hit hasid+score+ the requested fields only.
Example:
# full hit (default) results = (graph .select('Article') .where({'category': 'politics'}) .vector_search('summary', query_vec, top_k=10)) # ranking-only / slim payload ranked = graph.select('Article').vector_search( 'summary', query_vec, top_k=50, returning=['title'])
- where(conditions: dict[str, Any], sort: str | list[tuple[str, bool]] | None = None, limit: int | None = None) KnowledgeGraph¶
Filter the current selection by property conditions.
Conditions support exact match, comparison operators (
'>','<','>=','<='),'in','is_null','is_not_null','contains','starts_with','ends_with','regex'(or'=~'), and negated variants:'not_contains','not_starts_with','not_ends_with','not_in','not_regex'.Example:
graph.select('Person').where({ 'age': {'>': 25}, 'city': 'Oslo', 'name': {'regex': '^A.*'}, 'status': {'not_in': ['inactive', 'banned']}, })
- Returns:
A new KnowledgeGraph with the filtered selection.
- where_any(conditions: list[dict[str, Any]], sort: str | list[tuple[str, bool]] | None = None, limit: int | None = None) KnowledgeGraph¶
Filter the current selection with OR logic across multiple condition sets.
Each dict in conditions is a set of AND conditions (same as
where()). A node is kept if it matches any of the condition sets.- Parameters:
conditions – List of condition dicts. Must contain at least one.
sort – Optional sort spec.
limit – Limit the number of selected nodes.
- Returns:
A new KnowledgeGraph with the filtered selection.
Example:
graph.select('Person').where_any([ {'city': 'Oslo'}, {'city': 'Bergen'}, ])
- Raises:
ValueError – If conditions is empty.
- where_connected(connection_type: str, direction: str | None = None) KnowledgeGraph¶
Filter nodes that have at least one connection of the given type.
Keeps only nodes from the current selection that participate in edges of the specified type and direction.
- Parameters:
connection_type – Edge type to check (e.g.
'KNOWS').direction –
'outgoing','incoming', or'any'(default).
- Returns:
A new KnowledgeGraph with only connected nodes.
- Raises:
ValueError – If direction is not one of the valid values.
- where_orphans(include_orphans: bool | None = None, sort: str | list[tuple[str, bool]] | None = None, limit: int | None = None) KnowledgeGraph¶
Filter nodes based on whether they have connections.
- Parameters:
include_orphans – If
True, keep only orphan (disconnected) nodes. IfFalse, keep only connected nodes. DefaultTrue.sort – Optional sort spec.
limit – Limit the number of selected nodes.
- Returns:
A new KnowledgeGraph with the filtered selection.
- within_bounds(min_lat: float, max_lat: float, min_lon: float, max_lon: float, lat_field: str | None = None, lon_field: str | None = None) KnowledgeGraph¶
Filter nodes within a geographic bounding box.
- Parameters:
min_lat – South bound latitude.
max_lat – North bound latitude.
min_lon – West bound longitude.
max_lon – East bound longitude.
lat_field – Latitude property name. Default
'latitude'.lon_field – Longitude property name. Default
'longitude'.
- Returns:
A new KnowledgeGraph with only nodes in the bounding box.
- wkt_centroid(wkt_string: str | Any, as_shapely: bool = False) dict[str, float] | Any¶
Calculate the centroid of a WKT geometry string.
- Parameters:
wkt_string – WKT geometry string or shapely geometry object.
as_shapely – If
True, return ashapely.geometry.Pointinstead of a dict.
- Returns:
Dict with
latitudeandlongitude, or a shapely Point whenas_shapely=True.
- property is_columnar: bool¶
Whether node properties are stored in columnar format.
Returns
Trueifenable_columnar()has been called (or the graph was loaded from a v3.kglfile).
- property last_mutation_stats: dict[str, int] | None¶
Mutation statistics from the last Cypher mutation query.
Returns
Noneif no mutation has been executed yet. Keys:nodes_created,relationships_created,properties_set,nodes_deleted,relationships_deleted,properties_removed.
- property node_types: list[str]¶
List of node type names present in the graph.
- property schema_locked: bool¶
Whether the schema is currently locked.
- property shape: tuple[int, int]¶
(node_count, edge_count)— pandas-style. O(1) via the storage backend; does not materialise per-type breakdowns. Useschema()ordescribe()for the full structure.
- kglite.okf.build(path: str, *, dialect: str | None = ..., require_frontmatter: bool = ..., respect_skip: bool = ..., skip_dirs: list[str] | None = ..., with_body: bool = ..., embed: bool = ...) kglite.KnowledgeGraph¶
Build a
KnowledgeGraphfrom an OKF bundle directory.A bundle is a directory tree of markdown files with YAML frontmatter, cross-linked by markdown links. Each non-reserved
.mdfile becomes one node (label from the frontmattertype, orConceptwhen absent; id = the bundle-relative path minus.md); frontmatter keys become node properties (tagsand nested maps are JSON-encoded); markdown links become typed edges. Link types are inferred most-specific-first: an explicit link title ([x](/y.md "JOINS_WITH")) → the enclosing section header (# Citations→CITES) →LINKS_TO. Links to not-yet-written concepts become_provisionalstub nodes (MATCH (n {_provisional: true})).The graph is enriched by default with synthesized nodes that densify it:
Tagnodes ((:Concept)-[:TAGGED]->(:Tag)fromtags),Sourcenodes (externalhttp(s)links →(:Concept)-[:CITES]->(:Source)), andFoldernodes (the directory tree,(:Folder)-[:CONTAINS]->concepts / subfolders, with each directory’sindex.mdenriching its Folder).Ingestion is partial: the markdown body is not stored unless
with_bodyis set — each node keeps afile_pathpointer instead.- Parameters:
path – Bundle root directory.
dialect –
"okf"(default) for strict markdown links, or"loose"/"obsidian"to also resolve[[wikilinks]]and tolerate concepts with no frontmattertype.require_frontmatter – When
True(default), only.mdfiles with a YAML frontmatter block are ingested — the discriminator between structured knowledge (OKF concepts, Claude memories) and plain markdown (READMEs, notes). Point at a parent of many projects to sweep out only the structured files across all of them. SetFalseto ingest every.md(vault-style). Node labels fall backtype→metadata.type→Conceptand titlestitle→name→ file stem, so Claude memories land as:feedback/:project/ etc. with theirnameas title.respect_skip – When
True(default), honor akg_skip: truefrontmatter marker that opts a file out of the sweep. SetFalseto ingest skip-marked files anyway.skip_dirs – Directories to prune from the walk (the directory and its whole subtree). gitignore-style: an entry without a
/matches a directory by name at any depth ("node_modules"); an entry with a/is an anchored bundle-relative path ("vendor/repos"). Use it to exclude cloned / vendored trees you don’t own.with_body – Store each concept’s markdown body as a
bodyproperty (off by default — bodies are read on demand).embed – Reserved for the opt-in embedder pass (body vectors for
text_score); not yet wired.
- Returns:
A KnowledgeGraph of the bundle.
- Raises:
RuntimeError – If the bundle path does not exist or is not a directory.
- kglite.okf.source(path: str) str¶
Read a concept’s markdown body on demand (frontmatter stripped).
Pairs with partial ingestion: nodes store a
file_path(relative to the bundle root), not the body. Join it with the root and pass it here to fetch the prose once a query has narrowed to a single concept. A file with no frontmatter returns its whole content.- Parameters:
path – Path to the concept’s
.mdfile.- Returns:
The markdown body, with any leading YAML frontmatter removed.
- Raises:
RuntimeError – If the file cannot be read.