Found by the aggregate x dtype differential sweep in #1819 (which pinned sum/avg to the Cypher
INTEGER | FLOAT | DURATION contract). This one is a deliberate GFQL extension that Cypher does
not allow, left in place there rather than changed silently — it needs an owner decision.
The divergence from Cypher
# Neo4j 5.26.26
UNWIND [true,false,true] AS x RETURN sum(x);
-> Type mismatch: expected Float, Integer or Duration but was Boolean
GFQL, on every engine, answers 2:
import pandas as pd, graphistry
nodes = pd.DataFrame({"id": [0,1,2], "grp": ["x"]*3, "flag": [True, False, True]})
edges = pd.DataFrame({"src": [0], "dst": [1]})
g = graphistry.nodes(nodes, "id").edges(edges, "src", "dst")
g.gfql("MATCH (n) RETURN sum(n.flag) AS s", engine="pandas")._nodes # -> s = 2
g.gfql("MATCH (n) RETURN avg(n.flag) AS a", engine="polars")._nodes # -> a = 0.666...
Why it was NOT changed in #1819
- Both engines already agree, so it is not a divergence — it is a uniform extension.
- Summing an indicator column is idiomatic in the dataframe surface GFQL also serves, and rejecting
it would break working user queries with no correctness argument behind the break.
It is recorded in graphistry/compute/gfql/agg_types.py as a choice with a reason rather than an
accident, so whoever revisits it finds the note. The decision to make is whether GFQL wants strict
Cypher conformance here (reject) or wants to keep the dataframe affordance (document it as an
explicit extension in the aggregates docs).
Repro harness: graphistry/tests/compute/gfql/test_aggregate_type_contract.py (the bool_col
parameters of the positive lane pin the current behaviour).
Found by the aggregate x dtype differential sweep in #1819 (which pinned
sum/avgto the CypherINTEGER | FLOAT | DURATIONcontract). This one is a deliberate GFQL extension that Cypher doesnot allow, left in place there rather than changed silently — it needs an owner decision.
The divergence from Cypher
GFQL, on every engine, answers
2:Why it was NOT changed in #1819
it would break working user queries with no correctness argument behind the break.
It is recorded in
graphistry/compute/gfql/agg_types.pyas a choice with a reason rather than anaccident, so whoever revisits it finds the note. The decision to make is whether GFQL wants strict
Cypher conformance here (reject) or wants to keep the dataframe affordance (document it as an
explicit extension in the aggregates docs).
Repro harness:
graphistry/tests/compute/gfql/test_aggregate_type_contract.py(thebool_colparameters of the positive lane pin the current behaviour).