Skip to content

Compiled Cypher: a grouping key read in an order term's body fails, and one in any(y IN … WHERE y = e) groups by its variable (port of drivine4j#505) #316

Open

Description

@jasperblues

Port of liberation-data/drivine4j#505 (fixed in liberation-data/drivine4j#507). The part not in #315.

Two shapes that #315's _aggregates_first doesn't handle yet. Both were checked on Neo4j 2026.10.1.

1. An order term that aggregates isn't rekeyed. On #315's branch (355b720), drivine/cypher/sql/sql_cypher_compiler.py:3605-3616 (sorted_by) replaces an order term only at its top level with replaced(sort.expression, sorted_by), never inside a body. The order terms' beside (3562-3574) keeps only variables in keyed. So a property key read in an order term's body leaves its variable unbound:

UNWIND [{a: 1, b: 1}, {a: 1, b: 2}, {a: 2, b: 1}] AS x RETURN x.a AS a, count(*) AS n ORDER BY size([y IN collect(x.b) WHERE y > x.a])
-- here: "x is not defined"; Neo4j: [2, 1], [1, 2]
UNWIND [{a: 1, b: 1}, {a: 1, b: 2}, {a: 2, b: 1}] AS x RETURN x.a AS a, count(*) AS n ORDER BY size([y IN collect(x.b) | x.a]), a
-- here: "x is not defined"; Neo4j groups by x: [1, 1], [1, 1], [2, 1]

2. Neo4j rewrites any(y IN list WHERE y = e) as e IN list, and none(…) as NOT e IN list, where e reads no y. It also reads y IN [e] as y = e. So such a quantifier over an aggregate reads the key at the top level and groups by the property. all() and single() aren't rewritten. With rekeyed (3517), such a quantifier's WHERE groups by the variable:

UNWIND [{a: 1, b: 1}, {a: 1, b: 2}, {a: 2, b: 1}] AS x RETURN x.a AS a, any(y IN collect(x.b) WHERE y = x.a) AS n ORDER BY a
-- here: [1, false], [1, true], [2, false]; Neo4j: [1, true], [2, false]

The same holds for none(y IN collect(x.b) WHERE x.a = y), any(… WHERE y IN [x.a]) and any(t IN collect(m.title) WHERE t = r.role).

What drivine4j's fix does (liberation-data/drivine4j#507, aggregatesFirst in SqlCypherCompiler.kt):

  • Order terms go through rekeyed too. An item's name is still read first, with readingKeys (reading_keys there) in bodies. Where an order term's body groups by a property key's variable, that variable is kept beside the keys, as for an item.
  • readAsIn marks an any/none quantifier whose WHERE is y = e, e = y, y IN [e] or e IN [y], where e reads no y. rekeyed reads that WHERE through readingKeys, as it does a list comprehension's WHERE.

Tests to port:

Update (from drivine-python PR #318, which syncs spec v0.19.0): besides the ORDER BY bodies and any→IN steps, spec v0.18.0's queries.yaml "Implicit grouping key" steps 29–35 still fail on Postgres and Oracle on main, though #315 was meant to cover the nested-body rekeying: they give a row per variable where Neo4j gives one per key. #318 holds them against this issue. The port should find why: compare with drivine4j #507 (readingKeys/rekeyed, merged) and drivine #498, and fix them here, or split them into their own issue if the cause is different.

Activity

  1. added this to the Next release + 1 milestone on Oct 31, 2026
  2. changed the title [-]Compiled Cypher: a grouping key read in an order term's body, or in any(y IN … WHERE y = e), groups by its variable (port of drivine4j#505)[/-] [+]Compiled Cypher: a grouping key read in an order term's body fails, and one in any(y IN … WHERE y = e) groups by its variable (port of drivine4j#505)[/+] on Oct 31, 2026
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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions