Examples

Every output below was produced by GraphBuff itself, on the movies graph from examples/movies.cypher or the small data each example creates. Timings are left out.

Who acted with whom

Cypher

A two-hop pattern with an aggregate and a collected list.

MATCH (k:Person {name: 'Keanu Reeves'})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(co)
RETURN co.name AS coActor, count(m) AS movies, collect(m.title) AS titles
ORDER BY movies DESC, coActor;
+--------------------+--------+---------------------------------------+
| coActor            | movies | titles                                |
+--------------------+--------+---------------------------------------+
| "Carrie-Anne Moss" | 2      | ["The Matrix", "The Matrix Reloaded"] |
| "Hugo Weaving"     | 1      | ["The Matrix"]                        |
+--------------------+--------+---------------------------------------+
2 rows

Shortest path between two people

Cypher

A bidirectional breadth-first search, then a list comprehension over the path.

MATCH p = shortestPath((h:Person {name: 'Hugo Weaving'})-[*]-(l:Person {name: 'Lana Wachowski'}))
RETURN [n IN nodes(p) | coalesce(n.name, n.title)] AS path;
+--------------------------------------------------+
| path                                             |
+--------------------------------------------------+
| ["Hugo Weaving", "The Matrix", "Lana Wachowski"] |
+--------------------------------------------------+
1 row

Everyone within four hops

Cypher

Variable-length patterns in any direction, deduplicated.

MATCH (h:Person {name: 'Hugo Weaving'})-[*1..4]-(other:Person)
WHERE other <> h
RETURN DISTINCT other.name AS reachable ORDER BY reachable;
+--------------------+
| reachable          |
+--------------------+
| "Carrie-Anne Moss" |
| "Keanu Reeves"     |
| "Lana Wachowski"   |
+--------------------+
3 rows

Upsert with a uniqueness constraint

Cypher

MERGE creates the node once; the constraint keeps names unique; temporal values are first-class.

CREATE CONSTRAINT person_name IF NOT EXISTS FOR (p:Person) REQUIRE p.name IS UNIQUE;

MERGE (p:Person {name: 'Laurence Fishburne'})
  ON CREATE SET p.born = 1961, p.created = date('2026-11-13')
  ON MATCH SET p.seen = true
RETURN p.name, p.born, p.created;
1 indexes added, 1 constraints added
+----------------------+--------+------------+
| p.name               | p.born | p.created  |
+----------------------+--------+------------+
| "Laurence Fishburne" | 1961   | 2026-11-13 |
+----------------------+--------+------------+
1 row; 1 nodes created, 3 properties set, 1 labels added

Counting subqueries and durations

Cypher

COUNT { } inline, and date arithmetic with duration.between.

MATCH (m:Movie)
RETURN m.title AS title,
       COUNT { (m)<-[:ACTED_IN]-() } AS cast,
       duration.between(date({year: m.released}), date('2026-01-01')).years AS yearsAgo
ORDER BY m.released;
+-----------------------+------+----------+
| title                 | cast | yearsAgo |
+-----------------------+------+----------+
| "The Matrix"          | 3    | 27       |
| "The Matrix Reloaded" | 2    | 23       |
| "John Wick"           | 1    | 12       |
+-----------------------+------+----------+
3 rows

Distance between two places

Cypher

WGS-84 points and haversine distance in metres.

WITH point({longitude: 14.42, latitude: 50.09}) AS prague,
     point({longitude: 16.61, latitude: 49.20}) AS brno
RETURN round(point.distance(prague, brno) / 1000) AS km;
+-------+
| km    |
+-------+
| 186.0 |
+-------+
1 row

PageRank from Cypher

Algorithms

The procedure runs as a kernel over the graph, and the query keeps working with its rows.

CALL algo.pageRank({relationshipTypes: 'ACTED_IN'}) YIELD node, score
RETURN coalesce(node.name, node.title) AS name, round(score, 3) AS score
ORDER BY score DESC LIMIT 4;
+-----------------------+-------+
| name                  | score |
+-----------------------+-------+
| "The Matrix"          | 0.384 |
| "The Matrix Reloaded" | 0.256 |
| "John Wick"           | 0.193 |
| "Keanu Reeves"        | 0.15  |
+-----------------------+-------+
4 rows

Breadth-first search from a node

Algorithms

Hop distances from Keanu Reeves, ignoring direction.

MATCH (k:Person {name: 'Keanu Reeves'})
CALL algo.bfs(k, {orientation: 'UNDIRECTED'}) YIELD node, depth
RETURN coalesce(node.name, node.title) AS reached, depth ORDER BY depth, reached;
+-----------------------+-------+
| reached               | depth |
+-----------------------+-------+
| "Keanu Reeves"        | 0     |
| "John Wick"           | 1     |
| "The Matrix"          | 1     |
| "The Matrix Reloaded" | 1     |
| "Carrie-Anne Moss"    | 2     |
| "Hugo Weaving"        | 2     |
| "Lana Wachowski"      | 2     |
+-----------------------+-------+
7 rows

Money that goes round in circles

Algorithms

A small transfer graph: connected components group the accounts, and a three-hop cycle shows money returning to where it started.

UNWIND [
  {from: 'alice', to: 'bob',   amount: 120},
  {from: 'bob',   to: 'carol', amount: 80},
  {from: 'carol', to: 'alice', amount: 45},
  {from: 'dave',  to: 'erin',  amount: 300}
] AS t
MERGE (a:Account {id: t.from})
MERGE (b:Account {id: t.to})
CREATE (a)-[:SENT {amount: t.amount}]->(b);

CALL algo.wcc({relationshipTypes: 'SENT'}) YIELD node, componentId
RETURN componentId, collect(node.id) AS accounts ORDER BY componentId;

MATCH p = (a:Account)-[:SENT*3]->(a)
RETURN [n IN nodes(p) | n.id] AS cycle,
       reduce(total = 0, r IN relationships(p) | total + r.amount) AS moved
LIMIT 1;
5 nodes created, 4 relationships created, 9 properties set, 5 labels added
+-------------+---------------------------+
| componentId | accounts                  |
+-------------+---------------------------+
| 0           | ["alice", "bob", "carol"] |
| 3           | ["dave", "erin"]          |
+-------------+---------------------------+
2 rows
+------------------------------------+-------+
| cycle                              | moved |
+------------------------------------+-------+
| ["alice", "bob", "carol", "alice"] | 245   |
+------------------------------------+-------+
1 row

Python, over Bolt

Drivers

The official neo4j package, unchanged. Start the server with --bolt.

from neo4j import GraphDatabase

with GraphDatabase.driver("bolt://127.0.0.1:7687", auth=("neo4j", "s3cret")) as driver:
    records, summary, keys = driver.execute_query(
        "MATCH (p:Person) WHERE p.born < $year RETURN p.name AS name",
        year=1965,
    )
    print([r["name"] for r in records])

Any language, over HTTP

Drivers

One JSON request per query. Real responses from graphbuff-server, shortened.

curl -s http://127.0.0.1:7475/query -H 'Content-Type: application/json' \
  -d '{"query": "CREATE (:Person {name: 'Ann'})-[:KNOWS]->(:Person {name: 'Bob'})"}'

curl -s http://127.0.0.1:7475/query -H 'Content-Type: application/json' \
  -d '{"query": "MATCH (p:Person) RETURN p.name AS name ORDER BY name"}'
{"columns":[],"rows":[],"stats":{"nodesCreated":2,"relationshipsCreated":1,"propertiesSet":2,"labelsAdded":2,...,"containsUpdates":true}}
{"columns":["name"],"rows":[["Ann"],["Bob"]],"stats":{...,"containsUpdates":false}}

Java, with no dependencies

Drivers

examples/java/GraphbuffClient.java is a single-file HTTP client for Java 17+. Copy it into a project and drop main.

var db = new GraphbuffClient("http://127.0.0.1:7475", token);
db.query("CREATE DATABASE javademo IF NOT EXISTS", null);

Result r = db.query("javademo",
    "MATCH (p:Person) WHERE p.age > $min RETURN p.name AS name, p.age AS age ORDER BY age",
    Map.of("min", 30));
for (int i = 0; i < r.rows().size(); i++) {
    System.out.println(r.get(i, "name") + " is " + r.get(i, "age"));
}

Embedded in Rust, with a transaction

Drivers

No server at all: the database runs in your process. Readers keep reading while the transaction is open.

use graphbuff::{Database, StorageMode, params};

let db = Database::open(StorageMode::hybrid("data/bank"))?;
let mut tx = db.begin()?;
tx.execute_with_params(
    "MATCH (a:Account {id: $from}), (b:Account {id: $to})
     SET a.balance = a.balance - $amount, b.balance = b.balance + $amount",
    &params! { "from" => 1, "to" => 2, "amount" => 10 },
)?;
tx.commit()?;

See what the planner did

Operations

PROFILE runs the query and reports rows and time per operator (times left out here).

PROFILE MATCH (p:Person)-[:ACTED_IN]->(m:Movie {title: 'The Matrix'})
RETURN p.name ORDER BY p.name;
Select [3 rows]
  Sort p.name [3 rows]
    Project p.name [3 rows]
      Expand(All) (m)<-[:ACTED_IN]-(p:Person) (~1.5 rows) [3 rows]
        NodeByLabelScan (m:Movie {…}) (~3.0 rows) [1 row]
          Start [1 row]
+--------------------+
| p.name             |
+--------------------+
| "Carrie-Anne Moss" |
| "Hugo Weaving"     |
| "Keanu Reeves"     |
+--------------------+
3 rows

Bulk import from CSV

Operations

The neo4j-admin import header format, written straight into a persistent store.

graphbuff import --data data --database social \
    --nodes Person=people_header.csv,people1.csv,people2.csv \
    --relationships FOLLOWS=follows.csv \
    --index 'Person(id)'

# people_header.csv
id:ID(Person),name,born:int,tags:string[],:LABEL
# follows.csv
:START_ID(Person),:END_ID(Person),since:date

Several databases, one server

Operations

Each database picks its own storage engine.

CREATE DATABASE movies;
CREATE DATABASE scratch IF NOT EXISTS OPTIONS {storage: 'memory'};
CREATE OR REPLACE DATABASE events
  OPTIONS {storage: 'hybrid', sync: 'os', checkpointAfterBytes: 16777216};
SHOW DATABASES;