Skip to content

GFQL Cypher: an unseeded MATCH ... RETURN b LIMIT k builds the full binding table before LIMIT (81 ms vs 3.6 ms native at 100k edges) #2129

Description

@lmeyerov

Observed (master b18d808 / #2117 tree, pandas, 20k nodes / 100k edges, warmed medians; local = direction only; from #2116 item 3c)

query ms
MATCH (a)-[e]->(b) RETURN b LIMIT 5 81.0
MATCH (a)-[e]->(b) RETURN b.id AS id LIMIT 5 69.0
native [n(), e_forward(), n()] 3.6

The colleague's ~800 ms figure was the same shape at a larger graph.

Profile

76 of the 81 ms are inside graphistry/compute/gfql/row/pipeline.py::_gfql_connected_bindings_row_table (via rows(binding_ops=...)): 44 ms in _gfql_connected_bindings_state and 24 ms in _gfql_connected_bindings_row_frame_from_state — the full 100k-row a/e/b binding table is materialized (pandas merges, take, vstack) and only then does LIMIT 5 apply. Nothing is wrong; nothing is pushed down.

Shape of a fix (design item, not a one-liner)

Either a LIMIT pushdown into the binding-table builder for the unordered, unfiltered case (take the first k edges before the node gathers — valid only without ORDER BY/DISTINCT/aggregation), or a lazy binding table that materializes on demand. Either way: pin result identity against the current path (row set, and row order where ORDER BY is present), A/A control before any number is quoted, and profile the seeded form too so the pushdown does not regress the index path (#2117).

Related: #2128 fixed the other 3c item (the string-sniffer cost in cross-alias OR predicates).

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions