Conversation
Reached much better compliance with the latest changes:
```
=======================================================
OpenCypher TCK Compliance Report
=======================================================
Total scenarios: 3897
Passed: 1908 (48%)
Failed: 1939 (49%)
Skipped: 50 (1%)
-------------------------------------------------------
By category:
clauses/call 2/ 52 passed (3%)
clauses/create 64/ 78 passed (82%)
clauses/delete 24/ 41 passed (58%)
clauses/match 292/381 passed (76%)
clauses/match-where 25/ 34 passed (73%)
clauses/merge 47/ 75 passed (62%)
clauses/remove 29/ 33 passed (87%)
clauses/return 35/ 63 passed (55%)
clauses/return-orderby 23/ 35 passed (65%)
clauses/return-skip-limit 26/ 31 passed (83%)
clauses/set 30/ 53 passed (56%)
clauses/union 8/ 12 passed (66%)
clauses/unwind 10/ 14 passed (71%)
clauses/with 14/ 29 passed (48%)
clauses/with-orderBy 124/292 passed (42%)
clauses/with-skip-limit 7/ 9 passed (77%)
clauses/with-where 10/ 19 passed (52%)
expressions/aggregation 23/ 35 passed (65%)
expressions/boolean 150/150 passed (100%)
expressions/comparison 36/ 72 passed (50%)
expressions/conditional 13/ 13 passed (100%)
expressions/existentialSubqueries 4/ 10 passed (40%)
expressions/graph 32/ 61 passed (52%)
expressions/list 120/185 passed (64%)
expressions/literals 120/131 passed (91%)
expressions/map 28/ 44 passed (63%)
expressions/mathematical 3/ 6 passed (50%)
expressions/null 44/ 44 passed (100%)
expressions/path 0/ 7 passed (0%)
expressions/pattern 19/ 50 passed (38%)
expressions/precedence 20/121 passed (16%)
expressions/quantifier 478/604 passed (79%)
expressions/string 22/ 32 passed (68%)
expressions/temporal 0/1004 passed (0%)
expressions/typeConversion 19/ 47 passed (40%)
useCases/countingSubgraphMatches 6/ 11 passed (54%)
useCases/triadicSelection 1/ 19 passed (5%)
=======================================================
```
Summary of ChangesHello @lvca, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly expands the OpenCypher implementation by introducing comprehensive support for temporal data types and functions. It enables users to work with dates, times, datetimes, and durations in a Cypher-compliant manner, including advanced parsing, property access, arithmetic operations, and various utility functions. The changes also include crucial parser improvements to ensure correct expression evaluation and seamless integration of temporal values with ArcadeDB's storage mechanisms. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
🧪 CI InsightsHere's what we observed from your CI run for 79b5e5c. 🟢 All jobs passed!But CI Insights is watching 👀 |
PR Review: OpenCypher TCK Part 3 - Temporal SupportThank you for this comprehensive implementation of OpenCypher temporal types and functions. This is a substantial addition (2,616 additions across 18 files) that adds critical temporal functionality to ArcadeDB's OpenCypher support. Here's my detailed review: ✅ Strengths1. Comprehensive Temporal Implementation
2. Well-Structured Architecture
3. Good Code Quality
🔍 Issues & RecommendationsCRITICAL: Potential Arithmetic BugLocation: The removal of // Old: final boolean useInteger = ... && operator != Operator.DIVIDE && operator != Operator.POWER;
// New: final boolean useInteger = ... && operator != Operator.POWER;Issue: Integer division now uses truncation ( Example:
Recommendation:
MEDIUM: Duration Comparison ApproximationLocation: // Approximate comparison: convert everything to nanoseconds
final long thisTotal = (months * 30 * 86400 + days * 86400 + seconds) * 1_000_000_000L + nanosAdjustment;Issue: Duration comparison uses approximation (months = 30 days), which can lead to unexpected results. Example:
Recommendation:
MEDIUM: Fractional Month ApproximationLocation: double totalDays = weeks * 7 + days + fracMonths * 30; // fractional months → approximate daysIssue: Fractional months converted to days using 30-day approximation. Impact: Recommendation: Document this approximation or consider alternative approach. LOW: Negative Duration String FormattingLocation: The Example: Recommendation: Add unit tests specifically for negative durations to ensure correct behavior. LOW: Division by Zero Handling InconsistencyLocations:
Recommendation: Standardize zero-division behavior across the codebase. OpenCypher spec should guide this. LOW: Type Coercion in Helper MethodsLocation: The Recommendation: Add input validation or wrap in try-catch with better error messages. 🧪 Testing ConcernsMissing Test Evidence
Recommendation:
Suggested Test Cases// Duration arithmetic
RETURN duration({days: 1}) + duration({hours: 2})
// Temporal arithmetic with ArcadeDB LocalDate storage
CREATE (n:Event {date: date('2024-01-15')})
RETURN n.date + duration({months: 1})
// Negative durations
RETURN duration({seconds: -5}) * 2
// Zero division
RETURN duration({days: 1}) / 0
// Timezone conversions
RETURN datetime({timezone: 'Europe/Stockholm'})
// Property access
RETURN date('2024-01-15').quarter🎯 Code Style & Best Practices✅ Follows Project Conventions
✅ Good Documentation
|
Pull Request Review: OpenCypher TCK Part 3This is an impressive implementation that significantly improves OpenCypher compliance (from ~41% to ~48%). The PR introduces comprehensive temporal support and fixes critical issues with operator precedence and type comparisons. Here's my detailed review: ✅ Strengths1. Comprehensive Temporal Implementation
2. Robust Type System
3. Arithmetic Improvements
4. Function Coverage
🐛 Issues & Concerns1. Division by Zero Handling Inconsistency
|
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive support for temporal data types (Date, Time, LocalDateTime, DateTime, Duration) in OpenCypher expressions. Key changes include adding new CypherTemporalValue wrapper classes for java.time types, implementing temporal arithmetic in ArithmeticExpression, and enabling temporal comparisons in ComparisonExpression. Property access for temporal values (e.g., date.year) is now supported, and numerous new temporal functions (constructors, truncation, epoch, duration calculations) have been added to CypherFunctionFactory. The parsing logic in CypherExpressionBuilder was refactored to correctly handle operator precedence, and list operations were extended to include concatenation. Additionally, CreateStep now converts CypherTemporalValue objects to their underlying java.time types for storage, and toString(), toInteger(), toFloat(), toBoolean() functions were updated for better type handling and error reporting. Review comments highlighted the need to improve robustness in generic CypherTemporalValue conversion, address inefficient object creation in list comparisons, and correct the compareTo method for CypherDuration to align with OpenCypher specifications regarding ordering, as well as refactor duplicated temporal arithmetic logic.
| if (args[0] instanceof CypherTemporalValue) { | ||
| // Generic temporal → extract date part | ||
| final CypherTemporalValue tv = (CypherTemporalValue) args[0]; | ||
| final Object year = tv.getTemporalProperty("year"); | ||
| if (year != null) | ||
| return new CypherDate(LocalDate.of(((Number) year).intValue(), | ||
| ((Number) tv.getTemporalProperty("month")).intValue(), | ||
| ((Number) tv.getTemporalProperty("day")).intValue())); | ||
| } |
There was a problem hiding this comment.
The fallback logic for converting a generic CypherTemporalValue to a CypherDate is not robust. It checks for the existence of the year property and then assumes that month and day also exist and are of type Number. This could lead to a NullPointerException or ClassCastException at runtime if a temporal value has a year but not the other components. It's safer to check for the existence and type of all required properties before using them.
if (args[0] instanceof CypherTemporalValue) {
// Generic temporal → extract date part
final CypherTemporalValue tv = (CypherTemporalValue) args[0];
final Object year = tv.getTemporalProperty("year");
final Object month = tv.getTemporalProperty("month");
final Object day = tv.getTemporalProperty("day");
if (year instanceof Number && month instanceof Number && day instanceof Number) {
return new CypherDate(LocalDate.of(((Number) year).intValue(),
((Number) month).intValue(),
((Number) day).intValue()));
}
}| public int compareTo(final CypherTemporalValue other) { | ||
| if (other instanceof CypherDuration d) { | ||
| // Approximate comparison: convert everything to nanoseconds | ||
| final long thisTotal = (months * 30 * 86400 + days * 86400 + seconds) * 1_000_000_000L + nanosAdjustment; | ||
| final long otherTotal = (d.months * 30 * 86400 + d.days * 86400 + d.seconds) * 1_000_000_000L + d.nanosAdjustment; | ||
| return Long.compare(thisTotal, otherTotal); | ||
| } | ||
| throw new IllegalArgumentException("Cannot compare Duration with " + other.getClass().getSimpleName()); |
There was a problem hiding this comment.
The compareTo method provides an approximate ordering for durations by converting months to an average of 30 days. However, the openCypher specification states that durations can only be compared for equality, not for ordering (<, >, etc.). This implementation could lead to incorrect or unexpected results for ordering comparisons.
To align with the specification, this method should throw an IllegalArgumentException. ComparisonExpression should then be updated to handle CypherDuration equality checks separately using the .equals() method, and return null for ordering comparisons.
@Override
public int compareTo(final CypherTemporalValue other) {
if (other instanceof CypherDuration) {
// Per openCypher spec, Durations can only be compared for equality, not ordering.
throw new IllegalArgumentException("Durations cannot be ordered.");
}
throw new IllegalArgumentException("Cannot compare Duration with " + other.getClass().getSimpleName());
}| if (leftValue instanceof LocalDate ld && rightValue instanceof CypherDuration dur) { | ||
| final LocalDate d = ld.plusMonths(operator == Operator.ADD ? dur.getMonths() : -dur.getMonths()) | ||
| .plusDays(operator == Operator.ADD ? dur.getDays() : -dur.getDays()); | ||
| return new CypherDate(d); | ||
| } | ||
|
|
||
| // java.time.LocalDateTime (from ArcadeDB storage) ± Duration | ||
| if (leftValue instanceof LocalDateTime ldt && rightValue instanceof CypherDuration dur) { | ||
| LocalDateTime dt = ldt | ||
| .plusMonths(operator == Operator.ADD ? dur.getMonths() : -dur.getMonths()) | ||
| .plusDays(operator == Operator.ADD ? dur.getDays() : -dur.getDays()) | ||
| .plusSeconds(operator == Operator.ADD ? dur.getSeconds() : -dur.getSeconds()) | ||
| .plusNanos(operator == Operator.ADD ? dur.getNanosAdjustment() : -dur.getNanosAdjustment()); | ||
| return new CypherLocalDateTime(dt); | ||
| } | ||
|
|
||
| // Date ± Duration | ||
| if (leftValue instanceof CypherDate cd && rightValue instanceof CypherDuration dur) { | ||
| final LocalDate d = cd.getValue().plusMonths(operator == Operator.ADD ? dur.getMonths() : -dur.getMonths()) | ||
| .plusDays(operator == Operator.ADD ? dur.getDays() : -dur.getDays()); | ||
| return new CypherDate(d); | ||
| } | ||
|
|
||
| // LocalTime ± Duration | ||
| if (leftValue instanceof CypherLocalTime clt && rightValue instanceof CypherDuration dur) { | ||
| final long totalNanos = dur.getSeconds() * 1_000_000_000L + dur.getNanosAdjustment(); | ||
| final LocalTime t = operator == Operator.ADD ? clt.getValue().plusNanos(totalNanos) : clt.getValue().minusNanos(totalNanos); | ||
| return new CypherLocalTime(t); | ||
| } | ||
|
|
||
| // Time ± Duration | ||
| if (leftValue instanceof CypherTime ct && rightValue instanceof CypherDuration dur) { | ||
| final long totalNanos = dur.getSeconds() * 1_000_000_000L + dur.getNanosAdjustment(); | ||
| final OffsetTime t = operator == Operator.ADD ? ct.getValue().plusNanos(totalNanos) : ct.getValue().minusNanos(totalNanos); | ||
| return new CypherTime(t); | ||
| } | ||
|
|
||
| // LocalDateTime ± Duration | ||
| if (leftValue instanceof CypherLocalDateTime cldt && rightValue instanceof CypherDuration dur) { | ||
| LocalDateTime dt = cldt.getValue() | ||
| .plusMonths(operator == Operator.ADD ? dur.getMonths() : -dur.getMonths()) | ||
| .plusDays(operator == Operator.ADD ? dur.getDays() : -dur.getDays()) | ||
| .plusSeconds(operator == Operator.ADD ? dur.getSeconds() : -dur.getSeconds()) | ||
| .plusNanos(operator == Operator.ADD ? dur.getNanosAdjustment() : -dur.getNanosAdjustment()); | ||
| return new CypherLocalDateTime(dt); | ||
| } | ||
|
|
||
| // DateTime ± Duration | ||
| if (leftValue instanceof CypherDateTime cdt && rightValue instanceof CypherDuration dur) { | ||
| ZonedDateTime dt = cdt.getValue() | ||
| .plusMonths(operator == Operator.ADD ? dur.getMonths() : -dur.getMonths()) | ||
| .plusDays(operator == Operator.ADD ? dur.getDays() : -dur.getDays()) | ||
| .plusSeconds(operator == Operator.ADD ? dur.getSeconds() : -dur.getSeconds()) | ||
| .plusNanos(operator == Operator.ADD ? dur.getNanosAdjustment() : -dur.getNanosAdjustment()); | ||
| return new CypherDateTime(dt); | ||
| } |
There was a problem hiding this comment.
There is significant code duplication in evaluateTemporalArithmetic for handling java.time types and their corresponding Cypher* wrapper types. For example, the logic for LocalDate and CypherDate is nearly identical, as is the logic for LocalDateTime and CypherLocalDateTime, and ZonedDateTime and CypherDateTime. This can be refactored by extracting helper methods to reduce redundancy and improve maintainability.
| for (int i = 0; i < leftList.size(); i++) { | ||
| final Object elemResult = new ComparisonExpression( | ||
| new LiteralExpression(leftList.get(i), ""), Operator.EQUALS, | ||
| new LiteralExpression(rightList.get(i), "")) | ||
| .evaluateTernary(null, null); | ||
| if (elemResult == null) | ||
| hasNull = true; | ||
| else if (!Boolean.TRUE.equals(elemResult)) | ||
| return operator == Operator.NOT_EQUALS; |
There was a problem hiding this comment.
Creating new ComparisonExpression and LiteralExpression objects within a loop for each element comparison is inefficient. This can lead to performance issues when comparing large lists due to excessive object allocation. Consider refactoring this to avoid object creation in the loop, for example by extracting the comparison logic into a static helper method that can be called directly with the element values.
Coverage summary from CodacySee diff coverage on Codacy
Coverage variation details
Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: Diff coverage details
Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: See your quality gate settings Change summary preferences |
* test: including official opencypher tck
* fix: fixed many issues with opencypher from TCK tests
* fix: fixed broken tests from OpenCyphr TCK
* fix: more opencypher issues from tck tests
* fix: opencypher tck more tests pass now
Reached much better compliance with the latest changes:
```
=======================================================
OpenCypher TCK Compliance Report
=======================================================
Total scenarios: 3897
Passed: 1908 (48%)
Failed: 1939 (49%)
Skipped: 50 (1%)
-------------------------------------------------------
By category:
clauses/call 2/ 52 passed (3%)
clauses/create 64/ 78 passed (82%)
clauses/delete 24/ 41 passed (58%)
clauses/match 292/381 passed (76%)
clauses/match-where 25/ 34 passed (73%)
clauses/merge 47/ 75 passed (62%)
clauses/remove 29/ 33 passed (87%)
clauses/return 35/ 63 passed (55%)
clauses/return-orderby 23/ 35 passed (65%)
clauses/return-skip-limit 26/ 31 passed (83%)
clauses/set 30/ 53 passed (56%)
clauses/union 8/ 12 passed (66%)
clauses/unwind 10/ 14 passed (71%)
clauses/with 14/ 29 passed (48%)
clauses/with-orderBy 124/292 passed (42%)
clauses/with-skip-limit 7/ 9 passed (77%)
clauses/with-where 10/ 19 passed (52%)
expressions/aggregation 23/ 35 passed (65%)
expressions/boolean 150/150 passed (100%)
expressions/comparison 36/ 72 passed (50%)
expressions/conditional 13/ 13 passed (100%)
expressions/existentialSubqueries 4/ 10 passed (40%)
expressions/graph 32/ 61 passed (52%)
expressions/list 120/185 passed (64%)
expressions/literals 120/131 passed (91%)
expressions/map 28/ 44 passed (63%)
expressions/mathematical 3/ 6 passed (50%)
expressions/null 44/ 44 passed (100%)
expressions/path 0/ 7 passed (0%)
expressions/pattern 19/ 50 passed (38%)
expressions/precedence 20/121 passed (16%)
expressions/quantifier 478/604 passed (79%)
expressions/string 22/ 32 passed (68%)
expressions/temporal 0/1004 passed (0%)
expressions/typeConversion 19/ 47 passed (40%)
useCases/countingSubgraphMatches 6/ 11 passed (54%)
useCases/triadicSelection 1/ 19 passed (5%)
=======================================================
```
* fix: opencypher implemented missing temporal functions + precedence
Issue #3357 and #3355
* fix: opencypher implemented more missing functions
Issue #3357 and #3355
(cherry picked from commit 918725f)
Additional cleanup