ArcadeDB version
Observed on Docker images:
arcadedata/arcadedb:26.3.2
arcadedata/arcadedb:26.4.1-SNAPSHOT
arcadedata/arcadedb:26.4.2
Environment
- Host OS: Windows 10
- Architecture: x86_64
- Deployment: Docker
- ArcadeDB endpoint: HTTP
/api/v1/command/arcade
- Request mode matches ArcadeDB Studio:
language: opencypher
serializer: studio
- Differential comparison target: Neo4j Docker
neo4j:latest
Describe the bug
ArcadeDB may lose a previously bound relationship variable after a later MATCH uses a variable-length relationship pattern over the same endpoints.
In the repro below, r is already bound by the first MATCH.
Neo4j keeps that binding alive and count(r) remains 1.
ArcadeDB returns 0, as if r had become unbound after the later variable-length match.
To Reproduce
Setup:
CREATE (a:Person {name:'Alice'}),
(b:Person {name:'Bob'}),
(a)-[:KNOWS]->(b);
Query:
MATCH (a:Person {name:'Alice'})-[r:KNOWS]->(b:Person {name:'Bob'})
WITH a, b, r
MATCH path = (a)-[:KNOWS*]->(b)
RETURN count(r) AS rc;
Expected behavior
r is already bound to the KNOWS relationship, so the count should stay 1.
Observed Neo4j result:
Actual behavior
ArcadeDB returns:
So the later variable-length match appears to invalidate or drop the carried relationship variable.
Control cases
If the later match is fixed-length instead of variable-length, ArcadeDB behaves correctly:
MATCH (a:Person {name:'Alice'})-[r:KNOWS]->(b:Person {name:'Bob'})
WITH a, b, r
MATCH path = (a)-[:KNOWS]->(b)
RETURN count(r) AS rc;
Observed result on both Neo4j and ArcadeDB:
The problem also reproduces with explicit variable-length bounds:
MATCH (a:Person {name:'Alice'})-[r:KNOWS]->(b:Person {name:'Bob'})
WITH a, b, r
MATCH path = (a)-[:KNOWS*1..1]->(b)
RETURN count(r) AS rc;
Observed results:
- Neo4j:
rc = 1
- ArcadeDB:
rc = 0
This suggests the issue is not about arbitrary path length expansion, but about the variable-length relationship-pattern form itself.
ArcadeDB also loses the relationship variable when the query tries to collect it:
MATCH (a:Person {name:'Alice'})-[r:KNOWS]->(b:Person {name:'Bob'})
WITH a, b, r
MATCH path = (a)-[:KNOWS*]->(b)
RETURN count(r) AS rc, collect(type(r)) AS rts;
Observed results:
- Neo4j:
rc = 1, rts = ['KNOWS']
- ArcadeDB:
rc = 0, rts = []
Stronger variant
The same problem appears even when r was created earlier in the same query:
MATCH (a:Person {name:'Alice'}), (b:Person {name:'Bob'})
CREATE (a)-[r:KNOWS]->(b)
WITH a, b, r
MATCH path = (a)-[:KNOWS*]->(b)
WITH count(r) AS rc
RETURN rc;
Observed results:
- Neo4j:
rc = 1
- ArcadeDB:
rc = 0
That makes the failure mode more general: a carried relationship variable can disappear after a later variable-length match over the same endpoints, regardless of whether that relationship came from setup data or was created in the same statement.
An even stronger variant, closer to the original differential seed, can make ArcadeDB drop the row entirely rather than only zeroing out r:
MATCH (n) DETACH DELETE n;
CREATE (:Person {name:'Alice'}), (:Person {name:'Bob'});
MATCH (p1:Person), (p2:Person)
WHERE p1.name = 'Alice' AND p2.name = 'Bob'
MERGE (p1)-[r:KNOWS {since: 2020}]->(p2)
WITH p1, p2, r
MATCH path = (p1)-[:KNOWS*]->(p2)
RETURN p1.name AS start_name,
p2.name AS end_name,
COUNT(r) AS relationship_count,
LAST(RELATIONSHIPS(path)) AS last_relationship;
Observed Neo4j result:
Alice, Bob, 1, (Alice)-[:KNOWS]->(Bob)
Observed ArcadeDB result on all tested versions:
So in this shape, the later variable-length match does not just empty out the carried relationship variable. It can collapse the whole result row once the query also tries to consume the matched path.
This makes the boundary sharper:
- if the later match is fixed-length, ArcadeDB behaves correctly
- if the relationship already exists before the statement, ArcadeDB behaves correctly
- if the relationship is created or merged earlier in the same statement and a later variable-length match is used over the same endpoints, ArcadeDB can lose
r or even lose the whole row
ArcadeDB version
Observed on Docker images:
arcadedata/arcadedb:26.3.2arcadedata/arcadedb:26.4.1-SNAPSHOTarcadedata/arcadedb:26.4.2Environment
/api/v1/command/arcadelanguage: opencypherserializer: studioneo4j:latestDescribe the bug
ArcadeDB may lose a previously bound relationship variable after a later
MATCHuses a variable-length relationship pattern over the same endpoints.In the repro below,
ris already bound by the firstMATCH.Neo4j keeps that binding alive and
count(r)remains1.ArcadeDB returns
0, as ifrhad become unbound after the later variable-length match.To Reproduce
Setup:
Query:
Expected behavior
ris already bound to theKNOWSrelationship, so the count should stay1.Observed Neo4j result:
Actual behavior
ArcadeDB returns:
So the later variable-length match appears to invalidate or drop the carried relationship variable.
Control cases
If the later match is fixed-length instead of variable-length, ArcadeDB behaves correctly:
Observed result on both Neo4j and ArcadeDB:
The problem also reproduces with explicit variable-length bounds:
Observed results:
rc = 1rc = 0This suggests the issue is not about arbitrary path length expansion, but about the variable-length relationship-pattern form itself.
ArcadeDB also loses the relationship variable when the query tries to collect it:
Observed results:
rc = 1,rts = ['KNOWS']rc = 0,rts = []Stronger variant
The same problem appears even when
rwas created earlier in the same query:Observed results:
rc = 1rc = 0That makes the failure mode more general: a carried relationship variable can disappear after a later variable-length match over the same endpoints, regardless of whether that relationship came from setup data or was created in the same statement.
An even stronger variant, closer to the original differential seed, can make ArcadeDB drop the row entirely rather than only zeroing out
r:Observed Neo4j result:
Observed ArcadeDB result on all tested versions:
So in this shape, the later variable-length match does not just empty out the carried relationship variable. It can collapse the whole result row once the query also tries to consume the matched
path.This makes the boundary sharper:
ror even lose the whole row