feat(opencypher): enable filter pushdown for list predicates (any/all/none/single) in WHERE clause - #3891
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements variable collection for list predicate expressions in OpenCypher WHERE clauses, ensuring that variables within predicates like 'any()' are correctly identified. Feedback was provided regarding the placement and implementation of the new test cases. Specifically, the tests were added to an inappropriate nested class lacking the necessary vertex type setup, used inconsistent indentation, and utilized JUnit assertions instead of the project's standard AssertJ library. An unnecessary JUnit import should also be removed.
| @Test | ||
| public void testAnyPredicateInWhere() { | ||
| // Cleanup first to ensure a clean state | ||
| database.command("opencypher", "MATCH (n:Person) DETACH DELETE n"); | ||
|
|
||
| database.command("opencypher", | ||
| "CREATE (:Person {name:'Alice'}), (:Person {name:'Bob'}), (:Person {name:'Charlie'})"); | ||
|
|
||
| ResultSet rs = database.query("opencypher", | ||
| "MATCH (p:Person) WHERE any(x IN ['Alice'] WHERE x = p.name) RETURN p.name AS name ORDER BY name"); | ||
|
|
||
| List<String> names = new ArrayList<>(); | ||
| while (rs.hasNext()) names.add(rs.next().getProperty("name")); | ||
|
|
||
| assertEquals(List.of("Alice"), names); | ||
| } | ||
|
|
||
| @Test | ||
| public void testNotAnyPredicateInWhere() { | ||
| // Cleanup first to ensure a clean state | ||
| database.command("opencypher", "MATCH (n:Person) DETACH DELETE n"); | ||
|
|
||
| database.command("opencypher", | ||
| "CREATE (:Person {name:'Alice'}), (:Person {name:'Bob'}), (:Person {name:'Charlie'})"); | ||
|
|
||
| ResultSet rs = database.query("opencypher", | ||
| "MATCH (p:Person) WHERE NOT any(x IN ['Alice'] WHERE x = p.name) RETURN p.name AS name ORDER BY name"); | ||
|
|
||
| List<String> names = new ArrayList<>(); | ||
| while (rs.hasNext()) names.add(rs.next().getProperty("name")); | ||
|
|
||
| assertEquals(List.of("Bob", "Charlie"), names); | ||
| } | ||
| } |
There was a problem hiding this comment.
These tests are logically misplaced inside the NotOperatorParenthesesRegression nested class, which is intended for a different set of regression tests. Furthermore, the setUp method of this nested class does not create the Person vertex type, which will cause these tests to fail. Moving them to the main OpenCypherWhereClauseTest class ensures they use the correct database setup and follow the file's 2-space indentation and AssertJ usage style.
}
@Test
void testAnyPredicateInWhere() {
database.command("opencypher", "MATCH (n:Person) DETACH DELETE n");
database.command("opencypher",
"CREATE (:Person {name:'Alice'}), (:Person {name:'Bob'}), (:Person {name:'Charlie'})");
ResultSet rs = database.query("opencypher",
"MATCH (p:Person) WHERE any(x IN ['Alice'] WHERE x = p.name) RETURN p.name AS name ORDER BY name");
List<String> names = new ArrayList<>();
while (rs.hasNext()) names.add(rs.next().getProperty("name"));
assertThat(names).containsExactly("Alice");
}
@Test
void testNotAnyPredicateInWhere() {
database.command("opencypher", "MATCH (n:Person) DETACH DELETE n");
database.command("opencypher",
"CREATE (:Person {name:'Alice'}), (:Person {name:'Bob'}), (:Person {name:'Charlie'})");
ResultSet rs = database.query("opencypher",
"MATCH (p:Person) WHERE NOT any(x IN ['Alice'] WHERE x = p.name) RETURN p.name AS name ORDER BY name");
List<String> names = new ArrayList<>();
while (rs.hasNext()) names.add(rs.next().getProperty("name"));
assertThat(names).containsExactly("Bob", "Charlie");
}|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
| import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; |
Up to standards ✅🟢 Issues
|
|
Thanks for the PR! Here are my findings after reviewing the change and reproducing locally. Critical1. The new tests pass without the fix. 2. Tests are placed in the wrong nested class. 3. Project conventions not followed. Style
Design / completeness
What's good
Requested follow-upBecause the tests don't distinguish "with fix" from "without fix" on
Once the points above are addressed, I'm happy to take another look. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3891 +/- ##
==========================================
- Coverage 63.87% 63.83% -0.05%
==========================================
Files 1591 1591
Lines 118934 118948 +14
Branches 25275 25277 +2
==========================================
- Hits 75968 75926 -42
- Misses 32489 32548 +59
+ Partials 10477 10474 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Thanks for the thorough review. I've addressed all points. On the bug reproduction: I reproduced via ArcadeDB Studio using the OpenCypher HTTP API with the exact queries from issue #3888. On current Changes made:
Full Happy to make any further adjustments. |
|
Perfect, thanks!!! |
…h list predicates Locks in the PR #3891 fix by asserting that collectVariables() returns the correct outer variables for any/all/none/single predicates used in a comparison, and excludes the loop-scoped iterator from list comprehensions. All six tests fail on the pre-fix WhereClause implementation.
…h list predicates Locks in the PR #3891 fix by asserting that collectVariables() returns the correct outer variables for any/all/none/single predicates used in a comparison, and excludes the loop-scoped iterator from list comprehensions. All six tests fail on the pre-fix WhereClause implementation. (cherry picked from commit 09d5fff)
…io [skip ci] Bumps [marked](https://github.com/markedjs/marked) from 15.0.12 to 17.0.4. Release notes *Sourced from [marked's releases](https://github.com/markedjs/marked/releases).* > v17.0.4 > ------- > > [17.0.4](markedjs/marked@v17.0.3...v17.0.4) (2026-03-04) > ----------------------------------------------------------------------------------- > > ### Bug Fixes > > * prevent ReDoS in inline link regex title group ([ArcadeData#3902](https://redirect.github.com/markedjs/marked/issues/3902)) ([46fb9b8](markedjs/marked@46fb9b8)) > > v17.0.3 > ------- > > [17.0.3](markedjs/marked@v17.0.2...v17.0.3) (2026-02-17) > ----------------------------------------------------------------------------------- > > ### Bug Fixes > > * escape image alt text ([ArcadeData#3896](https://redirect.github.com/markedjs/marked/issues/3896)) ([909fe44](markedjs/marked@909fe44)) > > v17.0.2 > ------- > > [17.0.2](markedjs/marked@v17.0.1...v17.0.2) (2026-02-11) > ----------------------------------------------------------------------------------- > > ### Bug Fixes > > * fix blockquote after list ([ArcadeData#3888](https://redirect.github.com/markedjs/marked/issues/3888)) ([2a475a1](markedjs/marked@2a475a1)) > * fix empty list item ([ArcadeData#3890](https://redirect.github.com/markedjs/marked/issues/3890)) ([3fc6a44](markedjs/marked@3fc6a44)) > * fix list item wrong indent ([ArcadeData#3889](https://redirect.github.com/markedjs/marked/issues/3889)) ([e031175](markedjs/marked@e031175)) > * fix list with tabs ([ArcadeData#3891](https://redirect.github.com/markedjs/marked/issues/3891)) ([9fc4f8e](markedjs/marked@9fc4f8e)) > * fix strikethrough flanking rules ([ArcadeData#3882](https://redirect.github.com/markedjs/marked/issues/3882)) ([1a5b124](markedjs/marked@1a5b124)) > > v17.0.1 > ------- > > [17.0.1](markedjs/marked@v17.0.0...v17.0.1) (2025-11-20) > ----------------------------------------------------------------------------------- > > ### Bug Fixes > > * fix block elements in task item ([ArcadeData#3828](https://redirect.github.com/markedjs/marked/issues/3828)) ([921ee22](markedjs/marked@921ee22)) > > v17.0.0 > ------- > > [17.0.0](markedjs/marked@v16.4.2...v17.0.0) (2025-11-07) > =================================================================================== > > ### Bug Fixes > > * only create tokens inside tokenizers ([ArcadeData#3755](https://redirect.github.com/markedjs/marked/issues/3755)) ([7b19231](markedjs/marked@7b19231)) > > ### BREAKING CHANGES > > * Change how consecutive text tokens work in lists > * Simplify listItem renderer > * Checkbox token is added in list tokenizer ... (truncated) Commits * [`22f0c55`](markedjs/marked@22f0c55) chore(release): 17.0.4 [skip ci] * [`46fb9b8`](markedjs/marked@46fb9b8) fix: prevent ReDoS in inline link regex title group ([ArcadeData#3902](https://redirect.github.com/markedjs/marked/issues/3902)) * [`5b6faee`](markedjs/marked@5b6faee) chore(deps-dev): Bump eslint from 10.0.1 to 10.0.2 ([ArcadeData#3904](https://redirect.github.com/markedjs/marked/issues/3904)) * [`bcdaf6a`](markedjs/marked@bcdaf6a) chore(deps-dev): Bump `@semantic-release/npm` from 13.1.4 to 13.1.5 ([ArcadeData#3905](https://redirect.github.com/markedjs/marked/issues/3905)) * [`baa78a5`](markedjs/marked@baa78a5) docs: Add marked-abc to known extensions list ([ArcadeData#3903](https://redirect.github.com/markedjs/marked/issues/3903)) * [`1aed9ac`](markedjs/marked@1aed9ac) chore(deps-dev): Bump eslint from 10.0.0 to 10.0.1 ([ArcadeData#3901](https://redirect.github.com/markedjs/marked/issues/3901)) * [`8045055`](markedjs/marked@8045055) chore: rename escape helper function ([ArcadeData#3900](https://redirect.github.com/markedjs/marked/issues/3900)) * [`bced615`](markedjs/marked@bced615) chore(release): 17.0.3 [skip ci] * [`909fe44`](markedjs/marked@909fe44) fix: escape image alt text ([ArcadeData#3896](https://redirect.github.com/markedjs/marked/issues/3896)) * [`eb8ba2b`](markedjs/marked@eb8ba2b) chore(deps-dev): Bump `@semantic-release/github` from 12.0.5 to 12.0.6 ([ArcadeData#3897](https://redirect.github.com/markedjs/marked/issues/3897)) * Additional commits viewable in [compare view](markedjs/marked@v15.0.12...v17.0.4) Maintainer changes This version was pushed to npm by [GitHub Actions](<https://www.npmjs.com/~GitHub> Actions), a new releaser for marked since your current version. [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- Dependabot commands and options You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
…/none/single) in WHERE clause (ArcadeData#3891) * fix(opencypher): collect variables from ListPredicateExpression in WHERE clause * feat(opencypher): add filter pushdown for list predicates in WHERE clause
…h list predicates Locks in the PR ArcadeData#3891 fix by asserting that collectVariables() returns the correct outer variables for any/all/none/single predicates used in a comparison, and excludes the loop-scoped iterator from list comprehensions. All six tests fail on the pre-fix WhereClause implementation.
What does this PR do?
Enables filter pushdown optimization for
any(),all(),none(), andsingle()list predicates when used inWHEREclauses.Previously,
WhereClause.collectExpressionVariables()had no branch forListPredicateExpression, so outer variables referenced inside these predicates (e.g.pinp.name) were never collected. This prevented the query optimizer from recognizing which variables these predicates depend on, causing it to skip them as pushdown candidates entirely.This PR adds the missing branch, correctly collecting outer variables while excluding the loop-scoped iterator variable (e.g.
xinany(x IN list WHERE x = p.name)). The same iterator-exclusion logic is also applied consistently to the existingListComprehensionExpressionbranch, which had the same gap.Files changed:
engine/src/main/java/com/arcadedb/query/opencypher/WhereClause.java: addsListPredicateExpressionbranch and fixesListComprehensionExpressionbranch incollectExpressionVariables()engine/src/test/java/com/arcadedb/query/opencypher/OpenCypherWhereClauseTest.java: addsListPredicateFilterPushdownTestnested class with 5 tests coveringany,NOT any,all,none,singleTesting:
testAnyPredicateInWhere,testNotAnyPredicateInWhere,testAllPredicateInWhere,testNonePredicateInWhere,testSinglePredicateInWhereOpenCyphertest suite passes.