Repository navigation
Description
A Graph Analytical View that lists the edge types of the chain but not an edge type named by a pattern predicate is used for a count(*), and the predicate is then evaluated against the view, where the unlisted edge type has no edges.
WHERE NOT (a)-[:F]->(b) removes nothing and WHERE (a)-[:F]->(b) keeps nothing, although the F edges exist.
The same predicates answer correctly with no view, with a view that lists F, behind a WITH, and in a query that returns rows instead of count(*).
Found while verifying #9354 with a randomized differential; it is older than that PR and reproduces on the official 26.10.1 jars.
Reproduction
Engine API only: vertices x, y, z of type V, edges x -E-> y, x -F-> y, y -E-> z.
The view covers every vertex type and the edge type E but not F.
import com.arcadedb.database.Database;
import com.arcadedb.database.DatabaseFactory;
import com.arcadedb.database.RID;
import com.arcadedb.query.sql.executor.ResultSet;
public class ViewEdgeCoverage {
public static void main(final String[] args) throws Exception {
System.out.println("engine " + com.arcadedb.Constants.getRawVersion() + " build " + com.arcadedb.Constants.getBuildNumber() + ", java " + System.getProperty("java.version"));
final DatabaseFactory f = new DatabaseFactory(args[0]);
if (f.exists())
f.open().drop();
final Database db = f.create();
db.command("sql", "CREATE VERTEX TYPE V");
db.command("sql", "CREATE EDGE TYPE E");
db.command("sql", "CREATE EDGE TYPE F");
db.transaction(() -> {
final RID x = db.newVertex("V").save().getIdentity(), y = db.newVertex("V").save().getIdentity(), z = db.newVertex("V").save().getIdentity();
x.asVertex().newEdge("E", y).save();
x.asVertex().newEdge("F", y).save();
y.asVertex().newEdge("E", z).save();
});
// { text, expected }
final String[][] qs = {
{ "MATCH (a:V)-[:E]->(b:V) WHERE NOT (a)-[:F]->(b) RETURN count(*) AS n", "1" },
{ "MATCH (a:V)-[:E]->(b:V) WHERE (a)-[:F]->(b) RETURN count(*) AS n", "1" },
{ "MATCH (a:V)-[:E]->(b:V)-[:E]->(c:V) WHERE NOT (a)-[:F]->(b) RETURN count(*) AS n", "0" },
{ "MATCH (a:V)-[:E]->(b:V) WITH a, b WHERE NOT (a)-[:F]->(b) RETURN count(*) AS n", "1" } };
for (final String edgeTypes : new String[] { "none", "E", "E, F" }) {
if (!edgeTypes.equals("none")) {
db.command("sql", "DROP GRAPH ANALYTICAL VIEW IF EXISTS gav");
db.command("sql", "CREATE GRAPH ANALYTICAL VIEW gav VERTEX TYPES (V) EDGE TYPES (" + edgeTypes + ") UPDATE MODE OFF");
for (int i = 0; i < 6000; i++) {
try (ResultSet rs = db.query("sql", "SELECT FROM schema:graphAnalyticalViews WHERE name = ?", "gav")) {
if (rs.hasNext() && "READY".equals(rs.next().getProperty("status")))
break;
}
Thread.sleep(20);
}
}
for (final String[] q : qs)
try (ResultSet rs = db.query("opencypher", q[0])) {
System.out.println("view over edge types (" + edgeTypes + ") | " + q[0] + " -> " + rs.next().getProperty("n") + " (expected " + q[1] + ")");
}
}
db.close();
}
}Run with java --add-modules=jdk.incubator.vector --enable-native-access=ALL-UNNAMED -cp "<arcadedb lib>/*" ViewEdgeCoverage.java <empty directory>.
Output
The lines for the view over E only, identical on Temurin 25.0.4+7 (with -XX:+UseCompactObjectHeaders) and Temurin 21.0.12+8, on the official 26.10.1 jars (build d36b4ca3ae4c170abc73598e0dffa2bb58e06621) and on main 246821a605a4202c69f0426e7d753cf8b89025ce (26.11.1-SNAPSHOT), apart from the third line (0, which is right, on 26.10.1, and 1 on main):
view over edge types (E) | MATCH (a:V)-[:E]->(b:V) WHERE NOT (a)-[:F]->(b) RETURN count(*) AS n -> 2 (expected 1)
view over edge types (E) | MATCH (a:V)-[:E]->(b:V) WHERE (a)-[:F]->(b) RETURN count(*) AS n -> 0 (expected 1)
view over edge types (E) | MATCH (a:V)-[:E]->(b:V)-[:E]->(c:V) WHERE NOT (a)-[:F]->(b) RETURN count(*) AS n -> 1 (expected 0)
view over edge types (E) | MATCH (a:V)-[:E]->(b:V) WITH a, b WHERE NOT (a)-[:F]->(b) RETURN count(*) AS n -> 1 (expected 1)
The no-view and E, F lines of the same run all equal the expected column (1, 1, 0, 1).
The expected counts are the row pipeline's: the pair (x, y) has an F edge, so it is excluded by the NOT form and is the only match of the plain form.
The edge types that a pattern predicate or a negated pattern names are not part of what decides whether a view may serve the query, only the edge types of the chain hops seem to be (the same selection by edge type that #9301 describes for vertex types).
Activity
known issues: openCypher one-hop count over light edges and pattern p…
Merge pull request #9383 from ArcadeData/worktree-fix-9378-9377-9350-…
lvca commentedon Oct 7, 2026
Confirmed and fixed in #9383 (26.11.1). A Graph Analytical View answered pattern predicates such as
NOT (a)-[:F]->(b)for edge types it does not list, where it holds no edges. Those lookups now read the record instead, so results are the same with and without the view.❤️ Thanks for helping make ArcadeDB better! ArcadeDB is free and open source, sustained by its community. If it's useful to you or your company, please consider becoming a sponsor to keep development going.
known issues: ArcadeData#9377 and ArcadeData#9378 are fixed in the 26…
fix(ArcadeData#9378,ArcadeData#9377,ArcadeData#9350,ArcadeData#9349,A…