This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch steps-taking-traversal-poc in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 1473451213fb99e7090a168d5c6a9914d6eab051 Author: Yang Xia <[email protected]> AuthorDate: Thu Jun 11 11:59:54 2026 -0700 docs update --- CHANGELOG.asciidoc | 9 ++ docs/src/reference/the-traversal.asciidoc | 101 ++++++++++++++ docs/src/upgrade/release-4.x.x.asciidoc | 42 ++++++ .../tinkerpop/gremlin/process/traversal/P.java | 146 ++++++++++----------- .../traversal/util/ChildTraversalContext.java | 42 ------ 5 files changed, 221 insertions(+), 119 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 3d3284d480..66dc0980b1 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -28,6 +28,15 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima * Added typed numeric wrappers and `preciseNumbers` connection option to `gremlin-javascript` for explicit control over numeric type serialization and deserialization. * Added `NextN(n)` to `Traversal` in `gremlin-go` for batched result iteration, providing API parity with `next(n)` in the Java, Python, and .NET GLVs, and updated the Go translators in `gremlin-core` and `gremlin-javascript` to emit `NextN(n)` for the batched form. * Added Gremlator, a single page web application, that translates Gremlin into various programming languages like Javascript and Python. +* Added child traversal support to `has()`, `hasLabel()`, `V()`, `E()`, `property()`, `is()`, `where(P)`, `P.eq/neq/gt/lt/gte/lte/within/without()`, and `TextP` predicates. Child traversals are resolved per-traverser at runtime, enabling dynamic filtering and lookup patterns. +* Added multi-traversal `P.within(trav1, trav2, ...)` and `P.without(trav1, trav2, ...)` which combine results from multiple child traversals for collection membership testing. +* Added `V(traversal)` and `E(traversal)` as start steps with synthetic traverser seeding, consistent with `mergeV(traversal)` behavior. +* Added `ChildTraversalVerificationStrategy` that blocks mutating steps (`addV`, `addE`, `drop`, etc.) inside child traversals. All child traversals must be read-only. +* Added `hasLabel(Traversal)` overload with grammar support for dynamic label filtering. +* Added traversal-bearing predicate support to `where(P)` — resolves child traversal and tests against current value when `P.hasTraversal()` is true. +* Added rejection of traversal-bearing predicates in `choose(P)` — use `choose(__.is(P.op(traversal)), ...)` form instead. +* Added runtime Map validation for `property(traversal)` — rejects non-Map results to prevent sentinel key leakage. +* Added mixed traversal/literal detection in `P.within()` and `P.without()` — throws `IllegalArgumentException` with guidance to wrap literals in `__.constant()`. * Removed `uuid` dependency from `gremlin-javascript` in favor of the built-in `globalThis.crypto.randomUUID()`. * Added streaming HTTP response support to `gremlin-driver` for incremental result deserialization over GraphBinary. * Connected HTTP streaming response deserialization to the traversal API in `gremlin-javascript`, enabling `next()` to return the first result without waiting for the full response. diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index ab49ae2614..bb24250651 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -1267,6 +1267,17 @@ IMPORTANT: It is important to think of `choose()` as a branching step and not a intuitively lead to thinking the latter, where no match would mean to remove the traverser from the stream. As shown in the examples, this is not what happens. +NOTE: The `choose(P)` form does not support traversal-bearing predicates directly. To use a dynamic comparison as the +branch condition, wrap it in an `is()` step: `choose(__.is(P.op(traversal)), trueChoice, falseChoice)`. + +[gremlin-groovy,modern] +---- +g.V().values('age').choose(__.is(P.gt(__.V(1).values('age'))), __.constant('older'), __.constant('not older')) <1> +---- + +<1> For each age value, compare dynamically against marko's age using a traversal inside the predicate. If greater, +return "older"; otherwise return "not older". + The `choose()`-step can be used within a `map()` step to apply the branching logic to each element in a collection. [gremlin-groovy,modern] @@ -2088,6 +2099,10 @@ It is possible to filter vertices, edges, and vertex properties based on their p * `has(key,value)`: Remove the traverser if its element does not have the provided key/value property. * `has(label, key, value)`: Remove the traverser if its element does not have the specified label and provided key/value property. * `has(key,predicate)`: Remove the traverser if its element does not have a key value that satisfies the bi-predicate. For more information on predicates, please read <<a-note-on-predicates,A Note on Predicates>>. + * `has(key, traversal)`: Remove the traverser if its element's key value does not equal the first result of the provided traversal. + * `has(T, traversal)`: Remove the traverser if its element's `T`-based value (e.g. `T.id`, `T.label`) does not equal the first result of the provided traversal. + * `has(label, key, traversal)`: Remove the traverser if its element does not have the specified label and its key value does not equal the first result of the provided traversal. + * `hasLabel(traversal)`: Remove the traverser if its element's label does not equal the first result of the provided traversal. * `hasLabel(labels...)`: Remove the traverser if its element does not have any of the labels. * `hasId(ids...)`: Remove the traverser if its element does not have any of the ids. * `hasKey(keys...)`: Remove the `Property` traverser if it does not match one of the provided keys. @@ -2126,6 +2141,24 @@ the key,value pairs for those vertices. <9> Property key is always stored as `String` and therefore an equality check with `null` will produce no result. <10> An example of using `has()` with regular expression predicate. +The traversal-accepting forms of `has()` allow for dynamic property comparisons. Rather than providing a literal value, +a child traversal is supplied and its first result is used as the comparison value. This follows the same first-result +semantics as `by(traversal)`. The child traversal must be read-only — mutating steps are rejected. + +[gremlin-groovy,modern] +---- +g.V().has('age', P.gt(__.V(1).values('age'))) <1> +g.V().has('name', __.V(1).values('name')) <2> +g.V().hasLabel(__.V(1).label()) <3> +---- + +<1> Find all vertices whose age is greater than marko's age using a traversal inside a predicate. +<2> Find all vertices whose name equals marko's name using a traversal as the value argument. +<3> Find all vertices whose label equals the label of vertex 1. + +NOTE: When a `Traversal` is provided directly as the value argument (not inside a `P`), it is internally wrapped in +`P.eq(traversal)`. See <<a-note-on-predicates,A Note on Predicates>> for more details on traversal-bearing predicates. + *Additional References* link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#has(java.lang.String)++[`has(String)`], @@ -2481,6 +2514,18 @@ g.V().where(__.in('created').values('age'). <2> Find projects having two or more contributors. <3> Find projects whose contributors average age is between 30 and 35. +The `is()` step also supports predicates that contain traversal arguments for dynamic threshold comparison. + +[gremlin-groovy,modern] +---- +g.V().values('age').is(P.gt(__.V(1).values('age'))) <1> +---- + +<1> Find all age values that are greater than marko's age using a traversal inside the predicate. + +NOTE: When `is(traversal)` is used directly (without an explicit predicate), it is internally wrapped in +`P.eq(traversal)`. + *Additional References* link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#is(java.lang.Object)++[`is(Object)`], @@ -4003,6 +4048,19 @@ g.addV().property(set, null) <7> The label value can be specified as a property only at the time a vertex is added and if one is not specified in the addV() <8> If you pass a `null` value for the Map this will be treated as a no-op and the input will be returned +The `property()` step also accepts a traversal that produces a `Map` of key-value pairs to set as properties. The +child traversal must be read-only — mutating steps are rejected. If the traversal does not produce a `Map`, the result +is rejected. + +[gremlin-groovy,modern] +---- +g.V(2).property(__.V(2).project('friendCount').by(__.both('knows').count())) <1> +g.V(2).valueMap() +---- + +<1> Set properties on vertex 2 from a Map produced by a child traversal. Here the `project()` step creates a Map with +key "friendCount" and a value computed from the graph. + *Additional References* link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#property(java.lang.Object,java.lang.Object,java.lang.Object...)++[`property(Object, Object, Object...)`], @@ -5201,6 +5259,19 @@ g.V().has('name', within('marko', 'vadas', 'josh')).as('person'). <1> Normally the `V()`-step will iterate over all vertices. However, graph strategies can fold ``HasContainer``'s into a `GraphStep` to allow index lookups. <2> Whether the graph system provider supports mid-traversal `V()` index lookups or not can easily be determined by inspecting the `toString()` output of the iterated traversal. If `has` conditions were folded into the `V()`-step, an index - if one exists - will be used. +The `V()` step also accepts a traversal argument. The child traversal is evaluated and its results are used as the +vertex identifiers. This works both as a start step and mid-traversal. When used as a start step, a synthetic traverser +is provided to the child traversal. + +[gremlin-groovy,modern] +---- +g.V(__.V(1).id()).values('name') <1> +g.inject(1).V(__.identity()).values('name') <2> +---- + +<1> Use a child traversal to dynamically determine the vertex ID. Here the traversal resolves vertex 1's ID. +<2> Mid-traversal usage where the injected value is used via `identity()` as the ID argument to `V()`. + *Additional References* link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#V(java.lang.Object...)++[`V(Object...)`] @@ -5413,6 +5484,18 @@ g.V().as('a').both().both().as('b'). <8> Marko is younger than josh, but josh knows someone equal in age to marko (which is marko). <9> The "age" property is not <<by-step,productive>> for all vertices and therefore those values are filtered. +The `where()` step also supports predicates that contain traversal arguments. When a predicate contains a child +traversal, the traversal is resolved per-traverser and the result is tested directly against the current value — +bypassing scope-label resolution. + +[gremlin-groovy,modern] +---- +g.V().values('age').where(P.gt(__.V(1).values('age'))) <1> +---- + +<1> Find all age values that are greater than marko's age. The child traversal inside `P.gt()` is resolved and the +current traverser's value is compared against the result. + WARNING: The anonymous traversal of `where()` processes the current object "locally". In OLAP, where the atomic unit of computing is the vertex and its local "star graph," it is important that the anonymous traversal does not leave the confines of the vertex's star graph. In other words, it can not traverse to an adjacent vertex's properties or @@ -5538,6 +5621,24 @@ NOTE: The TinkerPop reference implementation uses the Java `Pattern` and `Matche engine. Other implementations may decide to use a different regular expression engine. It's a good idea to check the documentation for the implementation you are using to verify the allowed regular expression syntax. +In addition to literal values, most predicates also accept a `Traversal` argument. When a traversal is provided, it is +evaluated and only its first result is used for comparison — consistent with `by(traversal)` first-result semantics. +The child traversal must be read-only; mutating steps are rejected. + +* For `within()` and `without()`, the first result should be a `Collection`. Use `fold()` in the child traversal to + produce one. +* The multi-traversal form `within(trav1, trav2, ...)` takes the first result from each traversal and combines them + into a collection for membership testing. + +[gremlin-groovy,modern] +---- +g.V().has('age', P.gt(__.V(1).values('age'))) <1> +g.V().has('age', P.within(__.V(1).out('knows').values('age').fold())) <2> +---- + +<1> Find vertices whose age is greater than marko's age using a traversal inside the predicate. +<2> Find vertices whose age is in the set of ages of marko's friends, using `fold()` to produce a collection. + [gremlin-groovy] ---- eq(2) diff --git a/docs/src/upgrade/release-4.x.x.asciidoc b/docs/src/upgrade/release-4.x.x.asciidoc index 2b4606444c..45555d65dc 100644 --- a/docs/src/upgrade/release-4.x.x.asciidoc +++ b/docs/src/upgrade/release-4.x.x.asciidoc @@ -41,6 +41,33 @@ anonymized form. The original gremlator.com was a prototype built by TinkerPop c previous implementation required Java and a running Gremlin Server, whereas the new version runs entirely in the browser with no server infrastructure needed. +==== Traversal-Accepting Steps + +Steps and predicates that previously only accepted literal values now accept child traversals resolved per-traverser +at runtime. This enables dynamic filtering, lookup, and mutation patterns without `where()`/`select()` workarounds. + +Affected steps: `has()`, `hasLabel()`, `V()`, `E()`, `property()`, `is()`, `where(P)`, `P.eq/neq/gt/lt/gte/lte()`, +`P.within/without()`, and all `TextP` predicates. + +[source,groovy] +---- +// Dynamic property comparison +g.V().has("age", P.gt(__.V(1).values("age"))) + +// Dynamic vertex lookup via select() +g.V(1).id().as("x").V().has("name","josh").V(__.select("x")).values("name") + +// Multi-source filtering with within() +g.V().has("name", P.within(__.V(1).out("knows").values("name").fold(), __.constant("peter"))) +---- + +Child traversals take only the first result (consistent with `by(traversal)` semantics). For `within()`/`without()`, +use `fold()` to collect multiple values into a list. + +All child traversals must be read-only. Mutating steps (`addV`, `addE`, `drop`, `property`) inside child traversals +are rejected at construction time with `IllegalArgumentException`. A `ChildTraversalVerificationStrategy` provides +additional safety at strategy time. + ==== Removed `uuid` Dependency in gremlin-javascript The `uuid` npm package has been removed from `gremlin-javascript`. UUID generation now uses the built-in @@ -505,6 +532,21 @@ re-query elements against the original graph, extract their ids and call `g.V(id ==== Graph System Providers +===== Traversal-Accepting Steps — HasContainer Guard + +Steps and predicates now accept child traversals resolved at runtime. `HasContainer` instances that hold a child +traversal cannot be folded into index lookups because their value is dynamic (resolved per-traverser). Providers that +fold `HasContainer` predicates into their graph step must guard against this: + +[source,java] +---- +if (hasContainer.hasTraversal()) continue; // skip — dynamic value, cannot fold into index +---- + +`GraphStep.processHasContainerIds()` already includes this guard. Providers that implement their own `HasContainer` +folding strategy should add the same check. Without it, `getValue()` and `getBiPredicate()` will throw +`IllegalStateException` on traversal-bearing containers. + ==== Graph Driver Providers == TinkerPop 4.0.0-beta.2 diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java index f95001a55b..810a67e372 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java @@ -340,14 +340,9 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { if (this.traversalValue == null) return; final Traversal.Admin<Object, Object> trav = (Traversal.Admin<Object, Object>) (Traversal.Admin) this.traversalValue; - final Traverser.Admin<Object> split = (Traverser.Admin<Object>) traverser.split(); - split.setSideEffects(trav.getSideEffects()); - split.setBulk(1L); - trav.reset(); - trav.addStart(split); + prepareChildTraversal(traverser, trav); try { - // Take first result only — consistent with by(traversal) semantics. if (!trav.hasNext()) { this.resolvedEmpty = true; this.literals = Collections.emptyList(); @@ -356,8 +351,6 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { this.resolvedEmpty = false; final Object firstResult = trav.next(); if (this.biPredicate instanceof Contains) { - // Contains predicates need a Collection value. If first result is already - // a Collection (e.g., from fold()), use it directly. Otherwise wrap in singleton. if (firstResult instanceof Collection) { this.literals = (Collection<V>) firstResult; } else { @@ -383,15 +376,9 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { for (final Traversal.Admin<?, ?> tv : this.traversalValues) { final Traversal.Admin<Object, Object> trav = (Traversal.Admin<Object, Object>) (Traversal.Admin) tv; - final Traverser.Admin<Object> split = (Traverser.Admin<Object>) traverser.split(); - split.setSideEffects(trav.getSideEffects()); - split.setBulk(1L); - trav.reset(); - trav.addStart(split); + prepareChildTraversal(traverser, trav); try { - // Take first result only from each traversal. - // If the result is a Collection (from fold()), unpack it. if (trav.hasNext()) { final Object firstResult = trav.next(); if (firstResult instanceof Collection) { @@ -415,6 +402,18 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { } } + /** + * Prepares a child traversal for evaluation by splitting the current traverser and seeding it. + */ + @SuppressWarnings("unchecked") + private static void prepareChildTraversal(final Traverser.Admin<?> traverser, final Traversal.Admin<Object, Object> trav) { + final Traverser.Admin<Object> split = (Traverser.Admin<Object>) traverser.split(); + split.setSideEffects(trav.getSideEffects()); + split.setBulk(1L); + trav.reset(); + trav.addStart(split); + } + //////////////// predicate traversal utilities /** @@ -625,26 +624,8 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { * @since 3.0.0-incubating */ public static <V> P<V> within(final V... values) { - // If a single Traversal is passed, redirect to the Traversal-accepting overload. - // This handles cases where Java's overload resolution picks the varargs method - // instead of the Traversal-specific method. - if (values != null && values.length == 1 && values[0] instanceof Traversal) { - return P.within((Traversal<?, ?>) values[0]); - } - // If multiple Traversals are passed, redirect to the multi-traversal overload. - if (values != null && values.length > 1 && allTraversals(values)) { - final List<Traversal.Admin<?, ?>> traversals = new ArrayList<>(values.length); - for (final V v : values) { - traversals.add(((Traversal<?, ?>) v).asAdmin()); - } - return new P(Contains.within, traversals); - } - // Reject mixed traversals and literals — would silently produce wrong results. - if (values != null && values.length > 1 && anyTraversals(values)) { - throw new IllegalArgumentException( - "Cannot mix traversals and literal values in within(). " + - "Use within(__.constant(val1), __.constant(val2)) to wrap all values as traversals."); - } + final P<V> traversalP = handleContainsVarargs(values, Contains.within, "within"); + if (traversalP != null) return traversalP; final V[] v = null == values ? (V[]) new Object[] { null } : (V[]) values; return P.within(Arrays.asList(v)); } @@ -678,24 +659,8 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { * @since 3.0.0-incubating */ public static <V> P<V> without(final V... values) { - // If a single Traversal is passed, redirect to the Traversal-accepting overload. - if (values != null && values.length == 1 && values[0] instanceof Traversal) { - return P.without((Traversal<?, ?>) values[0]); - } - // If multiple Traversals are passed, redirect to the multi-traversal overload. - if (values != null && values.length > 1 && allTraversals(values)) { - final List<Traversal.Admin<?, ?>> traversals = new ArrayList<>(values.length); - for (final V v : values) { - traversals.add(((Traversal<?, ?>) v).asAdmin()); - } - return new P(Contains.without, traversals); - } - // Reject mixed traversals and literals - if (values != null && values.length > 1 && anyTraversals(values)) { - throw new IllegalArgumentException( - "Cannot mix traversals and literal values in without(). " + - "Use without(__.constant(val1), __.constant(val2)) to wrap all values as traversals."); - } + final P<V> traversalP = handleContainsVarargs(values, Contains.without, "without"); + if (traversalP != null) return traversalP; final V[] v = null == values ? (V[]) new Object[] { null } : values; return P.without(Arrays.asList(v)); } @@ -799,18 +764,7 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { * @since 4.0.0 */ public static <V> P<V> within(final Traversal<?, ?> first, final Traversal<?, ?> second, final Traversal<?, ?>... more) { - final List<Traversal.Admin<?, ?>> traversals = new ArrayList<>(2 + (more != null ? more.length : 0)); - traversals.add(first.asAdmin()); - traversals.add(second.asAdmin()); - if (more != null) { - for (final Traversal<?, ?> tv : more) { - traversals.add(tv.asAdmin()); - } - } - for (final Traversal.Admin<?, ?> tv : traversals) { - ChildTraversalValidator.validate(tv); - } - return new P(Contains.within, traversals); + return containsTraversals(Contains.within, first, second, more); } /** @@ -830,18 +784,7 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { * @since 4.0.0 */ public static <V> P<V> without(final Traversal<?, ?> first, final Traversal<?, ?> second, final Traversal<?, ?>... more) { - final List<Traversal.Admin<?, ?>> traversals = new ArrayList<>(2 + (more != null ? more.length : 0)); - traversals.add(first.asAdmin()); - traversals.add(second.asAdmin()); - if (more != null) { - for (final Traversal<?, ?> tv : more) { - traversals.add(tv.asAdmin()); - } - } - for (final Traversal.Admin<?, ?> tv : traversals) { - ChildTraversalValidator.validate(tv); - } - return new P(Contains.without, traversals); + return containsTraversals(Contains.without, first, second, more); } /** @@ -889,6 +832,55 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { return predicate.negate(); } + /** + * Handles varargs traversal detection for within/without. Returns a P if traversals were found, + * or null if the values are plain literals. + */ + @SuppressWarnings("unchecked") + private static <V> P<V> handleContainsVarargs(final V[] values, final PBiPredicate predicate, final String stepName) { + if (values != null && values.length == 1 && values[0] instanceof Traversal) { + final Traversal<?, ?> trav = (Traversal<?, ?>) values[0]; + ChildTraversalValidator.validate(trav.asAdmin()); + return new P(predicate, trav.asAdmin()); + } + if (values != null && values.length > 1 && allTraversals(values)) { + final List<Traversal.Admin<?, ?>> traversals = new ArrayList<>(values.length); + for (final V v : values) { + traversals.add(((Traversal<?, ?>) v).asAdmin()); + } + for (final Traversal.Admin<?, ?> tv : traversals) { + ChildTraversalValidator.validate(tv); + } + return new P(predicate, traversals); + } + if (values != null && values.length > 1 && anyTraversals(values)) { + throw new IllegalArgumentException( + "Cannot mix traversals and literal values in " + stepName + "(). " + + "Use " + stepName + "(__.constant(val1), __.constant(val2)) to wrap all values as traversals."); + } + return null; + } + + /** + * Creates a Contains predicate from multiple validated child traversals. + */ + @SuppressWarnings("unchecked") + private static <V> P<V> containsTraversals(final PBiPredicate predicate, final Traversal<?, ?> first, + final Traversal<?, ?> second, final Traversal<?, ?>... more) { + final List<Traversal.Admin<?, ?>> traversals = new ArrayList<>(2 + (more != null ? more.length : 0)); + traversals.add(first.asAdmin()); + traversals.add(second.asAdmin()); + if (more != null) { + for (final Traversal<?, ?> tv : more) { + traversals.add(tv.asAdmin()); + } + } + for (final Traversal.Admin<?, ?> tv : traversals) { + ChildTraversalValidator.validate(tv); + } + return new P(predicate, traversals); + } + /** * Checks if all elements in the array are {@link Traversal} instances (specifically * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal}). diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalContext.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalContext.java deleted file mode 100644 index fbb489813f..0000000000 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ChildTraversalContext.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.util; - -/** - * Classifies the context in which a child traversal is used, determining what validation rules apply. - * - * <ul> - * <li>{@link #FILTER} — child traversals inside {@code has()}, {@code is()}, {@code all()}, {@code any()}, - * {@code none()}, or {@code choose()} predicates. No {@link org.apache.tinkerpop.gremlin.process.traversal.step.Mutating} - * steps are allowed.</li> - * <li>{@link #LOOKUP} — child traversals inside {@code V(traversal)} or {@code E(traversal)}. No - * {@link org.apache.tinkerpop.gremlin.process.traversal.step.Mutating} steps are allowed.</li> - * <li>{@link #MUTATION} — child traversals inside {@code property(traversal)}. Only - * {@link org.apache.tinkerpop.gremlin.process.traversal.step.filter.DropStep} is blocked; other mutating - * steps are permitted because the traversal intentionally produces values from graph state.</li> - * <li>{@link #NONE} — not a child-traversal-bearing step, or a step whose children are not validated - * (e.g., {@code mergeV}, {@code mergeE}).</li> - * </ul> - */ -public enum ChildTraversalContext { - FILTER, - LOOKUP, - MUTATION, - NONE -}
