This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch asbool-step in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 8de45e454d532ace1508aa68d9fa794b83e49d10 Author: Alexey Temnikov <[email protected]> AuthorDate: Fri Jun 6 23:08:01 2025 -0700 asbool step initial implementation --- CHANGELOG.asciidoc | 1 + docs/src/dev/provider/gremlin-semantics.asciidoc | 25 +++++++++ docs/src/reference/the-traversal.asciidoc | 20 +++++++ .../grammar/DefaultGremlinBaseVisitor.java | 14 +++-- .../language/grammar/TraversalMethodVisitor.java | 8 +++ .../traversal/dsl/graph/GraphTraversal.java | 13 +++++ .../gremlin/process/traversal/dsl/graph/__.java | 7 +++ .../process/traversal/step/map/AsBoolStep.java | 60 ++++++++++++++++++++ .../process/traversal/step/map/AsBoolStepTest.java | 53 ++++++++++++++++++ .../Process/Traversal/GraphTraversal.cs | 9 +++ .../src/Gremlin.Net/Process/Traversal/__.cs | 8 +++ .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 14 +++-- gremlin-go/driver/anonymousTraversal.go | 7 ++- gremlin-go/driver/cucumber/gremlin.go | 8 ++- gremlin-go/driver/graphTraversal.go | 10 +++- .../lib/process/graph-traversal.js | 11 ++++ .../gremlin-javascript/test/cucumber/gremlin.js | 8 ++- gremlin-language/src/main/antlr4/Gremlin.g4 | 6 ++ .../gremlin_python/process/graph_traversal.py | 12 ++++ gremlin-python/src/main/python/radish/gremlin.py | 8 ++- .../gremlin/test/features/map/AsBool.feature | 65 ++++++++++++++++++++++ 21 files changed, 348 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 30aa4d1d6c..8699e308d5 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -69,6 +69,7 @@ This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>. * Added and made `OffsetDateTime` serializers the default for existing date types in Python, Go, JavaScript, and .NET. `Date` is only used to deserialize from server. * Added missing strategies in `gremlin-go`, updated certain strategies to take varargs and updated `GoTranslatorVisitor` for corresponding translations. * Fixed `BigInt` and `BigDecimal` parsing in `gremlin-go` cucumber test suite, fixed `UnscaledValue` type in `BigDecimal` struct and added `ParseBigDecimal` method. +* Added boolean parsing step `asBool()`. * Added validation to `valueMap()`, `propertyMap()`, `groupCount()`, `sack()`, `dedup()`, `sample()`, and `aggregate()` to prevent the invalid usage of multiple `by()` modulators. * Deprecated `ProcessLimitedStandardSuite` and `ProcessLimitedComputerSuite` in favor of `ProcessEmbeddedStandardSuite` and `ProcessEmbeddedComputerSuite` respectively. * Deprecated `ProcessStandardSuite` and the `ProcessComputerSuite` in favor of Gherkin testing and the `ProcessEmbeddedStandardSuite` and `ProcessEmbeddedComputerSuite` for testing JVM-specific Gremlin behaviors. diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc index a499a015fe..5966360fb5 100644 --- a/docs/src/dev/provider/gremlin-semantics.asciidoc +++ b/docs/src/dev/provider/gremlin-semantics.asciidoc @@ -801,6 +801,31 @@ Incoming date remains unchanged. See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsDateStep.java[source], link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asDate-step[reference] +[[asBool-step]] +=== asBool() + +*Description:* Parse the value of the incoming traverser as boolean. + +*Syntax:* `asBool()` + +[width="100%",options="header"] +|========================================================= +|Start Step |Mid Step |Modulated |Domain |Range +|N |Y |N |`any` |`any` +|========================================================= + +*Arguments:* + +None + +`null` values return `false`, numbers evaluate to `true` if non-zero and strings only accept "true" or "false". + +*Exceptions* +* If the incoming traverser type is unsupported or a string other than "true" or "false" then an `IllegalArgumentException` is thrown. + +See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsBoolStep.java[source], +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asBool-step[reference] + [[barrier-step]] === barrier() diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index eaf36d2b96..f27adaea14 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -829,6 +829,26 @@ g.inject(datetime("2023-08-24T00:00:00Z")).asDate() <3> link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asDate()++[`asDate()`] +[[asBool-step]] +=== AsBool Step + +The `asBool()`-step (*map*) converts the incoming traverser to a boolean value. Numbers evaluate to +`true` when non-zero (and not `NaN`), `null` evaluates to `false` and strings are only accepted when +equal to `"true"` or `"false"` (case-insensitive). All other types will throw an `IllegalArgumentException`. + +[gremlin-groovy,modern] +---- +g.inject(1).asBool() <1> +g.inject("false").asBool() <2> +---- + +<1> Convert number to boolean +<2> Convert string to boolean + +*Additional References* + +link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asBool()++[`asBool()`] + [[barrier-step]] === Barrier Step diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java index 37752152ff..09d1b97a4f 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java @@ -1050,11 +1050,15 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im /** * {@inheritDoc} */ - @Override public T visitTraversalMethod_substring_Scope_int_int(final GremlinParser.TraversalMethod_substring_Scope_int_intContext ctx) { notImplemented(ctx); return null; } - /** - * {@inheritDoc} - */ - @Override public T visitTraversalMethod_asDate(final GremlinParser.TraversalMethod_asDateContext ctx) { notImplemented(ctx); return null; } + @Override public T visitTraversalMethod_substring_Scope_int_int(final GremlinParser.TraversalMethod_substring_Scope_int_intContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_asBool(final GremlinParser.TraversalMethod_asBoolContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_asDate(final GremlinParser.TraversalMethod_asDateContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java index 872e0eb6e8..6b1578fb1e 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java @@ -2068,6 +2068,14 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> antlr.genericVisitor.parseIntegral(ctx.integerLiteral(1)).intValue()); } + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_asBool(final GremlinParser.TraversalMethod_asBoolContext ctx) { + return graphTraversal.asBool(); + } + /** * {@inheritDoc} */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java index 041888cb22..4e70acb716 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java @@ -83,6 +83,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsDateStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsBoolStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsStringGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsStringLocalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.CallStep; @@ -1852,6 +1853,17 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { return this.asAdmin().addStep(new FormatStep<>(this.asAdmin(), format)); } + /** + * Parse the incoming traverser as a boolean value. + * + * @return the traversal with an appended {@link AsBoolStep}. + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#asBool-step" target="_blank">Reference Documentation - asBool Step</a> + */ + public default GraphTraversal<S, Boolean> asBool() { + this.asAdmin().getBytecode().addStep(Symbols.asBool); + return this.asAdmin().addStep(new AsBoolStep<>(this.asAdmin())); + } + /** * Parse value of the incoming traverser as an ISO-8601 {@link OffsetDateTime}. * @@ -4131,6 +4143,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { public static final String substring = "substring"; public static final String split = "split"; public static final String format = "format"; + public static final String asBool = "asBool"; public static final String asDate = "asDate"; public static final String dateAdd = "dateAdd"; public static final String dateDiff = "dateDiff"; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java index e1edcfc5f4..22bb3b1be0 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java @@ -772,6 +772,13 @@ public class __ { return __.<A>start().format(format); } + /** + * @see GraphTraversal#asBool() + */ + public static <A> GraphTraversal<A, Boolean> asBool() { + return __.<A>start().asBool(); + } + /** * @see GraphTraversal#asDate() */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsBoolStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsBoolStep.java new file mode 100644 index 0000000000..299fc6c993 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsBoolStep.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.process.traversal.step.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; + +import java.util.Collections; +import java.util.Set; + +/** + * Reference implementation for boolean parsing step. + */ +public final class AsBoolStep<S> extends ScalarMapStep<S, Boolean> { + + public AsBoolStep(final Traversal.Admin traversal) { + super(traversal); + } + + @Override + protected Boolean map(final Traverser.Admin<S> traverser) { + final Object object = traverser.get(); + if (object == null) return false; + if (object instanceof Boolean) return (Boolean) object; + if (object instanceof Number) { + final double d = ((Number) object).doubleValue(); + return !Double.isNaN(d) && d != 0d; + } + if (object instanceof String) { + final String str = (String) object; + if (str.equalsIgnoreCase("true")) return true; + if (str.equalsIgnoreCase("false")) return false; + throw new IllegalArgumentException("Can't parse " + object + " as Boolean."); + } + throw new IllegalArgumentException("Can't parse " + object.getClass().getName() + " as Boolean."); + } + + @Override + public Set<TraverserRequirement> getRequirements() { + return Collections.singleton(TraverserRequirement.OBJECT); + } + +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsBoolStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsBoolStepTest.java new file mode 100644 index 0000000000..a09dfdc199 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AsBoolStepTest.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.process.traversal.step.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class AsBoolStepTest extends StepTest { + + @Override + protected List<Traversal> getTraversals() { + return Collections.singletonList(__.asBool()); + } + + @Test + public void shouldParseBoolean() { + assertEquals(true, __.__(1).asBool().next()); + assertEquals(false, __.__(0).asBool().next()); + assertEquals(true, __.__(3.14).asBool().next()); + assertEquals(false, __.__(Double.NaN).asBool().next()); + assertEquals(false, __.__(null).asBool().next()); + assertEquals(true, __.__("TRUE").asBool().next()); + assertEquals(false, __.__("false").asBool().next()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowOnInvalidString() { + __.__("hello").asBool().next(); + } +} diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs index ef645da751..d9091d1afb 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs @@ -251,6 +251,15 @@ namespace Gremlin.Net.Process.Traversal return Wrap<TStart, DateTimeOffset>(this); } + /// <summary> + /// Adds the asBool step to this <see cref="GraphTraversal{SType, EType}" />. + /// </summary> + public GraphTraversal<TStart, bool> AsBool() + { + Bytecode.AddStep("asBool"); + return Wrap<TStart, bool>(this); + } + /// <summary> /// Adds the barrier step to this <see cref="GraphTraversal{SType, EType}" />. /// </summary> diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs index 1e1995caad..7e077f7fc5 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/__.cs @@ -176,6 +176,14 @@ namespace Gremlin.Net.Process.Traversal return new GraphTraversal<object, DateTimeOffset>().AsDate(); } + /// <summary> + /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the asBool step to that traversal. + /// </summary> + public static GraphTraversal<object, bool> AsBool() + { + return new GraphTraversal<object, bool>().AsBool(); + } + /// <summary> /// Spawns a <see cref="GraphTraversal{SType, EType}" /> and adds the barrier step to that traversal. /// </summary> diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 5908c0d966..557b6d48bc 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -618,11 +618,15 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_injectXstr_offsetX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("2023-08-02T00:00:00-07:00").AsDate()}}, {"g_injectX1694017707000X_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1694017707000).AsDate()}}, {"g_injectX1694017708000LX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"]).AsDate()}}, - {"g_injectX1694017709000dX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"]).AsDate()}}, - {"g_injectX1_2X_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"]).AsDate()}}, - {"g_injectXnullX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null).AsDate()}}, - {"g_injectXinvalidstrX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("This String is not an ISO 8601 Date").AsDate()}}, - {"g_injectX1_2X_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"], p["xx2"]).AsString()}}, + {"g_injectX1694017709000dX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"]).AsDate()}}, + {"g_injectX1_2X_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"]).AsDate()}}, + {"g_injectXnullX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null).AsDate()}}, + {"g_injectXinvalidstrX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("This String is not an ISO 8601 Date").AsDate()}}, + {"g_injectX1X_asBool", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1).AsBool()}}, + {"g_injectX0X_asBool", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(0).AsBool()}}, + {"g_injectXstr_trueX_asBool", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("true").AsBool()}}, + {"g_injectXinvalidstrX_asBool", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("hello").AsBool()}}, + {"g_injectX1_2X_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"], p["xx2"]).AsString()}}, {"g_injectX1_2X_asStringXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"], p["xx2"]).AsString<object>(Scope.Local)}}, {"g_injectXlist_1_2X_asStringXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"]).AsString<object>(Scope.Local)}}, {"g_injectX1_nullX_asString", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null, p["xx1"]).AsString()}}, diff --git a/gremlin-go/driver/anonymousTraversal.go b/gremlin-go/driver/anonymousTraversal.go index a1b3a3f0b1..67b107ab07 100644 --- a/gremlin-go/driver/anonymousTraversal.go +++ b/gremlin-go/driver/anonymousTraversal.go @@ -383,7 +383,12 @@ func (anonymousTraversal *anonymousTraversal) As(args ...interface{}) *GraphTrav // AsDate adds the AsDate step to the GraphTraversal. func (anonymousTraversal *anonymousTraversal) AsDate(args ...interface{}) *GraphTraversal { - return anonymousTraversal.graphTraversal().AsDate(args...) + return anonymousTraversal.graphTraversal().AsDate(args...) +} + +// AsBool adds the AsBool step to the GraphTraversal. +func (anonymousTraversal *anonymousTraversal) AsBool(args ...interface{}) *GraphTraversal { + return anonymousTraversal.graphTraversal().AsBool(args...) } // AsString adds the asString step to the GraphTraversal. diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index 1574cb18df..48f8833d28 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -591,8 +591,12 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_injectX1694017709000dX_asDate": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).AsDate()}}, "g_injectX1_2X_asDate": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).AsDate()}}, "g_injectXnullX_asDate": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil).AsDate()}}, - "g_injectXinvalidstrX_asDate": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("This String is not an ISO 8601 Date").AsDate()}}, - "g_injectX1_2X_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"], p["xx2"]).AsString()}}, + "g_injectXinvalidstrX_asDate": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("This String is not an ISO 8601 Date").AsDate()}}, + "g_injectX1X_asBool": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(1).AsBool()}}, + "g_injectX0X_asBool": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(0).AsBool()}}, + "g_injectXstr_trueX_asBool": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("true").AsBool()}}, + "g_injectXinvalidstrX_asBool": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("hello").AsBool()}}, + "g_injectX1_2X_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"], p["xx2"]).AsString()}}, "g_injectX1_2X_asStringXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"], p["xx2"]).AsString(gremlingo.Scope.Local)}}, "g_injectXlist_1_2X_asStringXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).AsString(gremlingo.Scope.Local)}}, "g_injectX1_nullX_asString": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil, p["xx1"]).AsString()}}, diff --git a/gremlin-go/driver/graphTraversal.go b/gremlin-go/driver/graphTraversal.go index c3f7fca266..c9096d5d59 100644 --- a/gremlin-go/driver/graphTraversal.go +++ b/gremlin-go/driver/graphTraversal.go @@ -108,8 +108,14 @@ func (g *GraphTraversal) As(args ...interface{}) *GraphTraversal { // AsDate adds the asDate step to the GraphTraversal. func (g *GraphTraversal) AsDate(args ...interface{}) *GraphTraversal { - g.Bytecode.AddStep("asDate", args...) - return g + g.Bytecode.AddStep("asDate", args...) + return g +} + +// AsBool adds the asBool step to the GraphTraversal. +func (g *GraphTraversal) AsBool(args ...interface{}) *GraphTraversal { + g.Bytecode.AddStep("asBool", args...) + return g } // AsString adds the asString step to the GraphTraversal. diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js index b2a831b038..e86dd4af47 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js @@ -433,6 +433,16 @@ class GraphTraversal extends Traversal { return this; } + /** + * Graph traversal asBool method. + * @param {...Object} args + * @returns {GraphTraversal} + */ + asBool(...args) { + this.bytecode.addStep('asBool', args); + return this; + } + /** * Graph traversal asDate method. * @param {...Object} args @@ -1740,6 +1750,7 @@ const statics = { and: (...args) => callOnEmptyTraversal('and', args), any: (...args) => callOnEmptyTraversal('any', args), as: (...args) => callOnEmptyTraversal('as', args), + asBool: (...args) => callOnEmptyTraversal('asBool', args), asDate: (...args) => callOnEmptyTraversal('asDate', args), asString: (...args) => callOnEmptyTraversal('asString', args), barrier: (...args) => callOnEmptyTraversal('barrier', args), diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js index aab974e253..490e9da972 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js @@ -621,8 +621,12 @@ const gremlins = { g_injectX1694017709000dX_asDate: [function({g, xx1}) { return g.inject(xx1).asDate() }], g_injectX1_2X_asDate: [function({g, xx1}) { return g.inject(xx1).asDate() }], g_injectXnullX_asDate: [function({g}) { return g.inject(null).asDate() }], - g_injectXinvalidstrX_asDate: [function({g}) { return g.inject("This String is not an ISO 8601 Date").asDate() }], - g_injectX1_2X_asString: [function({g, xx1, xx2}) { return g.inject(xx1, xx2).asString() }], + g_injectXinvalidstrX_asDate: [function({g}) { return g.inject("This String is not an ISO 8601 Date").asDate() }], + g_injectX1X_asBool: [function({g}) { return g.inject(1).asBool() }], + g_injectX0X_asBool: [function({g}) { return g.inject(0).asBool() }], + g_injectXstr_trueX_asBool: [function({g}) { return g.inject("true").asBool() }], + g_injectXinvalidstrX_asBool: [function({g}) { return g.inject("hello").asBool() }], + g_injectX1_2X_asString: [function({g, xx1, xx2}) { return g.inject(xx1, xx2).asString() }], g_injectX1_2X_asStringXlocalX: [function({g, xx1, xx2}) { return g.inject(xx1, xx2).asString(Scope.local) }], g_injectXlist_1_2X_asStringXlocalX: [function({g, xx1}) { return g.inject(xx1).asString(Scope.local) }], g_injectX1_nullX_asString: [function({g, xx1}) { return g.inject(null, xx1).asString() }], diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4 index a2ae6fea14..6f4ee05b57 100644 --- a/gremlin-language/src/main/antlr4/Gremlin.g4 +++ b/gremlin-language/src/main/antlr4/Gremlin.g4 @@ -304,6 +304,7 @@ traversalMethod | traversalMethod_replace | traversalMethod_split | traversalMethod_substring + | traversalMethod_asBool | traversalMethod_asDate | traversalMethod_dateAdd | traversalMethod_dateDiff @@ -358,6 +359,10 @@ traversalMethod_asString | K_ASSTRING LPAREN traversalScope RPAREN #traversalMethod_asString_Scope ; +traversalMethod_asBool + : K_ASBOOL LPAREN RPAREN + ; + traversalMethod_barrier : K_BARRIER LPAREN traversalSackMethod RPAREN #traversalMethod_barrier_Consumer | K_BARRIER LPAREN RPAREN #traversalMethod_barrier_Empty @@ -1928,6 +1933,7 @@ K_ANY: 'any'; K_AS: 'as'; K_ASC: 'asc'; K_ASDATE: 'asDate'; +K_ASBOOL: 'asBool'; K_ASSTRING: 'asString'; K_ASSIGN: 'assign'; K_BARRIER: 'barrier'; diff --git a/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py index f1f55f089c..5ded8b3f53 100644 --- a/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py +++ b/gremlin-python/src/main/python/gremlin_python/process/graph_traversal.py @@ -318,6 +318,10 @@ class GraphTraversal(Traversal): self.bytecode.add_step("asDate", *args) return self + def as_bool(self, *args): + self.gremlin_lang.add_step("asBool", *args) + return self + def as_string(self, *args): self.bytecode.add_step("asString", *args) return self @@ -1099,6 +1103,10 @@ class __(object, metaclass=MagicType): def as_date(cls, *args): return cls.graph_traversal(None, None, Bytecode()).as_date(*args) + @classmethod + def as_bool(cls, *args): + return cls.graph_traversal(None, None, GremlinLang()).as_bool(*args) + @classmethod def as_string(cls, *args): return cls.graph_traversal(None, None, Bytecode()).as_string(*args) @@ -1870,6 +1878,8 @@ def as_(*args): def as_date(*args): return __.as_date(*args) +def as_bool(*args): + return __.as_bool(*args) def as_string(*args): return __.as_string(*args) @@ -2415,6 +2425,8 @@ statics.add_static('as_', as_) statics.add_static('as_date', as_date) +statics.add_static('as_bool', as_bool) + statics.add_static('as_string', as_string) statics.add_static('barrier', barrier) diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 27a45da568..c61a66f7b0 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -594,8 +594,12 @@ world.gremlins = { 'g_injectX1694017709000dX_asDate': [(lambda g, xx1=None:g.inject(xx1).as_date())], 'g_injectX1_2X_asDate': [(lambda g, xx1=None:g.inject(xx1).as_date())], 'g_injectXnullX_asDate': [(lambda g:g.inject(None).as_date())], - 'g_injectXinvalidstrX_asDate': [(lambda g:g.inject('This String is not an ISO 8601 Date').as_date())], - 'g_injectX1_2X_asString': [(lambda g, xx1=None,xx2=None:g.inject(xx1, xx2).as_string())], + 'g_injectXinvalidstrX_asDate': [(lambda g:g.inject('This String is not an ISO 8601 Date').as_date())], + 'g_injectX1X_asBool': [(lambda g:g.inject(1).as_bool())], + 'g_injectX0X_asBool': [(lambda g:g.inject(0).as_bool())], + 'g_injectXstr_trueX_asBool': [(lambda g:g.inject('true').as_bool())], + 'g_injectXinvalidstrX_asBool': [(lambda g:g.inject('hello').as_bool())], + 'g_injectX1_2X_asString': [(lambda g, xx1=None,xx2=None:g.inject(xx1, xx2).as_string())], 'g_injectX1_2X_asStringXlocalX': [(lambda g, xx1=None,xx2=None:g.inject(xx1, xx2).as_string(Scope.local))], 'g_injectXlist_1_2X_asStringXlocalX': [(lambda g, xx1=None:g.inject(xx1).as_string(Scope.local))], 'g_injectX1_nullX_asString': [(lambda g, xx1=None:g.inject(None, xx1).as_string())], diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsBool.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsBool.feature new file mode 100644 index 0000000000..93f9381464 --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/AsBool.feature @@ -0,0 +1,65 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +@StepClassMap @StepAsBool +Feature: Step - asBool() + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectX1X_asBool + Given the empty graph + And the traversal of + """ + g.inject(1).asBool() + """ + When iterated to list + Then the result should be unordered + | result | + | true | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectX0X_asBool + Given the empty graph + And the traversal of + """ + g.inject(0).asBool() + """ + When iterated to list + Then the result should be unordered + | result | + | false | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXstr_trueX_asBool + Given the empty graph + And the traversal of + """ + g.inject('true').asBool() + """ + When iterated to list + Then the result should be unordered + | result | + | true | + + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXinvalidstrX_asBool + Given the empty graph + And the traversal of + """ + g.inject('hello').asBool() + """ + When iterated to list + Then the traversal will raise an error with message containing text of "Can't parse"
