Having (group filter)
having filters on the aggregated value, not the source rows — the SQL HAVING to a column
filter’s WHERE. After the engine rolls up the tree, having keeps only the groups whose measures
satisfy a safe formula-DSL predicate (e.g. "revenue > 1000"); nodes that fail are pruned, but the
ancestor path to any survivor is kept so the hierarchy stays intact. It runs in the engine — the
frontend does no math — and applies to the grid (row-tree) flow only.
This is best seen in a running app; the config is the whole feature:
from dash_tensor_grid import TensorGrid, build_grid
payload = build_grid(sales, { "rows": ["region", "country"], "measures": {"revenue": "sum", "qty": "sum"}, "formats": {"revenue": "currency:USD"}, "having": "revenue > 1000", # keep only groups whose aggregated revenue exceeds 1000})TensorGrid(id="grid", row_data=payload["row_data"], column_defs=payload["column_defs"])import { TensorGrid } from '@tensorgrid/react';
<TensorGrid data={data} rows={['region', 'country']} measures={{ revenue: 'sum', qty: 'sum' }} having="revenue > 1000"/>;<TensorGrid :data="data" :rows="['region', 'country']" :measures="{ revenue: 'sum', qty: 'sum' }" having="revenue > 1000"/>import { mountTensorGrid } from '@tensorgrid/vanilla';
mountTensorGrid(document.getElementById('grid'), { data, rows: ['region', 'country'], measures: { revenue: 'sum', qty: 'sum' }, having: 'revenue > 1000',});- Post-aggregation. The predicate is evaluated against each node’s rolled-up measures — the same
safe DSL as a calculated measure, so
"revenue > cost * 1.5"works too. It never runs arbitrary code. - Ancestors kept. A failing parent that still has a passing descendant is retained as a path; only genuinely empty branches disappear.
- Grid flow only.
havingis refused on a pivot (columns) config — the cross-tab has a different shape. In lazy / server mode it prunes the shipped root level until children are fetched. - Contrast with a column range filter, which tests the source rows before aggregation.