OriginChain docs
reference · graph

Graph queries

Graph queries walk relationships between rows. Examples: "all customers a supplier sells to", "friends of friends", "shortest path from order to fulfillment center". You declare which columns are relationships on the schema, then the graph endpoints walk them.

Edges are not stored separately - they're a view over your existing row columns. Writing a row creates / updates / removes the edges automatically. See Schemas → relations for the declaration.

1. Declare a relation.

Add a [[relations]] block to the schema of the table that holds the foreign-key column. After registering this schema, every row write creates the edges automatically.

# manifest.toml - declare the relation on the table holding the FK column.
# The reverse edge is written atomically when bidirectional = true.
namespace   = "social"
table       = "follows"
primary_key = ["id"]

[[columns]]
name = "id"
ty   = "str"
required = true

[[columns]]
name = "follower_id"
ty   = "str"
required = true

[[columns]]
name = "followee_id"
ty   = "str"
required = true

[[relations]]
name          = "followee"          # the verb you walk in queries
from_col      = "followee_id"       # column on THIS table
bidirectional = true

[relations.target]
namespace = "social"
table     = "users"
pk        = "id"

Self-relations work - direction tags in the key prevent collisions. See Schemas → relations for the full reference.

2. One-hop neighbors.

what this does

Return the primary keys of every row directly connected to a given row through a relation. The fastest graph query - just an adjacency list lookup.

GET /v1/tenants/:t/graph/:schema/neighbors
curl "https://$OC_HOST/v1/tenants/$OC_TENANT/graph/social.follows/neighbors?rel=followee&pk=u001" \
  -H "Authorization: Bearer $OC_TOKEN"

For inbound edges (who points at this row?), use the parallel /reverse endpoint with the same params.

3. BFS - multi-hop traversal.

what this does

Walk every node reachable within max_depth hops, breadth-first. Each result carries the hop distance.

GET /v1/tenants/:t/graph/:schema/bfs
curl "https://$OC_HOST/v1/tenants/$OC_TENANT/graph/social.follows/bfs?rel=followee&pk=u001&max_depth=3" \
  -H "Authorization: Bearer $OC_TOKEN"
common mistakes
  • Forgetting max_depth. On a connected graph, BFS without a depth cap can return millions of nodes. Set max_depth to the smallest value that gives you what you need.
  • Using BFS when you just want reachability. If you only need to know whether two nodes are connected, use /path - it short-circuits on first match.

4. Shortest path (Dijkstra).

what this does

Find the lowest-cost path from src to dst. You provide a map of edge weights; the engine returns the total cost (or null if unreachable).

GET /v1/tenants/:t/graph/:schema/dijkstra
# weights_json maps "from→to" → cost. Empty {} = every edge weight 1.
curl "https://$OC_HOST/v1/tenants/$OC_TENANT/graph/social.follows/dijkstra?rel=followee&src=u001&dst=u042&weights_json=%7B%7D" \
  -H "Authorization: Bearer $OC_TOKEN"

For top-K paths instead of the single shortest, use /k-shortest (Yen's algorithm). For unweighted shortest path, use /path.

5. All 19 algorithms.

Every graph endpoint, its wire shape, and what it's good for. Centrality and community algorithms (betweenness, eigenvector, label-propagation, triangles, louvain) require same-schema relations - cross-schema is not yet supported.

operation signature use
neighbors GET /graph/:schema/neighbors?rel=&pk= One-hop forward - downstream of a node.
reverse GET /graph/:schema/reverse?rel=&pk= One-hop inbound - who points TO this node?
bfs GET /graph/:schema/bfs?rel=&pk=&max_depth= Breadth-first frontier up to a depth.
path GET /graph/:schema/path?rel=&src=&dst=&max_depth= Reachability check - short-circuits on first match.
all_simple_paths GET /graph/:schema/all_simple_paths?rel=&src=&dst=&max_depth= Every node-disjoint path. Hard-capped output.
dijkstra GET /graph/:schema/dijkstra?rel=&src=&dst=&weights_json= Weighted shortest path.
k-shortest GET /graph/:schema/k-shortest?rel=&source=&target=&k= Top-K loop-free paths in increasing weight.
pagerank GET /graph/:schema/pagerank?rel=&nodes= Influence ranking via power iteration.
triangles GET /graph/:schema/triangles?rel=&nodes= Per-node triangle count. Clustering signal.
components GET /graph/:schema/components?rel=&nodes= Connected components via Union-Find.
louvain GET /graph/:schema/louvain?rel=&nodes= Modularity-greedy community detection.
betweenness GET /graph/:schema/betweenness?rel=&nodes= Brandes' betweenness - bridge identification.
eigenvector_centrality GET /graph/:schema/eigenvector_centrality?rel=&nodes= Influence by who-you're-connected-to.
label_propagation GET /graph/:schema/label_propagation?rel=&nodes= Fast soft community detection.
random-walk GET /graph/:schema/random-walk?rel=&pk=&steps=&seed= Uniform or Node2Vec-biased walks.
node2vec POST /graph/:schema/node2vec { rel, dim, ..., persist } Train graph embeddings.
node2vec/topk GET /graph/:schema/node2vec/:rel/topk?pk=&k= Find similar nodes via Node2Vec embeddings.
graphsage POST /graph/:schema/graphsage { rel, dim, ..., feature_col, persist } Attribute-aware embeddings (Hamilton 2017).
graphsage/topk GET /graph/:schema/graphsage/:rel/topk?pk=&k= Find similar nodes via GraphSAGE.