Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] datetime() function #86

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ This release also includes changes from <<release-3-6-5, 3.6.6>> and <<release-3
* Added date manipulation steps `asDate`, `dateAdd` and `dateDiff`.
* Added new data type `DT` to represent periods of time.
* Added Gherkin support for Date.
* Extended `datetime()` function for current server date.
* Added list filtering functions `all` and `any`.
* Added `agent` parameter to `DriverRemoteConnection` options to allow a user-provided `http.Agent` implementation.

Expand Down
47 changes: 28 additions & 19 deletions docs/src/upgrade/release-3.7.x.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -141,25 +141,6 @@ See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#rTrim-step[rTrim()-
See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#reverse-step[reverse()-step]
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2672[TINKERPOP-2672]

==== 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
----

See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asDate-step[asDate()-step]
See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#dateAdd-step[dateAdd()-step]
See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#dateDiff-step[dateDiff()-step]
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2979[TINKERPOP-2979]


===== String Steps replace(), split(), substring()

The following example demonstrates the use of a closure to perform `replace()` and `split()` functions:
Expand Down Expand Up @@ -249,6 +230,34 @@ See: link:https://issues.apache.org/jira/browse/TINKERPOP-2978[TINKERPOP-2978],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#all-step[all()-step],
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#any-step[any()-step]

==== 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
----

See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asDate-step[asDate()-step]
See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#dateAdd-step[dateAdd()-step]
See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#dateDiff-step[dateDiff()-step]
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2979[TINKERPOP-2979]

===== `datetime()` for current server time

Function `datetime()` extended to return current server time when used without argument.

[source,text]
----
gremlin> datetime().toGMTString()
==>13 Oct 2023 20:44:20 GMT
----

=== Upgrading for Providers

==== Graph System Providers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ public final class CoreImports {
uniqueMethods(Lambda.class).forEach(METHOD_IMPORTS::add);
try {
METHOD_IMPORTS.add(DatetimeHelper.class.getMethod("datetime", String.class));
METHOD_IMPORTS.add(DatetimeHelper.class.getMethod("datetime"));
} catch (Exception ex) {
throw new IllegalStateException("Could not load datetime() function to imports");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,8 @@ public Object visitBooleanLiteral(final GremlinParser.BooleanLiteralContext ctx)
*/
@Override
public Object visitDateLiteral(final GremlinParser.DateLiteralContext ctx) {
if (ctx.stringArgument() == null)
return DatetimeHelper.datetime();
return DatetimeHelper.parse((String) antlr.argumentVisitor.visitStringArgument(ctx.stringArgument()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,11 @@ public static Date parse(final String d) {
public static Date datetime(final String d) {
return parse(d);
}

/**
* A proxy allows for syntax similar to Gremlin grammar of {@code datetime()}.
*/
public static Date datetime() {
return new Date();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,18 @@ public void shouldParse() {
}
}

public static class ValidCurrentDateLiteralTest {
@Test
public void shouldParseCurrentDate() {
final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString("datetime()"));
final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer));
final GremlinParser.DateLiteralContext ctx = parser.dateLiteral();

final Date dt = (Date) new GenericLiteralVisitor(new GremlinAntlrToJava()).visitDateLiteral(ctx);
assertTrue(new Date().getTime() - dt.getTime() < 1000);
}
}

@RunWith(Parameterized.class)
public static class ValidBooleanLiteralTest {
@Parameterized.Parameter(value = 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ private static IDictionary<string, List<Func<GraphTraversalSource, IDictionary<s
{"g_injectXdatetimeXstrXX_dateAddXminute_10X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(DateTimeOffset.FromUnixTimeMilliseconds(1690934400000)).DateAdd(DT.Minute,10)}},
{"g_injectXdatetimeXstrXX_dateAddXsecond_20X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(DateTimeOffset.FromUnixTimeMilliseconds(1690934400000)).DateAdd(DT.Second,20)}},
{"g_injectXdatetimeXstrXX_dateAddXday_11X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(DateTimeOffset.FromUnixTimeMilliseconds(1693958400000)).DateAdd(DT.Day,11)}},
{"g_injectXdatetimeXXX_dateDiffXdatetimeXstrXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(DateTimeOffset.FromUnixTimeMilliseconds(1697150417516)).DateDiff(DateTimeOffset.FromUnixTimeMilliseconds(1673481600000)).Is(P.Gt(0))}},
{"g_injectXdatetimeXstr1XX_dateDiffXdatetimeXstr2XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(DateTimeOffset.FromUnixTimeMilliseconds(1690934400000)).DateDiff(DateTimeOffset.FromUnixTimeMilliseconds(1691539200000))}},
{"g_injectXdatetimeXstr1XX_dateDiffXinjectXdatetimeXstr2XXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(DateTimeOffset.FromUnixTimeMilliseconds(1691452800000)).DateDiff(__.Inject(DateTimeOffset.FromUnixTimeMilliseconds(1690848000000)))}},
{"g_V_EX11X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().E(p["eid11"])}},
Expand Down Expand Up @@ -633,17 +634,17 @@ private static IDictionary<string, List<Func<GraphTraversalSource, IDictionary<s
{"g_V_hasLabelXsoftwareX_name_fold_orderXlocalX_index_unfold_order_byXtailXlocal_1XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values<object>("name").Fold().Order(Scope.Local).Index<object>().Unfold<object>().Order().By(__.Tail<object>(Scope.Local,1))}},
{"g_V_hasLabelXpersonX_name_fold_orderXlocalX_index_withXmapX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values<object>("name").Fold().Order(Scope.Local).Index<object>().With("~tinkerpop.index.indexer",1)}},
{"g_VX1X_valuesXageX_index_unfold_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Index<object>().Unfold<object>().Unfold<object>()}},
{"g_injectX__feature___test__nullX_lTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(" feature"," one test",null,""," ").LTrim()}},
{"g_injectX__feature__X_lTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(" feature ").LTrim()}},
{"g_injectXListXa_bXX_lTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).LTrim()}},
{"g_V_valuesXnameX_lTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name"," marko ").Property("age",29).As("marko").AddV("person").Property("name"," vadas ").Property("age",27).As("vadas").AddV("software").Property("name"," lop").Property("lang","java").As("lop").AddV("person").Property("name","josh ").Property("age",32).As("josh").AddV("software").Property("name"," ripple ").Property("lang","java").As("ripple").AddV("person").Property("name","peter").Property("age",35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight",0.5).AddE("knows").From("marko").To("josh").Property("weight",1.0).AddE("created").From("marko").To("lop").Property("weight",0.4).AddE("created").From("josh").To("ripple").Property("weight",1.0).AddE("created").From("josh").To("lop").Property("weight",0.4).AddE("created").From("peter").To("lop").Property("weight",0.2), (g,p) =>g.V().Values<object>("name").LTrim()}},
{"g_injectXfeature_test_nullX_length", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature","test",null).Length()}},
{"g_injectXListXa_bXX_length", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Length()}},
{"g_V_valuesXnameX_length", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Length()}},
{"g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX3XX_hasXname_peterX_path_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Repeat(__.Both().SimplePath()).Until(__.Has("name","peter").Or().Loops().Is(3)).Has("name","peter").Path().By("name")}},
{"g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_or_loops_isX2XX_hasXname_peterX_path_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Repeat(__.Both().SimplePath()).Until(__.Has("name","peter").Or().Loops().Is(2)).Has("name","peter").Path().By("name")}},
{"g_VX1X_repeatXboth_simplePathX_untilXhasXname_peterX_and_loops_isX3XX_hasXname_peterX_path_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Repeat(__.Both().SimplePath()).Until(__.Has("name","peter").And().Loops().Is(3)).Has("name","peter").Path().By("name")}},
{"g_V_emitXhasXname_markoX_or_loops_isX2XX_repeatXoutX_valuesXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Emit(__.Has("name","marko").Or().Loops().Is(2)).Repeat(__.Out()).Values<object>("name")}},
{"g_injectX__feature___test__nullX_lTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(" feature"," one test",null,""," ").LTrim()}},
{"g_injectX__feature__X_lTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(" feature ").LTrim()}},
{"g_injectXListXa_bXX_lTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).LTrim()}},
{"g_V_valuesXnameX_lTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name"," marko ").Property("age",29).As("marko").AddV("person").Property("name"," vadas ").Property("age",27).As("vadas").AddV("software").Property("name"," lop").Property("lang","java").As("lop").AddV("person").Property("name","josh ").Property("age",32).As("josh").AddV("software").Property("name"," ripple ").Property("lang","java").As("ripple").AddV("person").Property("name","peter").Property("age",35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight",0.5).AddE("knows").From("marko").To("josh").Property("weight",1.0).AddE("created").From("marko").To("lop").Property("weight",0.4).AddE("created").From("josh").To("ripple").Property("weight",1.0).AddE("created").From("josh").To("lop").Property("weight",0.4).AddE("created").From("peter").To("lop").Property("weight",0.2), (g,p) =>g.V().Values<object>("name").LTrim()}},
{"g_VX1X_mapXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Map<object>((IFunction) p["l1"])}},
{"g_VX1X_outE_label_mapXlengthX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).OutE().Label().Map<object>((IFunction) p["l1"])}},
{"g_VX1X_out_mapXnameX_mapXlengthX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Map<object>((IFunction) p["l1"]).Map<object>((IFunction) p["l2"])}},
Expand Down Expand Up @@ -906,17 +907,17 @@ private static IDictionary<string, List<Func<GraphTraversalSource, IDictionary<s
{"g_V_hasXageX_propertiesXage_nameX_value", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age").Properties<object>("age","name").Value<object>()}},
{"g_V_propertiesXname_age_nullX_value", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Properties<object>("name","age",null).Value<object>()}},
{"g_V_valuesXname_age_nullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name","age",null)}},
{"g_injectX__feature___test__nullX_rTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature ","one test ",null,""," ").RTrim()}},
{"g_injectX__feature__X_rTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(" feature ").RTrim()}},
{"g_injectXListXa_bXX_rTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).RTrim()}},
{"g_V_valuesXnameX_rTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name"," marko ").Property("age",29).As("marko").AddV("person").Property("name"," vadas ").Property("age",27).As("vadas").AddV("software").Property("name"," lop").Property("lang","java").As("lop").AddV("person").Property("name","josh ").Property("age",32).As("josh").AddV("software").Property("name"," ripple ").Property("lang","java").As("ripple").AddV("person").Property("name","peter").Property("age",35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight",0.5).AddE("knows").From("marko").To("josh").Property("weight",1.0).AddE("created").From("marko").To("lop").Property("weight",0.4).AddE("created").From("josh").To("ripple").Property("weight",1.0).AddE("created").From("josh").To("lop").Property("weight",0.4).AddE("created").From("peter").To("lop").Property("weight",0.2), (g,p) =>g.V().Values<object>("name").RTrim()}},
{"g_injectXthat_this_testX_replaceXh_jX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("that","this","test",null).Replace("h","j")}},
{"g_injectXListXa_bXcX_replaceXa_bX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Replace("a","b")}},
{"g_V_hasLabelXsoftwareX_valueXnameX_replaceXnull_iX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values<object>("name").Replace(null,"g")}},
{"g_V_hasLabelXsoftwareX_valueXnameX_replaceXa_iX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values<object>("name").Replace("p","g")}},
{"g_injectXfeature_test_nullX_reverse", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature","test one",null).Reverse()}},
{"g_injectXListXa_bXX_reverse", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Reverse()}},
{"g_V_valuesXnameX_reverse", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Reverse()}},
{"g_injectX__feature___test__nullX_rTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("feature ","one test ",null,""," ").RTrim()}},
{"g_injectX__feature__X_rTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(" feature ").RTrim()}},
{"g_injectXListXa_bXX_rTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).RTrim()}},
{"g_V_valuesXnameX_rTrim", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name"," marko ").Property("age",29).As("marko").AddV("person").Property("name"," vadas ").Property("age",27).As("vadas").AddV("software").Property("name"," lop").Property("lang","java").As("lop").AddV("person").Property("name","josh ").Property("age",32).As("josh").AddV("software").Property("name"," ripple ").Property("lang","java").As("ripple").AddV("person").Property("name","peter").Property("age",35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight",0.5).AddE("knows").From("marko").To("josh").Property("weight",1.0).AddE("created").From("marko").To("lop").Property("weight",0.4).AddE("created").From("josh").To("ripple").Property("weight",1.0).AddE("created").From("josh").To("lop").Property("weight",0.4).AddE("created").From("peter").To("lop").Property("weight",0.2), (g,p) =>g.V().Values<object>("name").RTrim()}},
{"g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a","b")}},
{"g_VX1X_asXaX_outXknowsX_asXbX_selectXa_bX_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a","b").By("name")}},
{"g_VX1X_asXaX_outXknowsX_asXbX_selectXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").Out("knows").As("b").Select<object>("a")}},
Expand Down
Loading