Subgraphs
A subgraph is a pattern-matched subset of a graph, extracted into a new, standalone Fallen-8 graph (its own engine instance) that you read, analyze, nest, and persist independently of its source. You give it optional vertex/edge pre-filters and an ordered, alternating vertex ↔ edge pattern; the engine keeps only the elements that lie on a matching path and prunes everything else. As everywhere in Fallen-8, there is no query language: every filter is a C# delegate (delegates), a code-free semantic threshold, or a stored template.
How extraction works
Section titled “How extraction works”Extraction runs the Breadth First Search Subgraph Algorithm plugin (plugins). PUT /subgraph carries no algorithm parameter, so it is the only subgraph algorithm reachable over REST; picking a different ISubGraphAlgorithm needs the embedded engine. The run has three phases:
- Copy vertices matching the top-level
vertexFilter(or all vertices, if omitted) into a fresh graph. - Copy edges matching the top-level
edgeFilter(or all), but only where both endpoints were copied in phase 1. - Prune to the pattern: find every path matching the ordered
patterns, then delete every vertex and edge not on at least one matching path. Paths are simple, revisiting an element closes a cycle and discards that path.
If patterns is empty, the copied elements (phases 1 to 2) are the result, unpruned. A syntactically valid pattern that matches nothing yields a registered empty subgraph (still 201), identically whether the source is empty or populated. A structurally invalid pattern is rejected with 400 (see the pattern rules below). The extract holds deep copies with its own ids, so it never mutates the source and can itself be the source of a nested subgraph.
REST surface
Section titled “REST surface”The bare paths below target the default namespace and also answer under /ns/{ns}/… (namespaces).
| Route | Effect | Notable responses |
|---|---|---|
PUT /subgraph |
Create + register from a specification. ?fromSubGraph=<name> sources it from an existing subgraph (nesting) instead of the whole graph. |
201 summary · 400 invalid spec/pattern, compile failure, or a stored query of the wrong kind · 401/403 (gating) · 404 unknown fromSubGraph/stored query · 409 duplicate name, quota, or the referenced stored query is not invocable · 413/429 body over 1 MiB / sensitive-endpoint rate limit · 500 |
GET /subgraph |
List registered subgraph names | 200 array of strings |
GET /subgraph/{name} |
Summary: element counts, algorithm, source id, canRecalculate, metadata, bound semantic |
200 · 404 |
GET /subgraph/{name}/graph |
The extracted vertices + edges. ?maxElements= caps each list (default 1000, clamped to 100000) |
200 { vertices, edges } · 404 |
POST /subgraph/{name}/recalculate |
Re-extract against the current source | 200 summary · 404 · 409 not recalculable |
DELETE /subgraph/{name} |
Deregister | 204 · 404 · 500 |
Gating: dynamic code execution is always on, so a request carrying inline fragments just needs the credential when a key is configured (security); a stored-query reference and the pure-data semantic block introduce no code either way.
Top-level body fields: name (required, the name it is registered under), the optional vertexFilter / edgeFilter pre-filters, patterns, and the code-free storedQuery / semantic alternatives (all four described below), plus additionalInformation, a string→string metadata map stored on the definition, persisted in the recipe, and echoed on every summary.
The pattern model
Section titled “The pattern model”patterns is an ordered list that must alternate vertex ↔ edge and end with a vertex; anything else is rejected with 400. Start it with a vertex. A fixed-length Edge is also accepted as the first entry (the engine then seeds source→edge→target triples from it, honouring that step’s edgePropertyFilter exactly as a deeper step would), and a VariableLengthEdge may never lead. Each entry:
| Field | Applies to | Meaning |
|---|---|---|
type |
all | Vertex, Edge, or VariableLengthEdge (default Vertex) |
patternName |
all | Optional label for the step (e.g. reported in the semantic summary) |
vertexFilter |
Vertex |
Fragment over the vertex |
semanticMinScore |
Vertex |
Code-free alternative to vertexFilter; owns the same slot (setting both is 400, as is setting it on an edge step or without a request-level semantic block) |
direction |
edge | OutgoingEdge (default), IncomingEdge, or UndirectedEdge |
edgePropertyFilter |
edge | Fragment over the edge’s type (its edgePropertyId, a string), checked before the edge itself |
edgeFilter |
edge | Fragment over the edge |
minLength / maxLength |
VariableLengthEdge |
Hop range, inclusive (both default 1). A maxLength above 100, or a minLength greater than maxLength, is 400 |
The filter slots (plus the top-level vertexFilter / edgeFilter pre-filters) compile to these delegate types. Each fragment is a return statement over a single-parameter lambda; the full contract (parameter members, property access, the semantic context) lives in delegates.
| Slot | Delegate | Fragment |
|---|---|---|
vertexFilter (pattern + top-level) |
VertexFilter |
return (v) => …;: v is a VertexModel |
edgeFilter (pattern + top-level) |
EdgeFilter |
return (e) => …;: e is an EdgeModel |
edgePropertyFilter |
EdgePropertyFilter |
return (p) => …;: p is the edge’s type (edgePropertyId) string |
An omitted or empty fragment matches everything.
Variable-length semantics: the edge filters apply to every hop, but the vertex pattern following a variable-length edge constrains only the terminal vertex of the matched path, intermediate vertices are unconstrained. Model an intermediate-vertex constraint with explicit fixed-length edge/vertex pairs instead.
Code-free alternatives: invoke a registered subgraph template with "storedQuery": "<name>" in place of all inline fragments (stored queries); or add a semantic block that scores vertices by embedding similarity, minScore is a code-free top-level vertex pre-filter and per-step semanticMinScore is its pattern-level twin (semantic traversal). Each is mutually exclusive with the fragments it replaces, and the two are mutually exclusive with each other: a semantic block on a storedQuery invocation is 400, because a stored template’s delegates are pinned at registration. Inline the filters when you need a fresh query vector.
Worked example
Section titled “Worked example”Match person -knows-> person, keeping only those people and their knows edges (the illustration’s pruning):
curl -X PUT http://localhost:8080/subgraph \ -H "Content-Type: application/json" \ -d '{ "name": "people-who-know-people", "vertexFilter": "return (v) => v.Label == \"person\";", "patterns": [ { "type": "Vertex", "patternName": "p1", "vertexFilter": "return (v) => v.Label == \"person\";" }, { "type": "Edge", "patternName": "knows", "direction": "OutgoingEdge", "edgePropertyFilter": "return (p) => p == \"knows\";" }, { "type": "Vertex", "patternName": "p2", "vertexFilter": "return (v) => v.Label == \"person\";" } ] }'$spec = @{ name = "people-who-know-people" vertexFilter = 'return (v) => v.Label == "person";' patterns = @( @{ type = "Vertex"; patternName = "p1"; vertexFilter = 'return (v) => v.Label == "person";' } @{ type = "Edge"; patternName = "knows"; direction = "OutgoingEdge"; edgePropertyFilter = 'return (p) => p == "knows";' } @{ type = "Vertex"; patternName = "p2"; vertexFilter = 'return (v) => v.Label == "person";' } )} | ConvertTo-Json -Depth 5Invoke-RestMethod -Method Put -Uri http://localhost:8080/subgraph -ContentType "application/json" -Body $specThe 201 body is a summary: { "name": "people-who-know-people", "vertexCount": 3, "edgeCount": 2, "algorithmPluginName": "Breadth First Search Subgraph Algorithm", "canRecalculate": true, … }. Read the extracted elements back:
curl "http://localhost:8080/subgraph/people-who-know-people/graph?maxElements=500"Invoke-RestMethod -Uri "http://localhost:8080/subgraph/people-who-know-people/graph?maxElements=500"{ "vertices": [ // creationDate / modificationDate are on every element too, elided here { "id": 1, "label": "person", "properties": [ { "propertyId": "name", "propertyValue": "Alice", "fullQualifiedTypeName": "System.String" } ], "outEdges": { "knows": [10] }, "inEdges": null } // null, not {}, when the vertex has no incoming edges ], "edges": [ { "id": 10, "edgePropertyId": "knows", "sourceVertex": 1, "targetVertex": 2 } ]}Ids are the subgraph’s own, not the source’s.
Recalculation, nesting, persistence
Section titled “Recalculation, nesting, persistence”- Recalculate (
POST …/recalculate) re-runs the extraction against the current state of the source and swaps in the fresh result; the subgraph’s id and name are preserved, its contents fully replaced. Only a subgraph that retains its source and algorithm (canRecalculate: true: every subgraph created over REST) can be recalculated; a subgraph registered directly from delegates in the embedded engine cannot (409). - Nesting: with
?fromSubGraph=<parent>the source is another subgraph, so the pattern runs over the parent’s extracted graph. The dependency is tracked, so the whole tree rebuilds in dependency order on load.POST …/recalculatedoes not cascade: it refreshes only the named subgraph. Recalculating a parent therefore leaves its children’s contents untouched, but a child is re-bound to the parent’s new extraction, so recalculating the child afterwards reads the parent’s current contents rather than a discarded instance. - Persistence: every REST-created subgraph attaches a recipe (its materialized specification) that survives
PUT /save/load and write-ahead-log crash recovery, rebuilt against the current graph in dependency order (save games). Subgraphs created directly from delegates have no recipe and are not persisted.
Limits (per namespace, fixed): 1024 subgraphs, 10,000,000 elements per subgraph, and 25,000,000 elements summed across all subgraphs. A breach answers 409. All three are checked when a subgraph is created, when a recipe is rehydrated on load, and on POST …/recalculate: a refresh whose fresh extraction would breach a ceiling answers 409 and the subgraph keeps its previous, in-quota contents, so it goes stale rather than over budget.
See also
Section titled “See also”- Delegates: the no-query-language model, fragment shape, compilation, validation
- Stored queries: register a subgraph template once, invoke by name with no code
- Semantic traversal: the
semanticblock and score thresholds - Security: the API key that gates access to these always-on code endpoints
- Save games: checkpoints, the write-ahead log, and rebuild-on-load
- Namespaces: per-namespace isolation and the
/ns/{ns}/…route twins - Graph model: vertices, edges, properties, and transactions
- Plugins: the subgraph algorithm plugin system
- REST API: the OpenAPI document and Scalar reference