Skip to content

added neo4j functions tests#3949

Merged
robfrank merged 37 commits intomainfrom
feat/more-cypher-tests
Apr 22, 2026
Merged

added neo4j functions tests#3949
robfrank merged 37 commits intomainfrom
feat/more-cypher-tests

Conversation

@robfrank
Copy link
Copy Markdown
Collaborator

What does this PR do?

pick #3428 , rebase it,

Pierre F and others added 30 commits April 22, 2026 08:25
…penCypherAggregatingFunctionsComprehensiveTest.java

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…penCypherAggregatingFunctionsComprehensiveTest.java

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
ExtReMLapin and others added 5 commits April 22, 2026 08:28
…tion.java

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented Apr 22, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 80 complexity

Metric Results
Complexity 80

View in Codacy

🟢 Coverage 88.74% diff coverage · -8.63% coverage variation

Metric Results
Coverage variation -8.63% coverage variation
Diff coverage 88.74% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (4cb5061) 118612 87094 73.43%
Head commit (e8507a7) 150156 (+31544) 97293 (+10199) 64.79% (-8.63%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#3949) 373 331 88.74%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes. Give us feedback

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly expands OpenCypher function support, adding geospatial, vector, string, and temporal capabilities alongside comprehensive tests. Feedback focuses on aligning implementations with Cypher specifications, specifically for substring(), split(), and point.distance(). The review also identifies potential NullPointerException and NumberFormatException risks in edge cases and cautions against modifying shared SQL functions, like standard deviation, which could break standard SQL semantics.

Comment thread engine/src/main/java/com/arcadedb/function/geo/CypherPointDistanceFunction.java Outdated
Comment on lines +69 to +74
final Number z1n = (Number) p1.get("z");
final Number z2n = (Number) p2.get("z");
if (z1n != null && z2n != null) {
final double dz = z2n.doubleValue() - z1n.doubleValue();
sumSq += dz * dz;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to Cypher specifications (e.g., Neo4j), point.distance() should return null if the points have different dimensions (e.g., comparing a 2D point with a 3D point). The current implementation falls back to a 2D distance calculation if one of the points is missing the 'z' coordinate, which may lead to inconsistent results compared to standard Cypher behavior.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Added if ((z1n == null) != (z2n == null)) return null; before the z-distance calculation. Mixed 2D/3D Cartesian points now return null per Cypher spec. Added regression test pointDistanceMixedDimensions.

if (variance != null)
return Math.sqrt((Double) variance);
return null;
return 0.0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Changing the return value from null to 0.0 when the variance is null aligns with Cypher's stDev() behavior for empty sets, but it breaks standard SQL semantics where aggregate functions over an empty set (except COUNT) should return NULL. Since this class is part of the SQL function package, this change might cause regressions in SQL queries. Consider handling the null to 0.0 conversion in the Cypher-specific layer instead.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 0.0 return is intentional. The Cypher tests stDevNull and stDevPNull in OpenCypherAggregatingFunctionsComprehensiveTest explicitly assert 0.0 for stDev([null, null]), matching Neo4j's documented behavior. No SQL test currently asserts that stddev returns null on an empty/all-null set - the SQL test suite only covers non-empty datasets. Moving this to a Cypher layer would be a larger refactor beyond the scope of this PR.

Comment on lines +93 to +101
if (vectorObj instanceof String s) {
final String trimmed = s.trim();
final String inner = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.substring(1, trimmed.length() - 1) : trimmed;
final String[] parts = inner.split(",");
final float[] result = new float[parts.length];
for (int i = 0; i < parts.length; i++)
result[i] = Float.parseFloat(parts[i].trim());
return result;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Parsing a vector from a string will fail with a NumberFormatException if the string represents an empty array (e.g., "[]"). In this case, inner becomes an empty string, and split(",") returns an array containing one empty string, which Float.parseFloat() cannot handle. An explicit check for an empty string after trimming and removing brackets should be added.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Added an inner.isEmpty() check before splitting - returns new float[0] for "[]" input, avoiding the NumberFormatException.

Comment on lines +41 to +43
final String str = args[0].toString();
final int start = ((Number) args[1]).intValue();
if (start < 0)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In Cypher, if the start index for substring() is negative, it is treated as 0. The current implementation throws a CommandExecutionException, which deviates from standard Cypher behavior. You should use Math.max(0, ...) for the start index instead of throwing an error.

Suggested change
final String str = args[0].toString();
final int start = ((Number) args[1]).intValue();
if (start < 0)
final int start = Math.max(0, ((Number) args[1]).intValue());

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushing back. The test substringNegativeStartRaisesError in OpenCypherStringFunctionsComprehensiveTest explicitly verifies that a negative start index raises an error. Neo4j 5.x also throws InvalidArgumentException for negative start values in substring(). The current behavior is intentional and tested.

robfrank and others added 2 commits April 22, 2026 08:43
- CypherPointDistanceFunction: null-check WGS-84 coordinate values before
  calling doubleValue() to avoid NPE; return null for mixed 2D/3D Cartesian
  points per Cypher spec
- VectorUtils: return empty float[] for empty array string "[]" instead of
  throwing NumberFormatException
- CypherSplitFunction: use split limit -1 to preserve trailing empty strings
  per Cypher spec (e.g. split('a,b,',',') -> ['a','b',''])
- Add regression tests for mixed-dimension point.distance() and trailing
  delimiter split()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
elementId() was implemented in createCypherSpecificExecutor but missing
from isCypherSpecificFunction, causing an "Unknown function" error at runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@robfrank robfrank merged commit 582167a into main Apr 22, 2026
19 of 25 checks passed
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 78.01609% with 82 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.85%. Comparing base (4cb5061) to head (e8507a7).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
...com/arcadedb/function/geo/CypherPointFunction.java 74.60% 6 Missing and 10 partials ⚠️
...dedb/function/geo/CypherPointDistanceFunction.java 73.80% 5 Missing and 6 partials ⚠️
.../query/opencypher/executor/CypherTrimFunction.java 72.50% 5 Missing and 6 partials ⚠️
...y/opencypher/executor/CypherSubstringFunction.java 66.66% 3 Missing and 3 partials ⚠️
...java/com/arcadedb/function/math/RoundFunction.java 64.28% 2 Missing and 3 partials ⚠️
...n/java/com/arcadedb/function/sql/geo/GeoUtils.java 44.44% 4 Missing and 1 partial ⚠️
...in/java/com/arcadedb/index/vector/VectorUtils.java 60.00% 1 Missing and 3 partials ⚠️
.../com/arcadedb/function/text/SubstringFunction.java 0.00% 1 Missing and 2 partials ⚠️
...query/opencypher/executor/CypherSplitFunction.java 66.66% 2 Missing and 1 partial ⚠️
...edb/function/temporal/DateConstructorFunction.java 71.42% 2 Missing ⚠️
... and 8 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3949      +/-   ##
==========================================
- Coverage   64.46%   63.85%   -0.62%     
==========================================
  Files        1587     1591       +4     
  Lines      118612   118917     +305     
  Branches    25194    25271      +77     
==========================================
- Hits        76468    75932     -536     
- Misses      31548    32508     +960     
+ Partials    10596    10477     -119     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@robfrank robfrank added this to the 26.4.1 milestone Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants