Semurg as a graph database: load edges, walk them to any depth
23 October 2026 · Semurg · 4 min read · Markdown
Last checked on r11.126.20261025.70c5311 (25 Oct 2026): every command below ran on a fresh node of that release. The current release is r11.127.20261026.f8fafdc (26 Oct 2026), and the commands have not been re-checked on it yet (how)
A graph in Semurg is a set of edges. An edge's id is computed from its two ends and its relation, so writing the same edge twice still leaves one edge. A walk starts from the nodes you name and follows outgoing edges level by level, as deep as you ask, and answers with how many nodes it reached, how many edges it followed, and how many nodes it first reached at each level.
Load edges and walk them, from Python
pip install semurg, then build each edge with semurg.container.edge(source, target) (or edge(source, target, relation)) and send them with ingest. The program below writes a small graph, a short chain with one branch, and walks it out from the first node of the chain. It reads your node's API token from SEMURG_API_TOKEN: the installer prints it, and semurg.open() reads it for you.
import os
from semurg import Client, container
c = Client("http://localhost:4000", token=os.environ["SEMURG_API_TOKEN"])
# 1 -> 2 -> 3 -> 4, and a branch 2 -> 5
edges = [container.edge(1, 2), container.edge(2, 3), container.edge(3, 4), container.edge(2, 5)]
print("written", c.ingest(edges)["containers"])
walk = c.query("traverse", {"seeds": [1], "hops": 3})["result"]
print("levels", walk["witness"]["levels"])
print("nodes", walk["nodes_visited"], "edges", walk["edge_hops"])Expected: four edges written; levels 1, 1, 2, 1 (node 1, then 2, then 3 and 5, then 4); five nodes reached (node 1 included) over four edges
semurg.open() starts a local node with Docker and returns the same client, so on an x86_64 Linux machine with Docker the first line can be c = semurg.open(). A large graph goes in batches: each ingest call takes a list of edges, and the edges are checked before anything is sent (an edge built wrong is refused by its position in the list, never stored wrong).
What a walk answers
nodes_visited: the distinct nodes reached, the starting nodes included.edge_hops: the edges followed.witness.levels: how many nodes were first reached at each level, starting with the seeds, so you can see where the walk grew and where it stopped.levels_completeistruewhen every node of the cluster reported its counts;falsemeans the per-level counts are partial, and"elided"means the walk went too deep to list every level.
A walk takes up to 100000 starting nodes in one call ("seeds": [...]) and any depth ("hops"). Over HTTP it is one call:
curl -sS http://localhost:4000/v1/query \
-H "Authorization: Bearer $SEMURG_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"op":"traverse","seeds":[1],"hops":3}'Read a node's neighbours
neighbour_ids answers each node's outgoing neighbours, or only how many there are with "count_only": true:
curl -sS http://localhost:4000/v1/query \
-H "Authorization: Bearer $SEMURG_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"op":"neighbour_ids","ids":[2],"direction":"out"}'Each id in the answer says its state: its edges (an empty list for a node with none), a page of them when there are more than you asked for, a count with count_only, or absent when nothing was ever written under that id.
When something is refused
422: a malformed request, with the field named.413: more than 100000 starting nodes in one call.501: starting nodes that exist but cannot be walked on this release.503: the node is under its memory floor, or busy with other walks; retry.
What it does not do yet
- A walk answers counts and a witness per level, not the list of nodes it reached.
- No shortest-path door, and walks follow outgoing edges only.
- No graph query language (Cypher or GQL).
- Edges load as records built by the clients (
semurg.containerin Python), not as JSON.
Where it stands, and run it yourself
Graph is on the benchmark page against Neo4j, ArangoDB, FalkorDB, ArcadeDB and SurrealDB, each on all of the server's cores, in memory and on a graph far bigger than memory, with the release and the machine beside each figure. Graph on one server and a graph bigger than memory walk through those results. To run the same comparison on your machine:
curl -fsSL https://one.semurg.io/bench | sudo bash -s -- --graphThe runner loads the same graph into every engine, checks that each answers every question the same way, and only then times it.
Next
- Walking a graph to extreme depths: the deepest walks, every engine.
- Talk to a Semurg node over plain HTTP: the token and every route.
How these commands were checked
Our test farm ran each of the 1 command block marked runnable, exactly as written, with the answer checked against the expectation printed under it, against a fresh node of the published release r11.126.20261025.70c5311. A post is published only when every one of them passed there.