Carry the pivot's expressions into the bridge's own view

The bridge builds its own view from the pivot's filters, and those filters
can name pf_bucket_ord or pf_segment_ord — columns that exist only as
expressions. A view created without them cannot resolve the column, so the
bridge failed outright as soon as anyone filtered on an ordering column.

The per-slice path is left alone: its filters are built from col_meta names,
so they only ever reference real columns. Said so in place, since the
asymmetry otherwise reads as an oversight.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Trowbridge 2026-09-17 15:38:03 -04:00
parent a83138b3ce
commit 0fdb08291d

View File

@ -228,6 +228,8 @@ export default function BridgeView({
...Object.entries(sl).filter(([c]) => dateNames.has(c)).map(([c, v]) => [c, '==', Number(v)]),
]
if (!f.length) continue
// No expressions needed here: these filters are built from col_meta
// names, so they only ever reference real columns.
const view = await tableRef.current.view({ filter: f })
const part = await view.to_json()
await view.delete()
@ -239,11 +241,20 @@ export default function BridgeView({
}
} else {
let filter = []
// Expression columns have to come with the filter that uses them. The
// pivot's filter can name pf_bucket_ord or pf_segment_ord, which exist
// only as expressions a view built without them cannot resolve the
// column and fails outright.
let expressions = {}
if (scope === 'filtered' && viewerRef?.current) {
const cfg = await viewerRef.current.save()
filter = (cfg.filter || []).filter(f => Array.isArray(f) && f.length >= 2)
expressions = cfg.expressions || {}
}
const view = await tableRef.current.view(filter.length ? { filter } : {})
const viewCfg = {}
if (filter.length) viewCfg.filter = filter
if (Object.keys(expressions).length) viewCfg.expressions = expressions
const view = await tableRef.current.view(viewCfg)
rows = await view.to_json()
await view.delete()
}