# SQL API Reference This page documents the SQL surface implemented by the current `graph` crate. All functions live in the `graph` schema. ## Function Index | Category | Functions | |---|---| | Availability | `test_enabled`, `status`, `memory_profile`, `sync_health`, `reset` | | Registration | `add_table`, `add_edge`, `add_filter_column`, `registered_tables`, `registered_edges`, `remove_table`, `remove_edge`, `estimate` | | Discovery | `auto_discover`, `auto_discover_tables` | | Build jobs | `build`, `build_status` | | Sync and maintenance | `enable_sync`, `apply_sync`, `run_scheduled_maintenance`, `vacuum`, `maintenance`, `maintenance_status` | | GQL | `gql`, `gql_explain` | | Search | `search`, `search_nodes` | | Traversal | `traverse`, `traverse_search`, `node_ref`, `node_ref_string`, `format_path` | | Filters | `equals`, `eq`, `not_equals`, `neq`, `greater_than`, `gt`, `at_least`, `gte`, `less_than`, `lt`, `at_most`, `lte`, `between`, `on_node`, `all` | | Workflow | `find`, `expand`, `find_related`, `path`, `connection`, `neighborhood` | | Paths | `shortest_path`, `weighted_shortest_path` | | Aggregation | `path_count_estimate`, `aggregate` | | Components | `connected_components`, `component_stats`, `components`, `largest_component`, `component`, `isolated_nodes` | ## Availability ### `graph.test_enabled()` ```sql graph.test_enabled() RETURNS boolean ``` Returns the current `graph.enabled` value. ### `graph.status()` ```sql graph.status() RETURNS TABLE ( node_count int, edge_count int, memory_used_mb double precision, memory_limit_mb int, sync_mode text, sync_status text, last_build timestamptz, last_vacuum timestamptz, edge_types text[], edge_buffer_used int, has_unidirectional_edges boolean, schema_status text, sync_lag bigint, pending_edge_deltas int, needs_vacuum boolean, needs_rebuild boolean, applied_sync_id bigint, pending_sync_rows bigint, invalid_reason text, disabled_trigger_count int, read_only boolean, read_only_reason text, projection_mode text, overlay_tombstone_count int, overlay_memory_bytes bigint, compaction_recommended boolean, tx_delta_dirty boolean, tx_delta_added_nodes int, tx_delta_deleted_nodes int, tx_delta_added_edges int, tx_delta_deleted_edges int, tx_delta_memory_bytes bigint ) ``` Reports backend-local engine state and current catalog/sync validity. When `read_only` is true, `read_only_reason` is `memory_limit`, `edge_buffer_full`, or another typed reason. `projection_mode` is `csr_readonly` or `mutable_overlay`. Overlay columns summarize tombstones, memory, and durable compaction policy; `edge_buffer_used` plus `tx_delta_dirty` show whether pending sync or transaction-local overlays exist. ### `graph.memory_profile()` ```sql graph.memory_profile(concurrent_backends int DEFAULT 1) RETURNS TABLE ( active_backend_private_mb double precision, active_backend_shared_mb double precision, active_backend_total_mb double precision, estimated_instance_private_mb double precision, estimated_instance_shared_mb double precision, estimated_instance_total_mb double precision, memory_limit_mb int, assumed_concurrent_backends int ) ``` Reports sizing estimates for the current backend's active engine. Shared mmap bytes are counted once; backend-private heap is multiplied by `concurrent_backends`, with a minimum of one. This is an operator sizing aid, not a live connection counter or cluster-wide hard cap. ### `graph.sync_health()` ```sql graph.sync_health() RETURNS TABLE ( sync_mode text, query_freshness text, sync_batch_size int, applied_sync_id bigint, max_sync_log_id bigint, pending_sync_rows bigint, disabled_trigger_count int, edge_buffer_used int, edge_buffer_size int, needs_vacuum boolean, needs_rebuild boolean, read_only boolean, read_only_reason text, projection_mode text, overlay_tombstone_count int, overlay_memory_bytes bigint, compaction_recommended boolean, tx_delta_dirty boolean, tx_delta_added_nodes int, tx_delta_deleted_nodes int, tx_delta_added_edges int, tx_delta_deleted_edges int, tx_delta_memory_bytes bigint, apply_sync_recommended boolean, maintenance_recommended boolean ) ``` Reports the sync and compaction signals most useful to schedulers and external monitors. Use `apply_sync_recommended` to decide when a lightweight replay is useful for a writable backend-local engine, and `maintenance_recommended` to decide when overlay/read-only/rebuild state should be folded back into the base graph. ### `graph.reset()` ```sql graph.reset() RETURNS void ``` Admin-only. Clears the backend-local engine and removes persisted graph files. ## Registration ### `graph.add_table()` ```sql graph.add_table( table_name regclass, id_column text, columns text[] DEFAULT NULL, tenant_column text DEFAULT NULL ) RETURNS void ``` ```sql graph.add_table( table_name regclass, id_columns text[], columns text[] DEFAULT NULL, tenant_column text DEFAULT NULL ) RETURNS void ``` Admin-only. Registers a source table as graph nodes. The identifier columns must match a primary key or unique `NOT NULL` index. ### `graph.add_edge()` ```sql graph.add_edge( from_table regclass, from_column text, to_table regclass, to_column text, label text, bidirectional boolean DEFAULT true, weight_column text DEFAULT NULL, label_column text DEFAULT NULL ) RETURNS void ``` Admin-only. Registers a relationship. `weight_column` must be numeric. `label_column`, when supplied, provides per-row dynamic edge labels. A graph can use up to 254 distinct user-facing edge labels; registering or building a graph that would require another label fails with `PG004`. `graph.add_edge()` supports two registration modes: - FK-style edges: when `from_table` is already registered with `graph.add_table()`, each source row becomes an edge from that row's registered primary key to the value in `from_column`. In this mode, `to_column` names the target table primary-key column on `to_table`. - Edge-table relationships: when `from_table` is not a registered node table, it is treated as a table of relationship rows. In this mode, both endpoint values are read from `from_table`: `from_column` is the source endpoint value and `to_column` is the target endpoint value. For edge-table relationships, `to_column` must exist on the source edge table. For FK-style edges, `to_column` must exist on the target table. Many-to-many junction tables use edge-table mode. For example, `graph.add_edge('public.follows', 'follower', 'public.users', 'followee', ...)` registers `follows.follower -> follows.followee`. `to_table` is the preferred target node table when it is registered; otherwise endpoint resolution falls back across the registered node tables. Use the target table primary-key column, such as `'id'`, only for FK-style edges from a registered source node table. ### `graph.add_filter_column()` ```sql graph.add_filter_column( table_name regclass, column_name text, column_type text DEFAULT 'numeric' ) RETURNS void ``` Admin-only. Supported types are `numeric`, `text`, `boolean`, `date`, `timestamptz`, and `uuid`. ### `graph.registered_tables()` ```sql graph.registered_tables() RETURNS TABLE ( table_name text, id_columns text[], columns text[], tenant_column text ) ``` ### `graph.registered_edges()` ```sql graph.registered_edges() RETURNS TABLE ( from_table text, from_column text, to_table text, to_column text, label text, bidirectional boolean, weight_column text, label_column text ) ``` ### `graph.remove_table()` ```sql graph.remove_table(table_name regclass) RETURNS void ``` Admin-only. Removes table registration, associated filter columns, and edges where the table is either endpoint. Rebuild after calling. ### `graph.remove_edge()` ```sql graph.remove_edge(label text) RETURNS void ``` Admin-only. Removes edge registrations with the given label. Rebuild after calling. ### `graph.estimate()` ```sql graph.estimate() RETURNS TABLE ( estimated_nodes bigint, estimated_edges bigint, estimated_memory_mb double precision, memory_limit_mb int, fits_in_memory boolean ) ``` Estimates graph size from `pg_class.reltuples`. ## Discovery ### `graph.auto_discover()` ```sql graph.auto_discover(schema_name text DEFAULT 'public') RETURNS TABLE ( item_type text, item_name text, details text ) ``` Admin-only. Discovers tables and FK edges in a schema, registers them, then calls `graph.build()`. ### `graph.auto_discover_tables()` ```sql graph.auto_discover_tables( tables regclass[], tenant_column text DEFAULT NULL ) RETURNS TABLE ( item_type text, item_name text, details text ) ``` Admin-only. Discovers selected tables and FK edges only among those tables, then calls `graph.build()`. ## Build Jobs ### `graph.build()` ```sql graph.build() RETURNS TABLE ( nodes_loaded bigint, edges_loaded bigint, build_time_ms double precision, memory_used_mb double precision, sync_mode text, projection_mode text ) ``` Admin-only. Builds the graph synchronously using `graph.default_projection_mode`. ```sql graph.build(mode text) RETURNS TABLE ( nodes_loaded bigint, edges_loaded bigint, build_time_ms double precision, memory_used_mb double precision, sync_mode text, projection_mode text ) ``` Admin-only. Builds the graph synchronously with `mode` set to `csr_readonly` or `mutable_overlay`. `mutable_overlay` requires `graph.mutable_enabled = on`. ```sql graph.build(concurrently boolean) RETURNS TABLE ( build_id text, status text, nodes_loaded bigint, edges_loaded bigint, build_time_ms double precision, memory_used_mb double precision, sync_mode text, projection_mode text ) ``` Admin-only. `false` runs the compatibility synchronous path. `true` creates a build job and attempts to launch a dynamic background worker. ### `graph.build_status()` ```sql graph.build_status(build_id text) RETURNS TABLE ( build_id text, status text, nodes_loaded bigint, edges_loaded bigint, build_time_ms double precision, memory_used_mb double precision, progress_phase text, progress_message text, started_at timestamp with time zone, finished_at timestamp with time zone, error text ) ``` Reads durable build job state, or returns `completed`/`not_found` for the special synchronous zero UUID path. ## Sync And Maintenance ### `graph.enable_sync()` ```sql graph.enable_sync() RETURNS void ``` Admin-only. Creates durable sync tables if needed and installs graph triggers on registered tables. ### `graph.apply_sync()` ```sql graph.apply_sync() RETURNS TABLE ( inserts_applied bigint, updates_applied bigint, deletes_applied bigint ) ``` Admin-only. Applies pending sync rows to the backend-local engine. ### `graph.run_scheduled_maintenance()` ```sql graph.run_scheduled_maintenance() RETURNS TABLE ( applied_sync boolean, maintenance_started boolean, maintenance_job_id text, pending_sync_rows bigint, edge_buffer_used int, message text ) ``` Admin-only. Applies pending sync when `graph.sync_health()` says it is safe, then starts `graph.maintenance(concurrently := true)` when refreshed health still recommends compaction or rebuild work. pgGraph does not run an internal scheduler; call this function from `pg_cron` or an external scheduler such as a Kubernetes CronJob, systemd timer, Docker init schedule, or application scheduler. ### `graph.vacuum()` ```sql graph.vacuum() RETURNS TABLE ( nodes_before bigint, nodes_after bigint, tombstones_removed bigint, edges_rebuilt bigint, vacuum_time_ms double precision ) ``` Admin-only. Rebuilds from source tables and swaps in the rebuilt backend-local engine; persistence writes the rebuilt base graph when requested by the caller. ### `graph.maintenance()` ```sql graph.maintenance(concurrently boolean DEFAULT false) RETURNS TABLE ( job_id text, status text, sync_rows_applied bigint, nodes_after bigint, edges_after bigint, vacuum_time_ms double precision, error text ) ``` Admin-only. Foreground path applies maintenance rebuild immediately. Background path creates a durable maintenance job and launches a dynamic worker. ### `graph.maintenance_status()` ```sql graph.maintenance_status(job_id text DEFAULT NULL) RETURNS TABLE ( job_id text, status text, sync_rows_applied bigint, nodes_after bigint, edges_after bigint, vacuum_time_ms double precision, progress_phase text, progress_message text, started_at timestamp with time zone, finished_at timestamp with time zone, error text ) ``` With `NULL`, returns the latest 50 jobs. With a job ID, returns that job or a single `not_found` row. ## GQL ### `graph.gql()` ```sql graph.gql( query text, params jsonb DEFAULT NULL, hydrate boolean DEFAULT true ) RETURNS TABLE (row jsonb) ``` Executes the documented GQL-compatible subset over the active graph. Each output row is a JSONB object keyed by the `RETURN` aliases or expressions. Node labels resolve to registered table names, relationship types resolve to registered edge labels, and `$parameters` are read from the `params` JSON object. See [GQL JSONB result format](/user_guide/querying#gql-jsonb-result-format) for the stable node, relationship, path, scalar, and null shapes returned to SQL clients and SDKs. Read pattern support: - Node-only `MATCH`. - Single-relationship graph patterns. - Comma-separated fixed single-hop patterns with reused node variables, such as `MATCH (u:users)-[:works_at]->(c:companies), (v:users)-[:works_at]->(c)`, including fixed single-hop path variables. - Fixed path variables: `MATCH p=()-[]->()`, `MATCH p=(s)-[r]->(e)`, `MATCH p=()<-[]-()`, `MATCH p=()-[]-()`, and `MATCH p=(a)-[]->(b)-[]->(c)`. - Top-level single-relationship `OPTIONAL MATCH`. Optional matches return matched relationship rows, and emit one null-extended row for each source node with no qualifying target. Null-extended rows use JSON `null` for target nodes, target properties, and relationship values. Return value support: - Node values and scalar properties. - Node values, scalar properties, and fixed single-hop relationship variables from supported multi-pattern joins. - JSONB list/map properties, including dotted paths such as `u.profile.plan`. - Coordinate-only single-hop relationship identities. - Bounded relationship path values with a minimum of at least one hop. - Fixed path values, including named node variables and single-hop named relationship variables. - Fixed single-hop path values from supported multi-pattern joins. - Path functions over fixed single-hop path values from supported multi-pattern joins. - `RETURN DISTINCT` for non-wildcard path reads, including projected rows from supported multi-pattern joins. - `RETURN` aggregates: `count`, `sum`, `avg`, `min`, `max`, and `collect`. JSONB arrays and objects keep their JSON shape. A missing key inside a JSONB property path projects as JSON `null`, but it does not satisfy `IS NULL`; explicit JSON `null` does. Aggregate behavior: - Non-aggregate return items are implicit grouping keys. - `count(*)` counts every input row. - `count(expr)`, `sum`, `avg`, `min`, `max`, and `collect` ignore JSON `null` inputs. - Aggregate-only empty inputs return one row with `0`, `null`, or `[]` according to the aggregate. - Aggregate `DISTINCT` is supported. - Multi-pattern aggregate reads support `count(*)`, node-value `count`/`collect`, relationship-value and path-value `count`/`collect`, and node-property `count`, `sum`, `avg`, `min`, `max`, and `collect`. Wildcard path-variable reads support `WHERE`, `RETURN`, `SKIP`, and `LIMIT` in this phase. They can bind node variables across fixed paths, bind relationship variables for single-hop paths, and use concrete node labels or concrete relationship types as filters, including type alternation such as `p=()-[:friend|works_at]->()`. A single wildcard segment may use bounded variable length, such as `p=(s)-[r*1..3]->(e)`. Multi-segment wildcard paths may also include bounded variable-length segments when those segments do not bind segment-local node or relationship variables. Named relationship variables on single bounded segments are path values, and named target nodes bind to the emitted endpoint. Variable-length wildcard paths use bounded walk semantics: nodes and relationships may repeat within the explicit maximum hop bound, and row caps are still enforced before projection. `WHERE` can read properties from named path-node variables only when every possible concrete table for that variable exposes the same registered property or JSONB property path, including the same registered nested paths under a JSONB root. They reject `ORDER BY`, `RETURN DISTINCT`, `WITH`, aggregates, optional matches, writes, relationship properties, relationship variables in multi-segment paths, named target variables on multi-segment variable-length boundaries, and relationship type alternation outside wildcard path-variable reads. Multi-pattern reads support fixed single-hop patterns with concrete labels and relationship types. Reused node variables join by graph coordinate, independent patterns produce Cartesian combinations up to the GQL result cap, joined node-property `WHERE` predicates filter after all referenced slots are bound, fixed single-hop relationship variables can be returned as coordinate identity objects, fixed single-hop path variables can be returned as path JSON objects, `nodes(path)`, `relationships(path)`, and `length(path)` can project from fixed single-hop path variables, node, node-property, relationship-value, and path-value aggregate inputs can be grouped over joined rows, `WITH` can project node, node-property, relationship, path, path-function, and grouped aggregate aliases over supported aggregate inputs between a multi-pattern `MATCH` and `RETURN`, and `WITH DISTINCT` can deduplicate non-aggregate projected row streams or mark direct aggregate projection clauses distinct, later `WITH DISTINCT` can deduplicate projected aggregate aliases, final aggregate expressions can read projected aggregate aliases after an aggregate `WITH` boundary, `ORDER BY` can sort joined node properties or returned property aliases, and `RETURN DISTINCT`, `SKIP`, and `LIMIT` apply after projection. Distinct multi-pattern reads must order by returned scalar expressions. Optional multi-pattern reads null-extend unmatched fixed single-hop pattern expansions and return JSON `null` for missing node, relationship, and path projections. Bounded variable-length relationship patterns can participate in multi-pattern joins for node and path projections. Variable-length relationship variables are treated as path values. Relationship properties, relationship/path property aggregate inputs, and numeric aggregates over relationship/path values remain rejected. Path behavior: - Named relationship patterns can be returned as path objects with `_path.nodes` and `_path.relationships`. - Fixed path variables return the same path object shape while scanning all registered edge labels or applying the concrete labels/types in the pattern. - `nodes(path)`, `relationships(path)`, and `length(path)` return those components directly. Projection and deduplication: - `WITH` introduces projection stages over the matched row stream. - Downstream `RETURN` and `ORDER BY` clauses see projected aliases, including shadowed names. - `WITH DISTINCT` deduplicates the row stream at that projection boundary. - DISTINCT forms use a bounded in-memory key set and abort if the unique-key cap is exceeded. - `RETURN DISTINCT` queries must order by returned scalar expressions. Hydration behavior: - `hydrate := true` returns hydrated source rows for node values when PostgreSQL ACL and RLS allow them. - If a required graph coordinate cannot be read from its backing source table, `graph.gql()` raises `PG017` rather than returning a partial empty node object. - Queries that only need identifiers should project identifier properties, such as `RETURN n.id AS id`, rather than returning the whole node. - `hydrate := false` returns coordinate-only node objects for callers that explicitly want the compact `{table, id}` shape. Not included in this release's GQL subset: - Relationship source-row hydration. - Multi-pattern relationship/path property aggregate inputs, numeric aggregates over relationship/path values, and relationship properties. - Node-only optional matches. `CREATE (n:label {properties}) RETURN ...` is supported for a single node whose label maps to a registered source table. It requires a `mutable_overlay` projection and inserts the PostgreSQL source row first, so table privileges, constraints, triggers, RLS, and MVCC remain authoritative. `CREATE` is rejected on `csr_readonly`. The same transaction can read created nodes through node-only `MATCH`. Relationship traversal, multi-pattern joins, and wildcard path reads reject transaction-created nodes when they would be used as traversal entry points until temporary graph node indexes are available for path expansion. `MATCH (n:label {...}) SET n.property = value RETURN ...` is supported for a single matched node and one registered non-key, non-tenant property column. It requires a `mutable_overlay` projection, updates the PostgreSQL source row first, and refreshes the backend's typed filter index value when the property is also a registered filter column. PostgreSQL rejects type mismatches according to the source column type. Before updating, pgGraph locks the matched source row and re-checks the matched GQL predicate against the current PostgreSQL row. `MATCH (n:label {...}) REMOVE n.property RETURN ...` is supported for a single matched node and one registered non-key, non-tenant property. It requires a `mutable_overlay` projection, updates the PostgreSQL source row first, and sets scalar columns to SQL `NULL`. For registered dotted JSONB property paths, `REMOVE` drops the addressed key and missing keys remain missing, so repeated removes are idempotent. Removing labels is rejected because labels map to registered source tables. Before updating, pgGraph locks the matched source row and re-checks the matched GQL predicate against the current PostgreSQL row. `MATCH (a:label)-[r:type]->(b:label) DELETE r RETURN ...` is supported for a single matched relationship backed by a registered edge row table. Endpoint labels and the relationship type may be omitted only when the wildcard pattern resolves to exactly one static-label registered edge-row mapping; ambiguous wildcard delete patterns and dynamic relationship label-column mappings are rejected before execution. It requires a `mutable_overlay` projection, deletes exactly one PostgreSQL edge row first, then tombstones the graph edge in the current backend. Bidirectional edge registrations hide both neighbor directions after the delete. Endpoint nodes are not deleted. Undirected relationship `DELETE` is not part of this release's GQL subset. Before deleting the edge row, pgGraph locks the matched endpoint source rows and re-checks the matched GQL predicate against the current PostgreSQL rows. `MATCH (n:label {...}) DETACH DELETE n RETURN ...` is supported for a single matched node. It requires a `mutable_overlay` projection, deletes registered incident edge-row relationships first, then deletes the PostgreSQL node row. The current backend records transaction-local edge and node tombstones so later reads in the same transaction do not see the deleted topology. Incident relationships must be backed by registered edge row tables. Relationship creation and multi-row writes are not part of this release's GQL subset. Before deleting incident edge rows or the node row, pgGraph locks the matched node source row and re-checks the matched GQL predicate against the current PostgreSQL row. `MERGE (n:label {properties}) [ON CREATE SET ...] [ON MATCH SET ...] RETURN ...` is supported for a single node whose label maps to a registered source table. It requires a `mutable_overlay` projection and uses the registered primary key columns as the match identity. Existing rows are locked with `FOR UPDATE` only when `ON MATCH SET` can update them; new rows are inserted with PostgreSQL `ON CONFLICT DO NOTHING` on that identity. `ON CREATE` expressions are evaluated only for inserted rows, and `ON MATCH` expressions are evaluated only for matched rows. `MERGE` requires source-table `SELECT` and `INSERT`; `UPDATE` is required only for statements that include `ON MATCH SET`. ### `graph.gql_explain()` ```sql graph.gql_explain(query text) RETURNS text ``` Parses, binds, and lowers a GQL query and returns stable explain text for the frontend, logical-plan, and physical-plan stages. It performs the same catalog binding and ACL preflight as `graph.gql()` but does not execute the plan. ### `graph.cypher()` ```sql graph.cypher( query text, params jsonb DEFAULT NULL, hydrate boolean DEFAULT true ) RETURNS TABLE(row jsonb) ``` Executes the openCypher compatibility subset. This function is separate from `graph.gql()` and intentionally does not claim full openCypher compatibility. Supported overlapping read patterns and mapped writes lower into the same shared IR as GQL, so the same PostgreSQL catalog registration, ACL, RLS, constraint, trigger, MVCC, and mutable-overlay rules apply. Cypher-only procedure, DDL, import, and control-flow constructs are rejected with the GQL unsupported-feature SQLSTATE. ### `graph.cypher_explain()` ```sql graph.cypher_explain(query text) RETURNS text ``` Parses, binds, and lowers an openCypher-compatible query without executing it. The explain output is the same physical-plan shape used by `graph.gql_explain()` for equivalent supported queries. ### `graph.cypher_compatibility()` ```sql graph.cypher_compatibility() RETURNS TABLE(feature text, status text, notes text) ``` Returns the current openCypher compatibility matrix. Use it to distinguish supported overlapping syntax from rejected Cypher-only features and from the explicit non-claim of full openCypher compatibility. ## Search ### `graph.search()` ```sql graph.search( property_key text, property_value text, table_filter regclass DEFAULT NULL, mode text DEFAULT 'contains', case_sensitive boolean DEFAULT false, max_rows int DEFAULT 100, row_offset int DEFAULT 0, tenant text DEFAULT NULL, hydrate boolean DEFAULT true ) RETURNS TABLE ( node_table oid, node_id text, match_type text, score real, verified boolean, node jsonb, node_table_name text ) ``` Searches registered source-table columns. Results are deduplicated by `(node_table, node_id)`, ordered by table OID then node ID, and paginated after the global merge. Per-table source scans are bounded to `row_offset + max_rows` candidates. ### `graph.search_nodes()` ```sql graph.search_nodes( property_key text, property_value text, table_filter regclass DEFAULT NULL, mode text DEFAULT 'contains', case_sensitive boolean DEFAULT false, max_rows int DEFAULT 100, row_offset int DEFAULT 0, tenant text DEFAULT NULL ) RETURNS TABLE ( node_table oid, node_id text, match_type text, score real, verified boolean, node_table_name text ) ``` Coordinate-only search. ## Traversal ### `graph.node_ref` ```sql CREATE TYPE graph.node_ref AS ( node_table regclass, node_id text ); graph.node_ref(node_table regclass, node_id text) RETURNS graph.node_ref ``` ### `graph.node_ref_string()` ```sql graph.node_ref_string(table_name regclass, node_id text) RETURNS text ``` Returns canonical JSON text for strict traversal specs. ### `graph.traverse()` single-start ```sql graph.traverse( seed_table regclass, seed_id text, max_depth int DEFAULT current_setting('graph.default_max_depth')::int, edge_types text[] DEFAULT NULL, direction text DEFAULT 'any', node_tables oid[] DEFAULT NULL, filter jsonb DEFAULT NULL, tenant text DEFAULT NULL, strategy text DEFAULT 'bfs', uniqueness text DEFAULT 'node_global', include_start boolean DEFAULT true, hydrate boolean DEFAULT true, max_rows int DEFAULT 1000, row_offset int DEFAULT 0, max_nodes int DEFAULT current_setting('graph.max_nodes')::int, max_frontier int DEFAULT current_setting('graph.max_frontier')::int ) RETURNS TABLE ( root_table oid, root_id text, node_table oid, node_id text, depth int, path jsonb, edge_path jsonb, node jsonb, root_table_name text, node_table_name text ) ``` ### `graph.traverse()` multi-start arrays ```sql graph.traverse( start_tables regclass[], start_ids text[], ... ) ``` `start_tables[i]` pairs with `start_ids[i]`. With `uniqueness := 'node_global'`, duplicate reached nodes are removed across the merged multi-start result before final pagination. With `uniqueness := 'node_per_root'`, a reached node may appear once per root. ### `graph.traverse()` node-ref array ```sql graph.traverse( starts graph.node_ref[], max_depth int DEFAULT current_setting('graph.default_max_depth')::int, ... ) ``` SQL wrapper over the parallel-array overload. ### `graph.traverse_search()` ```sql graph.traverse_search( property_key text, property_value text, table_filter regclass DEFAULT NULL, search_mode text DEFAULT current_setting('graph.default_search_mode'), case_sensitive boolean DEFAULT current_setting('graph.default_case_sensitive')::boolean, search_max_rows int DEFAULT 100, search_row_offset int DEFAULT 0, max_depth int DEFAULT current_setting('graph.default_max_depth')::int, edge_types text[] DEFAULT NULL, direction text DEFAULT 'any', node_tables oid[] DEFAULT NULL, filter jsonb DEFAULT NULL, tenant text DEFAULT NULL, strategy text DEFAULT 'bfs', uniqueness text DEFAULT 'node_per_root', include_start boolean DEFAULT true, hydrate boolean DEFAULT current_setting('graph.default_hydrate')::boolean, max_rows int DEFAULT 1000, row_offset int DEFAULT 0 ) RETURNS TABLE ( root_table oid, root_id text, node_table oid, node_id text, depth int, path jsonb, edge_path jsonb, node jsonb, root_table_name text, node_table_name text ) ``` Searches start nodes, then traverses from verified matches. `uniqueness` has the same merged-result behavior as multi-start `graph.traverse()`. ### `graph.format_path()` ```sql graph.format_path( path jsonb, edge_path jsonb, separator text DEFAULT ' | ' ) RETURNS text ``` Formats a path and edge path into readable hop text. ## Filter Constructors ```sql graph.equals(column_name text, value jsonb) RETURNS jsonb graph.equals(column_name text, value text) RETURNS jsonb graph.equals(column_name text, value bigint) RETURNS jsonb graph.eq(...) RETURNS jsonb graph.not_equals(column_name text, value jsonb) RETURNS jsonb graph.neq(column_name text, value jsonb) RETURNS jsonb graph.neq(column_name text, value text) RETURNS jsonb graph.neq(column_name text, value bigint) RETURNS jsonb graph.in(column_name text, values jsonb) RETURNS jsonb graph.not_in(column_name text, values jsonb) RETURNS jsonb graph.contains_text(column_name text, value text) RETURNS jsonb graph.prefix_text(column_name text, value text) RETURNS jsonb graph.is_null(column_name text) RETURNS jsonb graph.is_not_null(column_name text) RETURNS jsonb graph.greater_than(column_name text, value jsonb) RETURNS jsonb graph.greater_than(column_name text, value bigint) RETURNS jsonb graph.gt(column_name text, value jsonb) RETURNS jsonb graph.gt(column_name text, value bigint) RETURNS jsonb graph.at_least(column_name text, value jsonb) RETURNS jsonb graph.gte(column_name text, value jsonb) RETURNS jsonb graph.gte(column_name text, value bigint) RETURNS jsonb graph.less_than(column_name text, value jsonb) RETURNS jsonb graph.lt(column_name text, value jsonb) RETURNS jsonb graph.lt(column_name text, value bigint) RETURNS jsonb graph.at_most(column_name text, value jsonb) RETURNS jsonb graph.lte(column_name text, value jsonb) RETURNS jsonb graph.lte(column_name text, value bigint) RETURNS jsonb graph.between(column_name text, lower jsonb, upper jsonb) RETURNS jsonb graph.on_node(filter jsonb) RETURNS jsonb graph.all(filters jsonb[]) RETURNS jsonb ``` ## Workflow API ### `graph.find()` ```sql graph.find( property_key text, property_value text, table_name regclass DEFAULT NULL, mode text DEFAULT 'contains', case_sensitive boolean DEFAULT false, max_rows int DEFAULT 20, row_offset int DEFAULT 0, tenant text DEFAULT NULL ) RETURNS TABLE ( node_table oid, node_table_name text, node_id text, match_type text, score real, verified boolean, rank int, node jsonb ) ``` ### `graph.expand()` ```sql graph.expand( seed_table regclass, seed_id text, max_depth int DEFAULT current_setting('graph.default_max_depth')::int, edge_types text[] DEFAULT NULL, direction text DEFAULT 'any', target_table regclass DEFAULT NULL, target_tables regclass[] DEFAULT NULL, where_node jsonb DEFAULT NULL, tenant text DEFAULT NULL, max_rows int DEFAULT 50, row_offset int DEFAULT 0, include_start boolean DEFAULT false ) RETURNS TABLE ( root_table oid, root_id text, root_table_name text, node_table oid, node_table_name text, node_id text, depth int, rank int, path jsonb, edge_path jsonb, readable_path text, node jsonb, truncated boolean ) ``` ### `graph.find_related()` ```sql graph.find_related( property_key text, property_value text, source_table regclass DEFAULT NULL, search_mode text DEFAULT current_setting('graph.default_search_mode'), case_sensitive boolean DEFAULT current_setting('graph.default_case_sensitive')::boolean, search_max_rows int DEFAULT 1, search_row_offset int DEFAULT 0, max_depth int DEFAULT current_setting('graph.default_max_depth')::int, edge_types text[] DEFAULT NULL, direction text DEFAULT 'any', target_table regclass DEFAULT NULL, target_tables regclass[] DEFAULT NULL, where_node jsonb DEFAULT NULL, tenant text DEFAULT NULL, max_rows int DEFAULT 20, row_offset int DEFAULT 0, include_counts boolean DEFAULT false, candidate_limit int DEFAULT 10000, include_start boolean DEFAULT false ) RETURNS TABLE ( root_table oid, root_id text, root_table_name text, node_table oid, node_table_name text, node_id text, depth int, score real, rank int, path jsonb, edge_path jsonb, readable_path text, node jsonb, candidate_count bigint, filtered_count bigint, truncated boolean ) ``` ### `graph.path()` ```sql graph.path( source_table regclass, source_id text, target_table regclass, target_id text, max_depth int DEFAULT 20 ) RETURNS TABLE ( step int, node_table oid, node_table_name text, node_id text, edge_label text, readable_path text, node jsonb ) ``` ### `graph.connection()` ```sql graph.connection( source_key text, source_value text, target_key text, target_value text, source_table regclass DEFAULT NULL, target_table regclass DEFAULT NULL, source_k int DEFAULT 3, target_k int DEFAULT 3, search_mode text DEFAULT 'contains', max_depth int DEFAULT 6 ) RETURNS TABLE ( source_table_name text, source_id text, target_table_name text, target_id text, hop_count int, step int, node_table oid, node_table_name text, node_id text, edge_label text, readable_path text, node jsonb ) ``` ### `graph.neighborhood()` ```sql graph.neighborhood( property_key text, property_value text, source_table regclass DEFAULT NULL, search_mode text DEFAULT 'contains', search_max_rows int DEFAULT 1, max_depth int DEFAULT 4, edge_types text[] DEFAULT NULL, direction text DEFAULT 'any', tenant text DEFAULT NULL, sample_k int DEFAULT 5, node_limit int DEFAULT 10000 ) RETURNS TABLE ( depth int, node_table oid, node_table_name text, node_count bigint, sample_nodes jsonb, truncated boolean ) ``` ## Paths ### `graph.shortest_path()` ```sql graph.shortest_path( source_table regclass, source_id text, target_table regclass, target_id text, max_depth int DEFAULT 20, hydrate boolean DEFAULT true ) RETURNS TABLE ( step int, node_table oid, node_id text, edge_label text, node jsonb, node_table_name text ) ``` Returns no rows when no path is found. Pending trigger-sync edge overlays are included, so a backend that has applied sync sees inserted/deleted unweighted edges before the next vacuum or maintenance rebuild. ### `graph.weighted_shortest_path()` ```sql graph.weighted_shortest_path( source_table regclass, source_id text, target_table regclass, target_id text ) RETURNS TABLE ( step int, node_table oid, node_table_name text, node_id text, edge_label text, edge_weight bigint, step_cost bigint, total_cost bigint ) ``` Uses registered edge weights and returns one row per path step. Step `0` is the source node and has NULL `edge_label` and `edge_weight`. `step_cost` is the cumulative cost at that step, and `total_cost` is repeated on every returned row. Returns no rows when no weighted path exists or the graph has no weight data. If pending edge overlays exist, weighted path queries fail with `PG018` until `graph.vacuum()` or `graph.maintenance()` merges them, because overlay edge mutations do not carry edge weights. ## Aggregation ### `graph.path_count_estimate()` ```sql graph.path_count_estimate(traversal jsonb) RETURNS TABLE ( estimated_paths bigint, exact boolean, capped boolean ) ``` ### `graph.aggregate()` ```sql graph.aggregate( traversal jsonb, aggregations jsonb, scope text DEFAULT 'returned_nodes', path_limit int DEFAULT current_setting('graph.max_exact_path_count')::int ) RETURNS jsonb ``` Supported `scope` values: `returned_nodes`, `chosen_parent_path`, `all_possible_paths`. ## Components ### `graph.connected_components()` ```sql graph.connected_components() RETURNS TABLE ( node_table oid, node_id text, component_id bigint, component_size int ) ``` Admin-only. Returns one row per active node. Pending trigger-sync edge overlays are included in the component computation. ### `graph.component_stats()` ```sql graph.component_stats() RETURNS TABLE ( num_components int, largest_component int, num_isolated_nodes int, total_active_nodes int ) ``` Admin-only. ### `graph.components()` ```sql graph.components( max_rows int DEFAULT 100, row_offset int DEFAULT 0 ) RETURNS TABLE ( component_id bigint, component_size bigint, rank int ) ``` Admin-only. Ordered by descending component size. ### `graph.largest_component()` ```sql graph.largest_component( max_rows int DEFAULT 100, row_offset int DEFAULT 0, hydrate boolean DEFAULT true ) RETURNS TABLE ( component_id bigint, node_table oid, node_id text, node jsonb ) ``` Admin-only. ### `graph.component()` ```sql graph.component( component_id bigint, max_rows int DEFAULT 100, row_offset int DEFAULT 0, hydrate boolean DEFAULT true ) RETURNS TABLE ( component_id bigint, node_table oid, node_id text, node jsonb ) ``` Admin-only. ### `graph.isolated_nodes()` ```sql graph.isolated_nodes( max_rows int DEFAULT 100, row_offset int DEFAULT 0, hydrate boolean DEFAULT true ) RETURNS TABLE ( component_id bigint, node_table oid, node_id text, node jsonb ) ``` Admin-only.