Skip to Content
🚀 Introducing Polygres: Managed cloud for Postgres graph databases. Learn more at polygres.com →
User GuideFilters And Aggregation

Filters And Aggregation

Traversal filters are JSONB values produced by helper functions or by equivalent JSON. Aggregation uses strict JSON traversal specs and aggregation specs.

Register Filter Columns First

SELECT graph.add_filter_column('public.users'::regclass, 'age', 'numeric'); SELECT graph.add_filter_column('public.users'::regclass, 'active', 'boolean'); SELECT graph.add_filter_column('public.users'::regclass, 'state', 'text'); SELECT * FROM graph.build();

Filter columns are loaded into FilterIndex at build time. If you add or change filter-column registration, rebuild before using it in traversal.

Filter Helper Functions

FunctionMeaning
graph.equals(column, value) / graph.eq(column, value)equality
graph.not_equals(column, value) / graph.neq(column, value)inequality
graph.greater_than(column, value) / graph.gt(column, value)greater than
graph.at_least(column, value) / graph.gte(column, value)greater than or equal
graph.less_than(column, value) / graph.lt(column, value)less than
graph.at_most(column, value) / graph.lte(column, value)less than or equal
graph.between(column, lower, upper)inclusive range
graph.all(filters)logical AND merge
graph.on_node(filter)wrap helper output under the traversal node scope

Examples:

SELECT * FROM graph.traverse( 'public.users'::regclass, 'u1', max_depth := 2, filter := graph.all(ARRAY[ graph.gte('age', 40), graph.neq('state', '"inactive"'::jsonb) ]), hydrate := true );
SELECT * FROM graph.find_related( 'name', 'Alice', where_node := graph.gt('age', 40), target_table := 'public.users'::regclass );

The helper overloads accept convenient scalar text and integer values for common cases, but JSONB values are the general representation.

Filter Semantics

Filters are ANDed. A node must satisfy every predicate to be included.

JSON numbers compare as numbers. Integer values are compared exactly across the signed and unsigned 64-bit range; decimal values use numeric fallback semantics. JSON strings remain strings, so "123" does not match the number 123.

Missing or NULL values:

Operator familyNULL behavior
Equality and comparisonDoes not match
eq with JSON nullMatches absent value
neq with JSON nullMatches present value

The public helper set currently focuses on equality, inequality, comparisons, and ranges. For NULL checks, pass JSON null explicitly:

SELECT * FROM graph.traverse( 'public.users'::regclass, 'u1', filter := graph.eq('deleted_at', 'null'::jsonb) );

Path Formatting Helpers

graph.node_ref_string() returns a canonical JSON array string used by strict aggregation traversal specs:

SELECT graph.node_ref_string('public.users'::regclass, 'u1');

graph.format_path() converts a traversal path and edge_path pair to text:

SELECT graph.format_path( '[{"table":"users","id":"u1"},{"table":"users","id":"u2"}]'::jsonb, '["friend"]'::jsonb );

Result:

users:u1 --friend--> users:u2

Path Count Estimate

graph.path_count_estimate() evaluates a strict traversal spec and returns:

ColumnMeaning
estimated_pathsCount up to graph.max_exact_path_count
exactWhether the count completed exactly
cappedWhether counting stopped at the configured cap

Example:

WITH req AS ( SELECT jsonb_build_object( 'starts', jsonb_build_array(graph.node_ref_string('public.users'::regclass, 'u1')), 'direction', 'out', 'min_depth', 0, 'max_depth', 2, 'edge_types', jsonb_build_array('friend'), 'node_tables', jsonb_build_array('public.users') ) AS traversal ) SELECT * FROM graph.path_count_estimate((SELECT traversal FROM req));

Aggregation

graph.aggregate(traversal, aggregations, scope, path_limit) runs server-side aggregates over traversal results.

WITH req AS ( SELECT jsonb_build_object( 'starts', jsonb_build_array(graph.node_ref_string('public.users'::regclass, 'u1')), 'direction', 'out', 'min_depth', 0, 'max_depth', 1, 'edge_types', jsonb_build_array('friend'), 'node_tables', jsonb_build_array('public.users') ) AS traversal ) SELECT graph.aggregate( traversal, '{ "sum":[{"table":"public.users","column":"age","as":"total_age"}], "avg":[{"table":"public.users","column":"age","as":"avg_age"}], "count":[{"table":"public.users","column":"id","as":"user_count"}] }'::jsonb ) FROM req;

Supported aggregation groups:

GroupMeaning
sumSum numeric column values
avgAverage numeric column values
countCount non-null column values

Each spec object contains:

KeyMeaning
tableSource table regclass text
columnSource column to read
asOutput JSON key

Aggregation Scope

ScopeBehavior
returned_nodesAggregate over the returned traversal node set
chosen_parent_pathAggregate over the selected parent path to each result
all_possible_pathsEnumerate all possible paths under the traversal spec until path_limit

Use graph.path_count_estimate() before all_possible_paths on broad graphs.

all_possible_paths can grow exponentially. The code enforces graph.max_exact_path_count and the explicit path_limit to avoid unbounded path enumeration.

Last updated on