Skip to content

Commit d6b80bd

Browse files
Implement asBool() step (#3154)
--------- Co-authored-by: Alexey Temnikov <alexey.temnikov@improving.com>
1 parent 7b6f837 commit d6b80bd

24 files changed

Lines changed: 588 additions & 3 deletions

File tree

CHANGELOG.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>.
7070
* 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.
7171
* Added missing strategies in `gremlin-go`, updated certain strategies to take varargs and updated `GoTranslatorVisitor` for corresponding translations.
7272
* Fixed `BigInt` and `BigDecimal` parsing in `gremlin-go` cucumber test suite, fixed `UnscaledValue` type in `BigDecimal` struct and added `ParseBigDecimal` method.
73+
* Added boolean parsing step `asBool()`.
7374
* Added validation to `valueMap()`, `propertyMap()`, `groupCount()`, `sack()`, `dedup()`, `sample()`, and `aggregate()` to prevent the invalid usage of multiple `by()` modulators.
7475
* Deprecated `ProcessLimitedStandardSuite` and `ProcessLimitedComputerSuite` in favor of `ProcessEmbeddedStandardSuite` and `ProcessEmbeddedComputerSuite` respectively.
7576
* Deprecated `ProcessStandardSuite` and the `ProcessComputerSuite` in favor of Gherkin testing and the `ProcessEmbeddedStandardSuite` and `ProcessEmbeddedComputerSuite` for testing JVM-specific Gremlin behaviors.

docs/src/dev/provider/gremlin-semantics.asciidoc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,32 @@ None
776776
See: link:https://github.com/apache/tinkerpop/tree/x.y.z/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java[source],
777777
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#as-step[reference]
778778
779+
[[asBool-step]]
780+
=== asBool()
781+
782+
*Description:* Parse the value of the incoming traverser as boolean.
783+
784+
*Syntax:* `asBool()`
785+
786+
[width="100%",options="header"]
787+
|=========================================================
788+
|Start Step |Mid Step |Modulated |Domain |Range
789+
|N |Y |N |`Number`/`String`/`Boolean` |`Boolean`
790+
|=========================================================
791+
792+
*Arguments:*
793+
794+
None
795+
796+
Booleans are passed as is, numbers evaluate to `true` if non-zero, and `false` if zero or `NaN`. Strings only accept "true" or "false" (case-insensitive).
797+
798+
*Exceptions*
799+
800+
If the incoming traverser type is unsupported, a string other than "true" or "false", or `null`, then an `IllegalArgumentException` is thrown.
801+
802+
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],
803+
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#asBool-step[reference]
804+
779805
[[asDate-step]]
780806
=== asDate()
781807

docs/src/reference/the-traversal.asciidoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,27 @@ g.V().hasLabel('person').values('age').fold().asString(local) <3>
804804
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asString()++[`asString()`]
805805
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asString(org.apache.tinkerpop.gremlin.process.traversal.Scope)++[`asString(Scope)`]
806806
807+
[[asBool-step]]
808+
=== AsBool Step
809+
810+
The `asBool()`-step (*map*) converts the incoming traverser to a boolean value. If the traverser is already a boolean value, it is passed as-is. Numbers evaluate to
811+
`true` if non-zero, and to `false` if zero or `NaN`. Strings are only accepted when
812+
equal to `"true"` or `"false"` (case-insensitive), otherwise an `IllegalArgumentException` is thrown.
813+
All other types (including `null`) will throw an `IllegalArgumentException`.
814+
815+
[gremlin-groovy,modern]
816+
----
817+
g.inject(1).asBool() <1>
818+
g.inject("false").asBool() <2>
819+
----
820+
821+
<1> Convert number to boolean
822+
<2> Convert string to boolean
823+
824+
*Additional References*
825+
826+
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#asBool()++[`asBool()`]
827+
807828
[[asDate-step]]
808829
=== AsDate Step
809830

docs/src/upgrade/release-3.8.x.asciidoc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,34 @@ complete list of all the modifications that are part of this release.
3030
3131
=== Upgrading for Users
3232
33+
==== Boolean Conversion Step
34+
35+
The `asBool()` step bridges another gap in Gremlin's casting functionalities. Users now have the ability to parse strings and
36+
numbers into boolean values, both for normalization and to perform boolean logic with numerical values.
37+
38+
[source,text]
39+
----
40+
gremlin> g.inject(2, "true", 1, 0, false, "FALSE").asBool().fold()
41+
==>[true,true,true,false,false,false]
42+
43+
// using the modern graph, we can turn count() results into boolean values
44+
gremlin> g.V().local(outE().count()).fold()
45+
==>[3,0,0,2,0,1]
46+
gremlin> g.V().local(outE().count()).asBool().fold()
47+
==>[true,false,false,true,false,true]
48+
// a slightly more complex one using sack for boolean operations for vertices with both 'person' label and has out edges
49+
gremlin> g.V().sack(assign).by(__.hasLabel('person').count().asBool()).sack(and).by(__.outE().count().asBool()).sack().path()
50+
==>[v[1],true]
51+
==>[v[2],false]
52+
==>[v[3],false]
53+
==>[v[4],true]
54+
==>[v[5],false]
55+
==>[v[6],true]
56+
----
57+
58+
See: link:https://tinkerpop.apache.org/docs/3.8.0/reference/#asBool-step[asBool()-step]
59+
See: link:https://issues.apache.org/jira/browse/TINKERPOP-3175[TINKERPOP-3175]
60+
3361
==== Set minimum Java version to 11
3462
3563
TinkerPop 3.8 requires a minimum of Java 11 for building and running. Support for Java 1.8 has been dropped.

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,10 @@ protected void notImplemented(final ParseTree ctx) {
10511051
* {@inheritDoc}
10521052
*/
10531053
@Override public T visitTraversalMethod_substring_Scope_int_int(final GremlinParser.TraversalMethod_substring_Scope_int_intContext ctx) { notImplemented(ctx); return null; }
1054+
/**
1055+
* {@inheritDoc}
1056+
*/
1057+
@Override public T visitTraversalMethod_asBool(final GremlinParser.TraversalMethod_asBoolContext ctx) { notImplemented(ctx); return null; }
10541058
/**
10551059
* {@inheritDoc}
10561060
*/

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,14 @@ public GraphTraversal visitTraversalMethod_substring_Scope_int_int(final Gremlin
20682068
antlr.genericVisitor.parseIntegral(ctx.integerLiteral(1)).intValue());
20692069
}
20702070

2071+
/**
2072+
* {@inheritDoc}
2073+
*/
2074+
@Override
2075+
public GraphTraversal visitTraversalMethod_asBool(final GremlinParser.TraversalMethod_asBoolContext ctx) {
2076+
return graphTraversal.asBool();
2077+
}
2078+
20712079
/**
20722080
* {@inheritDoc}
20732081
*/

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
8484
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep;
8585
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsDateStep;
86+
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsBoolStep;
8687
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsStringGlobalStep;
8788
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsStringLocalStep;
8889
import org.apache.tinkerpop.gremlin.process.traversal.step.map.CallStep;
@@ -1852,6 +1853,18 @@ public default GraphTraversal<S, String> format(final String format) {
18521853
return this.asAdmin().addStep(new FormatStep<>(this.asAdmin(), format));
18531854
}
18541855

1856+
/**
1857+
* Parse the incoming traverser as a boolean value.
1858+
*
1859+
* @return the traversal with an appended {@link AsBoolStep}.
1860+
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#asBool-step" target="_blank">Reference Documentation - asBool Step</a>
1861+
* @since 3.8.0
1862+
*/
1863+
public default GraphTraversal<S, Boolean> asBool() {
1864+
this.asAdmin().getBytecode().addStep(Symbols.asBool);
1865+
return this.asAdmin().addStep(new AsBoolStep<>(this.asAdmin()));
1866+
}
1867+
18551868
/**
18561869
* Parse value of the incoming traverser as an ISO-8601 {@link OffsetDateTime}.
18571870
*
@@ -4131,6 +4144,7 @@ private Symbols() {
41314144
public static final String substring = "substring";
41324145
public static final String split = "split";
41334146
public static final String format = "format";
4147+
public static final String asBool = "asBool";
41344148
public static final String asDate = "asDate";
41354149
public static final String dateAdd = "dateAdd";
41364150
public static final String dateDiff = "dateDiff";

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,13 @@ public static <A> GraphTraversal<A, String> format(final String format) {
772772
return __.<A>start().format(format);
773773
}
774774

775+
/**
776+
* @see GraphTraversal#asBool()
777+
*/
778+
public static <A> GraphTraversal<A, Boolean> asBool() {
779+
return __.<A>start().asBool();
780+
}
781+
775782
/**
776783
* @see GraphTraversal#asDate()
777784
*/
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.tinkerpop.gremlin.process.traversal.step.map;
20+
21+
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
22+
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
23+
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
24+
25+
import java.util.Collections;
26+
import java.util.Set;
27+
28+
/**
29+
* Reference implementation for boolean parsing step.
30+
*/
31+
public final class AsBoolStep<S> extends ScalarMapStep<S, Boolean> {
32+
33+
public AsBoolStep(final Traversal.Admin traversal) {
34+
super(traversal);
35+
}
36+
37+
@Override
38+
protected Boolean map(final Traverser.Admin<S> traverser) {
39+
final Object object = traverser.get();
40+
if (object == null) throw new IllegalArgumentException("Can't parse null as Boolean.");
41+
if (object instanceof Boolean) return (Boolean) object;
42+
if (object instanceof Number) {
43+
final double d = ((Number) object).doubleValue();
44+
if (Double.isNaN(d)) return false;
45+
return d != 0d;
46+
}
47+
if (object instanceof String) {
48+
final String str = ((String) object).trim();
49+
if (str.equalsIgnoreCase("true")) return true;
50+
if (str.equalsIgnoreCase("false")) return false;
51+
throw new IllegalArgumentException("Can't parse " + object + " as Boolean.");
52+
}
53+
throw new IllegalArgumentException("Can't parse " + object.getClass().getName() + " as Boolean.");
54+
}
55+
56+
@Override
57+
public Set<TraverserRequirement> getRequirements() {
58+
return Collections.singleton(TraverserRequirement.OBJECT);
59+
}
60+
61+
}

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/BytecodeHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.WhereTraversalStep;
6565
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep;
6666
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep;
67+
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsBoolStep;
6768
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsDateStep;
6869
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsStringGlobalStep;
6970
import org.apache.tinkerpop.gremlin.process.traversal.step.map.AsStringLocalStep;
@@ -244,6 +245,7 @@ public final class BytecodeHelper {
244245
put(GraphTraversal.Symbols.replace, Arrays.asList(ReplaceGlobalStep.class, ReplaceLocalStep.class));
245246
put(GraphTraversal.Symbols.substring, Arrays.asList(SubstringGlobalStep.class, ReplaceLocalStep.class));
246247
put(GraphTraversal.Symbols.split, Arrays.asList(SplitGlobalStep.class, SplitLocalStep.class));
248+
put(GraphTraversal.Symbols.asBool, Collections.singletonList(AsBoolStep.class));
247249
put(GraphTraversal.Symbols.asDate, Collections.singletonList(AsDateStep.class));
248250
put(GraphTraversal.Symbols.dateAdd, Collections.singletonList(DateAddStep.class));
249251
put(GraphTraversal.Symbols.dateDiff, Collections.singletonList(DateDiffStep.class));

0 commit comments

Comments
 (0)