Skip to content

Commit

Permalink
TINKERPOP-2978 Implement all() and any() steps.
Browse files Browse the repository at this point in the history
  • Loading branch information
kenhuuu committed Sep 24, 2023
1 parent e444e77 commit a7bf934
Show file tree
Hide file tree
Showing 30 changed files with 1,082 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This release also includes changes from <<release-3-6-5, 3.6.6>> and <<release-3
* Added the `trim()`, `lTrim()`, `rTrim()`, and `reverse()` steps to perform `String` manipulations.
* Added `replace()`, `split()` and `substring()` steps to perform `String` manipulations.
* Checked graph features for meta-property support before trying to serialize them in `VertexPropertySerializer` for GraphBinary.
* Added list filtering functions `all` and `any`.
[[release-3-7.0]]
=== TinkerPop 3.7.0 (Release Date: July 31, 2023)
Expand Down
42 changes: 16 additions & 26 deletions docs/src/dev/future/proposal-3-remove-closures.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -1087,64 +1087,54 @@ g.inject([1,2]).product([3,4])
=== `any()` [[any_list]]
Returns true if any items in the array `value` exist in the input
Allows the traverser to continue if any items in the array pass the
supplied predicate.
==== Signature(s)
`any(value)`
`any(Traversal)`
`any(Predicate)`
==== Parameters
* value - An array of the items to check in the incoming list
* Predicate - The predicate to use to test the values in the array
==== Allowed incoming traverser types
Array data types. If non-array data types are passed in then an
`IllegalArgumentException` will be thrown
Array data types. All other types will be filtered out.
==== Expected Output
True if any values from one list are in the other, False otherwise
The arrays which have any item pass the predicate.
....
g.inject([1,2]).any([1])
==>true
g.inject([1,2]).any([3])
==>false
g.inject([1,2]).any(P.eq(1))
==>[1,2]
....
=== `all()` [[all_list]]
Returns true if all items in the array `value` exist in the input
Allows the traverser to continue if all items in the array pass the
supplied predicate.
==== Signature(s)
`all(value)`
`all(Traversal)`
`all(Predicate)`
==== Parameters
* value - An array of the items to check in the incoming list
* Predicate - The predicate to use to test the values in the array
==== Allowed incoming traverser types
Array data types. If non-array data types are passed in then an
`IllegalArgumentException` will be thrown
Array data types. All other types will be filtered out.
==== Expected Output
True if all values from one list are in the other, False otherwise
The arrays which have all items pass the predicate.
....
g.inject([1,2]).all([1])
==>true
g.inject([1,2]).all([1, 3])
==>false
g.inject([1,2]).all([3])
==>false
g.inject([1,2]).all(P.gt(0))
==>[1,2]
....
=== `none()` [[none_list]]
Expand Down
52 changes: 52 additions & 0 deletions docs/src/dev/provider/gremlin-semantics.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,58 @@ fully demonstrative of Gremlin step semantics. It is also hard to simply read th
step is meant to behave. This section discusses the semantics for individual steps to help users and providers
understand implementation expectations.
[[all-step]]
=== all()
*Description:* Filters array data from the Traversal Stream if all of the array's items match the supplied predicate.
*Syntax:* `all(Predicate)`
[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`List`/`array`/`Iterable`/`Iterator` |`List`/`array`/`Iterable`/`Iterator`
|=========================================================
*Arguments:*
* `Predicate` - The predicate to use to test each value in the array data.
*Modulation:*
None
*Considerations:*
Each value will be tested using the supplied predicate with any ERROR state being reduced to FALSE. Empty lists always
pass through and null/non-list traversers will be filtered out of the Traversal Stream.
[[any-step]]
=== any()
*Description:* Filters array data from the Traversal Stream if any of the array's items match the supplied predicate.
*Syntax:* `any(Predicate)`
[width="100%",options="header"]
|=========================================================
|Start Step |Mid Step |Modulated |Domain |Range
|N |Y |N |`List`/`array`/`Iterable`/`Iterator` |`List`/`array`/`Iterable`/`Iterator`
|=========================================================
*Arguments:*
* `Predicate` - The predicate to use to test each value in the array data.
*Modulation:*
None
*Considerations:*
Each value will be tested using the supplied predicate with any ERROR state being reduced to FALSE. Empty lists, null
traversers, and non-list traversers will be filtered out of the Traversal Stream.
[[asString-step]]
=== asString()
Expand Down
2 changes: 1 addition & 1 deletion docs/src/reference/gremlin-variants.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -2468,7 +2468,7 @@ that makes traversals a bit more concise.
In situations where Python reserved words and global functions overlap with standard Gremlin steps and tokens, those
bits of conflicting Gremlin get an underscore appended as a suffix:
*Steps* - <<and-step,and_()>>, <<as-step,as_()>>, <<filter-step,filter_()>>, <<from-step,from_()>>,
*Steps* - <<all-step,all_()>>, <<and-step,and_()>>, <<any-step,any_()>>, <<as-step,as_()>>, <<filter-step,filter_()>>, <<from-step,from_()>>,
<<has-step,has_key_>>, <<id-step,id_()>>, <<is-step,is_()>>, <<in-step,in_()>>, <<max-step,max_()>>,
<<min-step,min_()>>, <<not-step,not_()>>, <<or-step,or_()>>, <<range-step,range_()>>, <<sum-step,sum_()>>,
<<with-step,with_()>>
Expand Down
48 changes: 48 additions & 0 deletions docs/src/reference/the-traversal.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,30 @@ g.E().aggregate(local,'x').by('weight').cap('x')
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#aggregate-java.lang.String-++[`aggregate(String)`],
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#aggregate-org.apache.tinkerpop.gremlin.process.traversal.Scope,java.lang.String-++[`aggregate(Scope,String)`]
[[all-step]]
=== All Step
It is possible to filter list traversers using `all()`-step (*filter*). Every item in the list will be tested against
the supplied predicate and if all of the items pass then the traverser is passed along the stream, otherwise it is
filtered. Empty lists are passed along but null or non-iterable traversers are filtered out.
[NOTE, caption=Python]
====
The term `all` is a reserved word in Python, and therefore must be referred to in Gremlin with `all_()`.
====
[gremlin-groovy,modern]
----
g.V().values('age').fold().all(gt(25)) <1>
----
<1> Return the list of ages only if everyone's age is greater than 25.
*Additional References*
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#all-org.apache.tinkerpop.gremlin.process.traversal.P-++[`all(P)`],
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/P.html++[`P`]
[[and-step]]
=== And Step
Expand Down Expand Up @@ -689,6 +713,30 @@ g.V().where(outE('created').and().outE('knows')).values('name')
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#and-org.apache.tinkerpop.gremlin.process.traversal.Traversal...-++[`and(Traversal...)`]
[[any-step]]
=== Any Step
It is possible to filter list traversers using `any()`-step (*filter*). All items in the list will be tested against
the supplied predicate and if any of the items pass then the traverser is passed along the stream, otherwise it is
filtered. Empty lists, null traversers, and non-iterable traversers are filtered out as well.
[NOTE, caption=Python]
====
The term `any` is a reserved word in Python, and therefore must be referred to in Gremlin with `any_()`.
====
[gremlin-groovy,modern]
----
g.V().values('age').fold().any(gt(25)) <1>
----
<1> Return the list of ages if anyone's age is greater than 25.
*Additional References*
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#any-org.apache.tinkerpop.gremlin.process.traversal.P-++[`any(P)`],
link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/P.html++[`P`]
[[as-step]]
=== As Step
Expand Down
17 changes: 17 additions & 0 deletions docs/src/upgrade/release-3.7.x.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,23 @@ See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#split-step[split()-
See: link:https://tinkerpop.apache.org/docs/x.y.z/reference/#substring-step[substring()-step]
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2672[TINKERPOP-2672]
==== List Traverser based Steps
Additional List manipulation/filter steps have been added to replace the use of closures: `any()` and `all()`.
The following example demonstrates usage of the newly introduced steps:
[source,text]
----
gremlin> g.V().values("age").fold().all(P.gt(10))
==>[29,27,32,35]
gremlin> g.V().values("age").fold().any(P.eq(32))
==>[29,27,32,35]
----
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]
=== Upgrading for Providers
==== Graph System Providers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,18 @@ protected void notImplemented(final ParseTree ctx) {
* {@inheritDoc}
*/
@Override public T visitTraversalMethod_aggregate_Scope_String(final GremlinParser.TraversalMethod_aggregate_Scope_StringContext ctx) { notImplemented(ctx); return null; }
/**
* {@inheritDoc}
*/
@Override public T visitTraversalMethod_all_P(final GremlinParser.TraversalMethod_all_PContext ctx) { notImplemented(ctx); return null; }
/**
* {@inheritDoc}
*/
@Override public T visitTraversalMethod_and(final GremlinParser.TraversalMethod_andContext ctx) { notImplemented(ctx); return null; }
/**
* {@inheritDoc}
*/
@Override public T visitTraversalMethod_any_P(final GremlinParser.TraversalMethod_any_PContext ctx) { notImplemented(ctx); return null; }
/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ public GraphTraversal visitTraversalMethod_aggregate_Scope_String(final GremlinP
antlr.argumentVisitor.parseString(ctx.stringArgument()));
}

/**
* {@inheritDoc}
*/
@Override
public GraphTraversal visitTraversalMethod_all_P(final GremlinParser.TraversalMethod_all_PContext ctx) {
return graphTraversal.all(antlr.traversalPredicateVisitor.visitTraversalPredicate(ctx.traversalPredicate()));
}

/**
* {@inheritDoc}
*/
Expand All @@ -186,6 +194,14 @@ public GraphTraversal visitTraversalMethod_and(final GremlinParser.TraversalMeth
antlr.tListVisitor.visitNestedTraversalList(ctx.nestedTraversalList()));
}

/**
* {@inheritDoc}
*/
@Override
public GraphTraversal visitTraversalMethod_any_P(final GremlinParser.TraversalMethod_any_PContext ctx) {
return graphTraversal.any(antlr.traversalPredicateVisitor.visitTraversalPredicate(ctx.traversalPredicate()));
}

/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.OptionalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.RepeatStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.AllStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.AndStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.AnyStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.CoinStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.ConnectiveStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep;
Expand Down Expand Up @@ -2449,6 +2451,32 @@ public default GraphTraversal<S, E> drop() {
return this.asAdmin().addStep(new DropStep<>(this.asAdmin()));
}

/**
* Filters <code>E</code> lists given the provided {@code predicate}.
*
* @param predicate the filter to apply
* @return the traversal with an appended {@link AllStep}
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#all-step" target="_blank">Reference Documentation - All Step</a>
* @since 3.7.1
*/
public default <S2> GraphTraversal<S, E> all(final P<S2> predicate) {
this.asAdmin().getBytecode().addStep(Symbols.all, predicate);
return this.asAdmin().addStep(new AllStep<>(this.asAdmin(), predicate));
}

/**
* Filters <code>E</code> lists given the provided {@code predicate}.
*
* @param predicate the filter to apply
* @return the traversal with an appended {@link AnyStep}
* @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#any-step" target="_blank">Reference Documentation - Any Step</a>
* @since 3.7.1
*/
public default <S2> GraphTraversal<S, E> any(final P<S2> predicate) {
this.asAdmin().getBytecode().addStep(Symbols.any, predicate);
return this.asAdmin().addStep(new AnyStep<>(this.asAdmin(), predicate));
}

///////////////////// SIDE-EFFECT STEPS /////////////////////

/**
Expand Down Expand Up @@ -3691,6 +3719,8 @@ private Symbols() {
public static final String replace = "replace";
public static final String substring = "substring";
public static final String split = "split";
public static final String all = "all";
public static final String any = "any";

public static final String timeLimit = "timeLimit";
public static final String simplePath = "simplePath";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,16 @@ public static <A> GraphTraversal<A, A> drop() {
return __.<A>start().drop();
}

/**
* @see GraphTraversal#all(P)
*/
public static <A> GraphTraversal<A, A> all(final P<A> predicate) { return __.<A>start().all(predicate); }

/**
* @see GraphTraversal#any(P)
*/
public static <A> GraphTraversal<A, A> any(final P<A> predicate) { return __.<A>start().any(predicate); }

///////////////////// SIDE-EFFECT STEPS /////////////////////

/**
Expand Down
Loading

0 comments on commit a7bf934

Please sign in to comment.