Skip to content

[FEA] GFQL GRAPH predicates: explicit eager boundary for row-shaped filters #1715

Description

@lmeyerov

Problem

#1706 makes GRAPH { MATCH ... WHERE ... } safe by applying single-node/single-edge residual filters as graph masks and rejecting harder cases such as pattern predicates and multi-alias conditions. That avoids silent wrong results, but the rejection should not be the long-term ceiling.

The original GRAPH { } intent is graph-pipeline composition: keep _nodes and _edges flowing across stages without forcing row output unless the query shape truly needs it. Some rejected predicates do need joined match rows, but that can be an explicit strictness/eagerness boundary instead of a compile error.

Example currently rejected shapes:

GRAPH { MATCH (a)-[r]->(b) WHERE a.score > b.score }
GRAPH { MATCH (a)-[r]->(b) WHERE (a)-[:R]->() }
GRAPH { MATCH (a)-[r]->(b) WHERE EXISTS { (a)-[:R]->() } }

Desired behavior

Support these cases by making the boundary explicit and inspectable:

  1. Preserve current strict/default behavior unless we deliberately opt into an eager boundary, or choose a repo-consistent default with an explicit warning/plan marker.
  2. When a GRAPH { } predicate requires joined match rows, materialize/evaluate the needed row binding set, then project the surviving rows back into graph state (_nodes, _edges) for later USE, GRAPH { ... }, and graph-preserving CALL graphistry.*.write() stages.
  3. Expose the boundary in a normal repo-consistent way: logical/physical plan annotation, validation issue/warning, or equivalent query-inspection surface. The important bit is that users and benchmark authors can detect when a graph pipeline crossed from pure graph filtering into row-materializing execution.
  4. Keep strict/benchmark integrity available, similar in spirit to existing auto vs strict off-engine call policy: no hidden materialization when the caller asked for strictness.

Suggested implementation path

  • Extend the Cypher lowering/plan metadata near CompiledGraphResidualFilter / _compile_graph_residual_filters to classify row-shaped GRAPH predicates instead of only raising.
  • Add an execution path that evaluates the joined rows and projects back to graph state. Define projection semantics explicitly for nodes and edges touched by surviving rows.
  • Add an inspectable marker such as eager_boundary, materializes_rows, or a validation warning code.
  • Keep clear errors for genuinely unsupported semantics until projection rules are defined.

Acceptance criteria

  • GRAPH { MATCH (a)-[r]->(b) WHERE a.score > b.score } can execute in the supported eager mode and returns graph state, not row output.
  • Pattern existence / EXISTS { ... } inside GRAPH { } either works through the same boundary or produces a remaining clear unsupported error with a distinct reason.
  • Query validation/inspection exposes the eager boundary so tests can assert it.
  • Strict mode or equivalent can still decline the boundary for benchmark integrity.
  • Docs explain the behavior in user terms: "this filter needs joined rows, so GFQL crosses an explicit eager boundary and then returns a graph."
  • Tests cover pandas first; cuDF/polars behavior is either implemented or declines clearly with parity-or-NIE semantics.

Deferral / dependency

Deferred until the current GFQL correctness/perf PR stack lands, starting with #1706 and continuing through the benchmark stack. This should be stacked on top after the safety fix is merged, not folded into #1706.

Stack recommendation

Stack this after #1706. #1706 should stay the safety fix: no silent drops, clearer docs, and single-alias graph masks. This issue is the follow-up feature that changes rejected GRAPH predicates into explicit eager-boundary execution.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions