Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Language Bindings

Beyond the CLI and server, patinaDB is reachable from Python, an MCP server for AI agents, and a graph browser / admin UI.

Python (patinadb)

A native Python extension (PyO3). The API is shaped for a data-science workflow: open a database, bulk-load a DataFrame, query it, run a graph algorithm, and get results back as ordinary Python objects.

Install the wheel provided with your distribution (or from your organization’s package index):

python -m venv .venv && . .venv/bin/activate
pip install patinadb-<version>-<platform>.whl

Open, query, close

The class is Database, opened via Database.open(path):

from patinadb import Database

db = Database.open("./mygraph")             # opens or creates an embedded db

db.query("CREATE (n:Person {name: 'Ada', age: 36})")

# query_rows → list[dict], keyed by RETURN column (the pandas-friendly path)
rows = db.query_rows("MATCH (n:Person) RETURN n.name AS name, n.age AS age")
# rows == [{"name": "Ada", "age": 36}]

db.close()

query() returns patinaDB’s column-oriented QueryResult shape; query_rows() returns row-oriented list[dict] with native Python values (a node/relationship cell becomes a nested dict). Both accept named parameters:

db.query_with_params(
    "MATCH (n:Person) WHERE n.name = $name RETURN n", {"name": "Ada"}
)
db.query_rows("MATCH (n:Person) WHERE n.age > $min RETURN n.name", {"min": 30})

Bulk-load a DataFrame → graph

bulk_load_nodes / bulk_load_edges take a list of dicts — exactly what DataFrame.to_dict("records") produces — and stream them through the engine’s batched, durable bulk-insert path. Loaded data is visible at HEAD (it bypasses the versioning layer, so it is not in AS OF time-travel reads).

import pandas as pd

people  = pd.read_parquet("people.parquet")    # columns: user_id, name, age
follows = pd.read_parquet("follows.parquet")   # columns: src, dst

# Nodes: deterministic UUIDs from a business key (id_key) → idempotent re-loads.
db.bulk_load_nodes("Person", people.to_dict("records"), id_key="user_id")

# Edges: 'from'/'to' are node UUIDs. Database.node_id() reproduces the id.
edges = [
    {"from": Database.node_id("Person", str(r.src)),
     "to":   Database.node_id("Person", str(r.dst))}
    for r in follows.itertuples()
]
db.bulk_load_edges("FOLLOWS", edges)

Graph algorithms

Thin wrappers over the built-in patinadb.algo.* procedures, returning the same list[dict] shape:

ranked     = db.page_rank("Person", "FOLLOWS")       # -> list of {node, score}
components = db.wcc("Person", "FOLLOWS")             # -> list of {node, componentId}
degrees    = db.degree("Person", "FOLLOWS", "in")    # -> list of {node, score}

You can also call any procedure (full-text, vector, stats, export, …) directly through query / query_rows via CALL.

API surface

MethodPurpose
Database.open(path)Open or create an embedded database.
db.query(cypher)Run a query → column-oriented QueryResult.
db.query_with_params(cypher, params)Same, with a dict of named parameters.
db.query_rows(cypher, params=None)Row-oriented list[dict] (DataFrame-friendly).
db.bulk_load_nodes(label, rows, id_key=None)Bulk-load node records; deterministic UUIDs from id_key.
db.bulk_load_edges(rel_type, edges)Bulk-load edge records (from/to UUIDs).
Database.node_id(label, key)Reproduce the deterministic UUID for a business key.
db.page_rank(label=None, rel_type=None, iterations=20, damping_factor=0.85)PageRank → list[{node, score}].
db.wcc(label=None, rel_type=None)Weakly-connected components → list[{node, componentId}].
db.degree(label=None, rel_type=None, direction="both")Degree centrality → list[{node, score}].
db.close()Close and flush.

The bindings talk to an embedded database directly. Time-travel/engram methods (AS OF, diffs) are reachable from the CLI, MCP, and the server; exposing them on the Database pyclass is planned.

MCP server (patinadb-mcp)

A Model Context Protocol server (stdio JSON-RPC) that exposes a database to an AI agent as tools. Beyond the core graph operations it surfaces patinaDB’s differentiators — time-travel, diffs, provenance, statistics, and graph algorithms:

ToolPurpose
schemaInspect the graph schema (labels, types, properties).
cypherRun a read query.
writeRun a write query.
beginStart a pending engram (transaction).
commitCommit the pending engram.
rollbackDiscard the pending engram.
snapshotCapture/inspect a full-graph snapshot.
engramsList the versioned commit history.
diffSingle-engram git-style diff.
diff_rangeStructural diff between two engrams.
time_travel_queryRead the graph AS OF a past engram.
provenanceRead the Anamnesis PROV projection (<db>__anamnesis).
statisticsData-shape catalog (CALL patinadb.stats) before querying.
graph_algorithmRun pageRank / wcc / degree / betweenness / closeness / triangleCount / labelPropagation.
spatial_searchNearest nodes to a point within a radius (geo metres or cartesian).
constraintsList (SHOW CONSTRAINTS) or create a unique / exists / node_key constraint.
adviseEXPLAIN plan + index suggestions (CALL patinadb.advisor), read-only.
cache_statsPer-level cache hit-rate / bytes (CALL patinadb.cache.stats).
import_csvBatched, deterministic-UUID CSV bulk-load (HEAD-only).

This lets an agent query, mutate, inspect history, time-travel, and analyze a patinaDB graph through structured tool calls. Full-text and vector search are reachable through the cypher tool via CALL.

spatial_search takes a label, a point-typed property, and a query point as either lat+lon (WGS-84, distance in metres via Haversine) or x+y (cartesian, Euclidean), plus an optional radius and limit; it returns the nearest nodes first with their distance. A CREATE POINT INDEX on the property turns it into a fast curve-range seek. Point/Polygon cells render as compact JSON so an agent can parse the coordinates. Label/property names are validated as plain identifiers before inlining, so the generated Cypher can’t be injected.

Security note. The MCP server has no read-only mode and no import sandbox today — every tool listed above, including write (arbitrary CREATE/SET/DELETE/DETACH DELETE) and import_csv (bulk-load from any filesystem path the process can read), is available to whatever agent holds the stdio connection. If you point an LLM agent at patinadb-mcp, treat it like handing that agent a database admin credential — there is no --read-only flag or import-directory allowlist to fall back on yet. Run it only against a database (and from an environment) you’re comfortable letting an agent mutate freely, and keep prompt-injected query results in mind: a malicious result returned by cypher could steer a subsequent write call.

Graph browser & admin UI (patinadb-browser)

patinadb-browser is not a WASM module — it is a single axum HTTP binary that serves one self-contained index.html (inline CSS/JS) and connects to a database in one of three modes:

ModeFlagData plane
File<db-path>opens an embedded Dataset directly
Server--server http://host:8080proxies a patinadb-raft node’s REST API
Bolt--bolt bolt://host:7687native Bolt client (neo4rs)
# File mode (embedded — no server needed)
patinadb-browser /path/to/mydb

# Server mode (against a running patinadb-raft node)
patinadb-browser --server http://127.0.0.1:8080 --user admin --password … --database default

# Bolt mode
patinadb-browser --bolt bolt://127.0.0.1:7687 --user neo4j --password …

Then open http://127.0.0.1:4200. It renders a force-directed graph view with a Neo4j-style node detail panel and neighbor expansion, an engram/diff timeline, graph-algorithm visualization, and (in server mode) a cluster/metrics/cache admin dashboard. See Graph Browser & Admin UI for details.