From 00d50c65bbce2499ea823a5c78df5ae2ba83ecc5 Mon Sep 17 00:00:00 2001 From: Valentyn Kahamlyk Date: Wed, 23 Aug 2023 17:59:31 -0700 Subject: [PATCH] added documentation --- CHANGELOG.asciidoc | 9 +++ .../src/dev/developer/for-committers.asciidoc | 1 + docs/src/dev/io/graphbinary.asciidoc | 5 ++ docs/src/dev/io/graphson.asciidoc | 20 ++++++ docs/src/reference/the-traversal.asciidoc | 63 +++++++++++++++++++ docs/src/upgrade/release-3.7.x.asciidoc | 21 +++++++ .../traversal/step/map/AsDateStep.java | 3 + .../traversal/step/map/AsDateStepTest.java | 10 ++- .../traversal/step/map/DateDiffStepTest.java | 10 ++- 9 files changed, 137 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index cc08fc1d7fa..be9b3a23bfd 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -20,6 +20,15 @@ limitations under the License. image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/images/gremlin-zamfir.png[width=185] +[[release-3-7.1]] +=== TinkerPop 3.7.1 (Release Date: NOT OFFICIALLY RELEASED YET) + +This release also includes changes from <>. + +* Added date manipulation steps `asDate`, `dateAdd` and `dateDiff`. +* Added new data type `DT` to represent periods of time. +* Added Gherkin support for Date. + [[release-3-7.0]] === TinkerPop 3.7.0 (Release Date: July 31, 2023) diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc index 2785894eaab..31f669aa356 100644 --- a/docs/src/dev/developer/for-committers.asciidoc +++ b/docs/src/dev/developer/for-committers.asciidoc @@ -399,6 +399,7 @@ appropriate types expected by the tests. The syntax of the type notation involves a prefix character to help denote the type, a value between two square brackets, optionally suffixed with some additional notation depending on the primary type. +* Date - *dt[_xxx_]* - The "xxx" should be number of seconds since "the epoch". * Edge - *e[_xxx_]* - The "xxx" should be replaced with a representation of an edge in the form of the `vertex_name-edgelabel->vertex_name`. This syntax may also include the `.id` suffix which would indicate getting the edge identifier or the `.sid` suffix which gets a string representation of the edge identifier. diff --git a/docs/src/dev/io/graphbinary.asciidoc b/docs/src/dev/io/graphbinary.asciidoc index 210c953c791..410cc6f8456 100644 --- a/docs/src/dev/io/graphbinary.asciidoc +++ b/docs/src/dev/io/graphbinary.asciidoc @@ -108,6 +108,7 @@ Changes to existing types require new revision. - `0x2c`: Metrics - `0x2d`: TraversalMetrics - `0x2e`: Merge +- `0x2f`: DT - `0xfe`: Unspecified null object - `0x00`: Custom @@ -572,6 +573,10 @@ Where: Format: a single `String` representing the enum value. +==== DT + +Format: a single `String` representing the enum value. + ==== Custom A custom type, represented as a blob value. diff --git a/docs/src/dev/io/graphson.asciidoc b/docs/src/dev/io/graphson.asciidoc index dcabd8cf8ce..3faea206282 100644 --- a/docs/src/dev/io/graphson.asciidoc +++ b/docs/src/dev/io/graphson.asciidoc @@ -2430,6 +2430,16 @@ The following `Bytecode` example represents the traversal of `g.V().hasLabel('pe } ---- +==== DT + +[source,json] +---- +{ + "@type" : "g:DT", + "@value" : "minute" +} +---- + ==== Lambda [source,json] @@ -4824,6 +4834,16 @@ The following `Bytecode` example represents the traversal of `g.V().hasLabel('pe } ---- +==== DT + +[source,json] +---- +{ + "@type" : "g:DT", + "@value" : "minute" +} +---- + ==== Lambda [source,json] diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index de1a6d723c8..1119818da0c 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -731,6 +731,31 @@ g.V().hasLabel('software').as('a','b','c'). link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#as-java.lang.String-java.lang.String...-++[`as(String,String...)`] +[[asDate-step]] +=== AsDate Step + +The `asDate()`-step (*map*) converts string or numeric input to Date. +For string input only ISO-8601 format is supported. +For numbers, the value is considered as the number of the milliseconds since "the epoch" (January 1, 1970, 00:00:00 GMT). +Date input passed without changes. +If the incoming traverser is not a string, number or Date then an `IllegalArgumentException` will be thrown. + +[gremlin-groovy,modern] +---- +g.inject(1690934400000).asDate() <1> +g.inject("2023-08-02T00:00:00Z").asDate() <2> +g.inject(new Date()).asDate() <3> +---- + +<1> Convert number to Date +<2> Convert ISO-8601 string to Date +<3> Pass Date without modification + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asDate-- +++[`asDate()`] + [[barrier-step]] === Barrier Step @@ -1204,6 +1229,44 @@ g.V(1).as('a').out('created').as('b'). link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#cyclicPath--++[`cyclicPath()`] +[[dateAdd-step]] +=== DateAdd Step + +The `dateAdd()`-step (*map*) returns the value with the addition of the value number of units as specified by the DateToken. +If the incoming traverser is not a Date, then an `IllegalArgumentException` will be thrown. + +[gremlin-groovy,modern] +---- +g.inject("2023-08-02T00:00:00Z").asDate().dateAdd(DT.day, 7) <1> +g.inject(["2023-08-02T00:00:00Z", "2023-08-03T00:00:00Z"]).unfold().asDate().dateAdd(DT.minute, 1) <2> +---- + +<1> Add 7 days to Date +<2> Add 1 minute to incoming dates + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#dateAdd-- +++[`dateAdd()`] + +[[dateDiff-step]] +=== DateDiff Step + +The `dateDiff()`-step (*map*) returns the difference between two Dates in epoch time. +If the incoming traverser is not a Date, then an `IllegalArgumentException` will be thrown. + +[gremlin-groovy,modern] +---- +g.inject("2023-08-02T00:00:00Z").asDate().dateDiff(inject("2023-08-03T00:00:00Z").asDate()) <1> +---- + +<1> Find difference between two dates + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#dateDiff-- +++[`dateDiff()`] + [[dedup-step]] === Dedup Step diff --git a/docs/src/upgrade/release-3.7.x.asciidoc b/docs/src/upgrade/release-3.7.x.asciidoc index 0f6732b0528..0ce32685cf5 100644 --- a/docs/src/upgrade/release-3.7.x.asciidoc +++ b/docs/src/upgrade/release-3.7.x.asciidoc @@ -21,6 +21,27 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima *Gremfir Master of the Pan Flute* +== TinkerPop 3.7.1 + +*Release Date: (Release Date: NOT OFFICIALLY RELEASED YET) + +Please see the link:https://github.com/apache/tinkerpop/blob/3.7.1/CHANGELOG.asciidoc#release-3-7-1[changelog] for a complete list of all the modifications that are part of this release. + +=== Upgrading for Users + +==== Date manipulation functions + +Date manipulations in Gremlin queries were only possible using closures, which may or may not be supported by +different providers. In 3.7.1, we introduce the `asDate()`, `dateAdd` and `dateDiff` steps aimed to replace the usage of closure. + +The following example demonstrates usage of newly introduced steps: + +[source,text] +---- +gremlin> g.inject("2023-08-02T00:00:00Z").asDate().dateAdd(DT.day, 7).dateDiff(datetime("2023-08-02T00:00:00Z")) +==>604800 +---- + == TinkerPop 3.7.0 *Release Date: July 31, 2023* diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java index 2b06ad53f96..8c24c7c363d 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java @@ -48,7 +48,10 @@ protected Date map(final Traverser.Admin traverser) { final Object object = traverser.get(); if (object == null) throw new IllegalArgumentException("Can't parse null as Date."); + if (object instanceof Date) + return (Date) object; if (object instanceof Number) + // numbers handled as milliseconds since January 1, 1970, 00:00:00 GMT. return new Date(((Number) object).longValue()); if (object instanceof String) { return DatetimeHelper.parse((String) object); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStepTest.java index 2a86d39e987..03f10ec4ed4 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStepTest.java @@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; import org.junit.Test; +import java.time.Instant; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Collections; @@ -42,12 +43,17 @@ protected List getTraversals() { @Test public void shouldParseDate() { + + final Instant testInstant = ZonedDateTime.of(2023, 8, 2, 0, 0, 0, 0, UTC).toInstant(); + final Date testDate = new Date(testInstant.getEpochSecond() * 1000); + assertEquals(new Date(1), __.__(1).asDate().next()); assertEquals(new Date(2), __.__(2.0).asDate().next()); assertEquals(new Date(3), __.__(3L).asDate().next()); + assertEquals(testDate, __.__(testDate.getTime()).asDate().next()); - assertEquals(ZonedDateTime.of(2023, 8, 2, 0, 0, 0, 0, UTC).toInstant(), - __.__("2023-08-02T00:00:00Z").asDate().next().toInstant()); + assertEquals(testDate, __.__("2023-08-02T00:00:00Z").asDate().next()); + assertEquals(testDate, __.__(testDate).asDate().next()); } @Test(expected = IllegalArgumentException.class) diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStepTest.java index e4e8bf8fe66..dcf663513c6 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/DateDiffStepTest.java @@ -46,7 +46,7 @@ public void shouldHandlePositiveValues() { cal.add(Calendar.DAY_OF_MONTH, 7); final Date other = cal.getTime(); - assertEquals(604800L, (long)__.__(other).dateDiff(now).next()); + assertEquals(604800L, (long) __.__(other).dateDiff(now).next()); } @Test @@ -58,7 +58,7 @@ public void shouldHandleNegativeValues() { cal.add(Calendar.DAY_OF_MONTH, 7); final Date other = cal.getTime(); - assertEquals(-604800L, (long)__.__(now).dateDiff(other).next()); + assertEquals(-604800L, (long) __.__(now).dateDiff(other).next()); } @Test @@ -70,7 +70,7 @@ public void shouldHandleTraversalParam() { cal.add(Calendar.DAY_OF_MONTH, 7); final Date other = cal.getTime(); - assertEquals(-604800L, (long)__.__(now).dateDiff(__.__(other)).next()); + assertEquals(-604800L, (long) __.__(now).dateDiff(__.__(other)).next()); } @Test @@ -87,4 +87,8 @@ public void shouldHandleNullDate() { assertEquals(now.getTime() / 1000, (long) __.__(now).dateDiff((Date) null).next()); } + @Test(expected = IllegalArgumentException.class) + public void shouldThrowWhenInputIsNotDate() { + __.__("2023-08-23T00:00:00Z").dateDiff(new Date()).next(); + } }