Summary
The bindings-row builder attaches every node alias's property columns (alias.{col}) for every node alias in the pattern, regardless of whether the downstream query references them. For aggregate/count queries that reference few or no properties, this materializes large intermediates for nothing.
Evidence (profiled 2026-07-06)
MATCH (a)-[]->(b)-[]->(c) RETURN count(*) on polars @100k nodes / 1M edges:
- full query = 399.8ms; the
rows(binding_ops) build alone = 382.9ms (96%); group_by+select = 16.9ms.
- build output cols:
a, b, c, a.node_id, a.age, b.node_id, b.age, c.node_id, c.age — but count(*) needs none of the .age columns.
- the raw join-chain (no property attach) is only ~50ms → ~330ms is wasted unused-property joins.
So skipping unreferenced property columns is a ~7× win on q8 (383→~50ms build), and it directly de-risks the pandas full-data OOM concern (57M-path 2-hop binding × unused property columns at 2.42M edges).
Fix (pandas/cuDF)
Thread the set of referenced alias.property columns from the cypher lowering into rows(binding_ops=..., keep_alias_cols=<set>), and have the builders (_gfql_connected_bindings_row_frame_from_state pandas; binding_rows_polars for the non-lazy path) only left-join the kept columns. Bare alias id columns + grouping/join keys always kept.
Safety: the keep-set MUST be a conservative superset of referenced columns (walk all downstream ops: RETURN/WHERE/ORDER BY/GROUP BY keys+aggs/WITH stages/hidden-reentry refs). Under-inclusion = wrong answer; over-inclusion = merely slower. Default to keep-all when the set can't be computed confidently.
Scope / relation
Summary
The bindings-row builder attaches every node alias's property columns (
alias.{col}) for every node alias in the pattern, regardless of whether the downstream query references them. For aggregate/count queries that reference few or no properties, this materializes large intermediates for nothing.Evidence (profiled 2026-07-06)
MATCH (a)-[]->(b)-[]->(c) RETURN count(*)on polars @100k nodes / 1M edges:rows(binding_ops)build alone = 382.9ms (96%); group_by+select = 16.9ms.a, b, c, a.node_id, a.age, b.node_id, b.age, c.node_id, c.age— butcount(*)needs none of the.agecolumns.So skipping unreferenced property columns is a ~7× win on q8 (383→~50ms build), and it directly de-risks the pandas full-data OOM concern (57M-path 2-hop binding × unused property columns at 2.42M edges).
Fix (pandas/cuDF)
Thread the set of referenced
alias.propertycolumns from the cypher lowering intorows(binding_ops=..., keep_alias_cols=<set>), and have the builders (_gfql_connected_bindings_row_frame_from_statepandas;binding_rows_polarsfor the non-lazy path) only left-join the kept columns. Bare alias id columns + grouping/join keys always kept.Safety: the keep-set MUST be a conservative superset of referenced columns (walk all downstream ops: RETURN/WHERE/ORDER BY/GROUP BY keys+aggs/WITH stages/hidden-reentry refs). Under-inclusion = wrong answer; over-inclusion = merely slower. Default to keep-all when the set can't be computed confidently.
Scope / relation