Command-Line Interface
patinadb operates on an embedded database directory. The first argument
is always the database path; the rest is a subcommand.
patinadb <db-path> <subcommand> [args]
If you’re running it from the Docker image rather than a host-installed
binary: docker run -v "$PWD:/data" --entrypoint patinadb patinadb/patinadb /data/<db-path> <subcommand> [args]. See Installation.
Subcommands
query
Execute a Cypher query and print the results.
patinadb ./mygraph query "MATCH (n:Person) RETURN n LIMIT 10"
patinadb ./mygraph query "MATCH (n) RETURN n" --json
patinadb ./mygraph query "MATCH (n) RETURN n" --at <engram-id>
| Flag | Effect |
|---|---|
--json | Emit results as JSON instead of a human-readable table. |
--at <id> | Time-travel: run against the graph at that engram. |
import
Offline bulk-load neo4j-admin-style CSV / Parquet / Arrow IPC files straight
into the embedded store (bypasses Raft + the engram/versioning layer — fast, but
the loaded data is HEAD-visible only, not in --at history). All node files load
first, then all relationship files.
patinadb ./mygraph import --nodes people.csv --nodes companies.parquet \
--rels works_at.arrow
patinadb ./mygraph import --nodes n.csv --rels r.csv --id-type uuid
Node files carry a :ID column, a :LABEL column, and typed property columns
(name:int, name:float, name:boolean, name:date, name:localdatetime, or
bare name for string). Relationship files carry :START_ID/:END_ID/:TYPE
- property columns. For Parquet/Arrow the property types come from the file schema. A blank/null cell means the property is absent.
| Flag | Default | Effect |
|---|---|---|
--nodes <file> | — | A node file (repeatable). |
--rels <file> | — | A relationship file (repeatable). |
--format <fmt> | (infer) | Force csv|parquet|arrow for all files (else per extension). |
--delimiter <c> | , | CSV field delimiter. |
--batch-size <n> | 10000 | Rows per durable batch. |
--skip-ref-check | off | Don’t verify rel endpoints exist (allows dangling edges). |
--id-type <t> | hash | hash = UUIDv5 of the id string; uuid = the id cell IS the UUID. |
--id-type uuid (and an auto-detected :ID(uuid) header) treat the id column as
a literal vertex UUID — this is what lets export → import reproduce the
same graph.
export
The complement of import: scan the whole graph read-only and write
import-compatible node/relationship files. One node file per primary label
(nodes_<Label>.<ext>) and one relationship file per edge type
(rels_<TYPE>.<ext>) are written into --out, streamed in --batch-size
batches (bounded memory).
patinadb ./mygraph export --out ./dump
patinadb ./mygraph export --out ./dump --format parquet
patinadb ./mygraph export --out ./dump --format arrow --batch-size 50000
| Flag | Default | Effect |
|---|---|---|
--out <dir> | — | Output directory (created if missing). |
--format <fmt> | csv | csv, parquet, or arrow. |
--batch-size <n> | 10000 | Rows per output batch (columnar formats). |
Round-trip: the id column is written as the raw vertex UUID under an
:ID(uuid) header, so re-importing the dump reproduces the same graph, the
same UUIDs included (no re-keying):
patinadb ./mygraph export --out ./dump --format parquet
patinadb ./fresh.db import --nodes ./dump/nodes_Person.parquet \
--rels ./dump/rels_KNOWS.parquet
Typed properties round-trip through the columns: Integer→:int, Float→:float,
Bool→:boolean, String, Date→:date, LocalDateTime→:localdatetime (Parquet/
Arrow use the native Int64/Float64/Boolean/Date32/Timestamp types). A
property that appears as mixed types across a label promotes (Int+Float→Float)
or, if incompatible, is written as a string column. Zoned/offset datetimes,
times, and durations are exported as ISO-8601 strings (they have no lossless
columnar type).
backup / restore
Portable dump and restore of one embedded database. backup writes a single
self-contained file (the graph plus compound-index and UNIQUE-constraint defs);
restore rebuilds a target database directory (fresh or existing) from it. This
is the embedded/offline analogue of the server’s GET /mgmt/snapshot +
POST /mgmt/restore.
patinadb ./mygraph backup ./mygraph.bak
patinadb ./mygraph backup ./mygraph.bak --history # + engram timeline (PITR)
patinadb ./fresh.db restore ./mygraph.bak
patinadb ./existing.db restore ./mygraph.bak --force # overwrite a non-empty target
| Flag | Effect |
|---|---|
--history | Also carry the full engram delta/snapshot timeline (backup only). |
--force | Required to restore into a non-empty target database (restore only). |
restore refuses to silently overwrite a non-empty target. Before opening
the backup file at all, it opens the target and cheaply checks its node/edge
counts; if the target already holds data and --force wasn’t passed, it dies
with a clear target db <path> is not empty (N nodes, M edges) — pass --force to overwrite and touches nothing (no file is opened, no data is changed). An
empty or not-yet-created target restores with no flag needed — this only
guards against accidentally pointing restore at the wrong, already-populated
directory (mirroring neo4j-admin’s --overwrite-destination convention). A
corrupt or truncated backup file also fails cleanly with a clear message
(never a panic), whether or not --force is given.
Without --history the backup is a HEAD-state dump — the current graph only.
With --history it additionally carries the engram history, so after restore
the whole pre-backup timeline is available for time-travel:
patinadb ./mygraph backup ./mygraph.bak --history
patinadb ./fresh.db restore ./mygraph.bak
patinadb ./fresh.db query "MATCH (n) RETURN count(n)" --at <old-engram-id>
changes
Print the change log — every committed engram after a cursor (oldest first),
each rendered as its individual mutations (createNode, setNodeProperty,
createRel, …). The embedded/offline analogue of the server’s /changes stream.
patinadb ./mygraph changes # whole history
patinadb ./mygraph changes --since <engram-id> # only what changed after the cursor
patinadb ./mygraph changes --json
| Flag | Effect |
|---|---|
--since <id> | Only engrams committed after this engram UUID (a cursor). |
--json | One JSON object per engram, with a changes array. |
Each engram’s own id is a valid --since cursor for the next call, so a script
can poll incrementally by remembering the last id it saw. (A built-in --follow
is intentionally not offered: an embedded database is exclusively locked by its
single process, so there is no concurrent writer for one to tail.)
algo
Run a graph algorithm via CALL patinadb.algo.* and print the top-k ranked
table (node id + value, highest first) — a convenience over typing the full CALL.
patinadb ./mygraph algo pageRank --label Person --rel KNOWS --limit 10
patinadb ./mygraph algo wcc
patinadb ./mygraph algo degree --label Person
<name> is one of pageRank, wcc, degree, betweenness, closeness,
triangleCount, labelPropagation (case-insensitive).
| Flag | Default | Effect |
|---|---|---|
--label <L> | (all) | Restrict to nodes with this label. |
--rel <R> | (all) | Restrict to relationships of this type. |
--limit <k> | 20 | Number of top-ranked rows to print. |
Spatial queries and constraints are already reachable through query, e.g.
query "CREATE CONSTRAINT FOR (p:Person) REQUIRE p.email IS UNIQUE" or
query "MATCH (p:Place) WHERE point.distance(p.loc, point({latitude:52.5, longitude:13.4})) < 5000 RETURN p".
log
List committed engrams (the history).
patinadb ./mygraph log
diff
Git-style view of a single engram.
patinadb ./mygraph diff <engram-id>
patinadb ./mygraph diff <engram-id> --json
diff-range
Structural, move-aware diff between two engrams (or from the empty graph).
patinadb ./mygraph diff-range <from-id> <to-id>
patinadb ./mygraph diff-range empty <to-id>
patinadb ./mygraph diff-range <from> <to> --identity-props email,id
patinadb ./mygraph diff-range <from> <to> --json
| Flag | Default | Effect |
|---|---|---|
--identity-props <list> | qualified_name,fqn,name | Identity priority for move pairing; none off. |
--json | — | JSON output. |
See Diffs and Time Travel for the concepts
behind diff, diff-range, and --at.