This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch steps-taking-traversal in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 83fed48ba027a427866248261d5601ba85363a12 Author: Yang Xia <[email protected]> AuthorDate: Fri Jun 12 00:20:19 2026 -0700 Add feature conformance tests and documentation for traversal-accepting steps Add cross-language Gherkin scenarios covering has/is/where/property/V/E with child-traversal arguments and the child-traversal mutation verification, plus CHANGELOG, upgrade, and reference documentation describing the feature, its first-result/empty-set semantics, and the provider-side HasContainer folding guard. --- CHANGELOG.asciidoc | 9 + docs/src/reference/the-traversal.asciidoc | 103 +++++++ docs/src/upgrade/release-4.x.x.asciidoc | 41 +++ .../filter/ChildTraversalVerification.feature | 212 ++++++++++++++ .../test/features/filter/HasTraversal.feature | 326 +++++++++++++++++++++ .../test/features/filter/IsTraversal.feature | 273 +++++++++++++++++ .../test/features/filter/WhereTraversal.feature | 122 ++++++++ .../gremlin/test/features/map/VETraversal.feature | 149 ++++++++++ .../features/sideEffect/PropertyTraversal.feature | 253 ++++++++++++++++ 9 files changed, 1488 insertions(+) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 4971f79217..f035157d1d 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -34,6 +34,15 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima * Added explicit transaction support to all non-Java GLVs (gremlin-python, gremlin-go, gremlin-javascript, gremlin-dotnet). * Changed default transaction close behavior from commit to rollback across all GLVs to align with embedded graph defaults. * Refactored Go driver connection to block until response headers arrive, enabling synchronous error returns and proper transaction ordering. +* 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 results that are not a `Map` of property key/value pairs. +* 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 bc05a52b44..790873c581 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -1253,6 +1253,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] @@ -2074,6 +2085,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. @@ -2112,6 +2127,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)`], @@ -2467,6 +2500,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)`], @@ -3989,6 +4034,21 @@ 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. Each entry in the resulting Map becomes a separate property on the element, allowing multiple properties +to be set in a single step. + +[gremlin-groovy,modern] +---- +g.V(4).property(__.V(1).project('friendCount','createdSoftware').by(__.out('knows').count()).by(__.out('created').values('name'))) <1> +g.V(4).valueMap('friendCount','createdSoftware') +---- + +<1> Set two properties on vertex 4 (josh) from a Map produced by a child traversal. The `project()` step builds a Map +with keys "friendCount" and "createdSoftware", whose values are computed from vertex 1's (marko's) relationships - +number of friends and name of software created. Both properties are applied to vertex 4 in one operation. + *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...)`], @@ -5187,6 +5247,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...)`] @@ -5399,6 +5472,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 @@ -5524,6 +5609,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 ac8debb0a9..ed92ed73a1 100644 --- a/docs/src/upgrade/release-4.x.x.asciidoc +++ b/docs/src/upgrade/release-4.x.x.asciidoc @@ -69,6 +69,32 @@ GLVs (including the Java driver). This aligns with the embedded graph transactio partial work is discarded if the user forgets to call `commit()`. In Java (both remote and embedded mode), the behavior can still be overridden via `tx.onClose(Transaction.CLOSE_BEHAVIOR.COMMIT)`. The non-Java GLVs do not support configuring close behavior and always rollback. +==== 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: resolve the ids of marko's friends, then look them up by id +g.V(1).V(__.out("knows").id()).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 @@ -565,6 +591,21 @@ benefiting all driver users transparently. See <<provider-defined-types>> for full details on annotation usage, field filtering, nested types, and ServiceLoader registration. +===== 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, the container's value is read before it has been resolved +against a traverser, so an unresolved value would be folded into the index lookup and produce incorrect results. + ==== Graph Driver Providers == TinkerPop 4.0.0-beta.2 diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/ChildTraversalVerification.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/ChildTraversalVerification.feature new file mode 100644 index 0000000000..eacf7a972a --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/ChildTraversalVerification.feature @@ -0,0 +1,212 @@ +# 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. + +@StepClassFilter @StepHas +Feature: Child Traversal Verification - mutating steps blocked in filter/lookup contexts + + # ===== FILTER CONTEXT: has() with mutating child traversals ===== + # All error scenarios use addV()/drop() which ComputerVerificationStrategy also rejects on OLAP + # with a different message, so we exclude them from GraphComputer runs. + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_addVXxX_valuesXnameXX_rejected + Given the modern graph + And the traversal of + """ + g.V().has("name", __.addV("x").values("name")) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_V_drop_constantXxXX_rejected + Given the modern graph + And the traversal of + """ + g.V().has("name", __.V().drop().constant("x")) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_V_mapXaddVXxXX_valuesXnameXX_rejected + Given the modern graph + And the traversal of + """ + g.V().has("name", __.V().map(__.addV("x")).values("name")) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_eqXaddVXxX_valuesXnameXXX_rejected + Given the modern graph + And the traversal of + """ + g.V().has("name", P.eq(__.addV("x").values("name"))) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_withinXaddVXxX_valuesXnameXXX_rejected + Given the modern graph + And the traversal of + """ + g.V().has("name", P.within(__.addV("x").values("name"))) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXage_gtXaddVXxX_valuesXageXXX_rejected + Given the modern graph + And the traversal of + """ + g.V().has("age", P.gt(__.addV("x").values("age"))) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + # ===== FILTER CONTEXT: is() with mutating child traversals ===== + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXaddVXxX_valuesXageXX_rejected + Given the modern graph + And the traversal of + """ + g.V().values("age").is(__.addV("x").values("age")) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXgtXaddVXxX_valuesXageXXX_rejected + Given the modern graph + And the traversal of + """ + g.V().values("age").is(P.gt(__.addV("x").values("age"))) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + # ===== LOOKUP CONTEXT: V()/E() with mutating child traversals ===== + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_VXaddVXxX_idX_rejected + Given the modern graph + And the traversal of + """ + g.V().V(__.addV("x").id()) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidENotSupported + Scenario: g_V_EXaddVXxX_idX_rejected + Given the modern graph + And the traversal of + """ + g.V().E(__.addV("x").id()) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXaddVXxX_idX_rejected + Given the modern graph + And the traversal of + """ + g.V(__.addV("x").id()) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_EXaddVXxX_idX_rejected + Given the modern graph + And the traversal of + """ + g.E(__.addV("x").id()) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + # ===== MUTATION CONTEXT: property(traversal) blocks ALL mutating steps ===== + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected + Given the modern graph + And the traversal of + """ + g.V().property(__.V().map(__.drop()).project("x").by("name")) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_propertyXaddVXtempX_projectXkX_byXnameXX_rejected + Given the modern graph + And the traversal of + """ + g.V().property(__.addV("temp").project("k").by("name")) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "mutating step" + + # ===== VALID TRAVERSALS: should NOT be rejected ===== + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("name", __.V(vid1).values("name")) + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("age", P.gt(__.V(vid1).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[josh] | + | v[peter] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).V(__.out("knows").id()).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | vadas | + | josh | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasTraversal.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasTraversal.feature new file mode 100644 index 0000000000..74e194ba65 --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasTraversal.feature @@ -0,0 +1,326 @@ +# 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. + +@StepClassFilter @StepHas +Feature: Step - has() with traversal arguments + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_VXvid1X_valuesXnameXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("name", __.V(vid1).values("name")) + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | + + @GraphComputerVerificationMidVNotSupported + # has(key, traversal) with multi-result child traversal - takes first result (order-dependent) + @InsertionOrderingRequired + Scenario: g_V_hasXname_VXvid1X_outXknowsX_valuesXnameXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("name", __.V(vid1).out("knows").values("name")) + """ + When iterated to list + Then the result should be unordered + | result | + | v[vadas] | + + # has(key, traversal) with multi-result child traversal (age) - takes first result (order-dependent) + @GraphComputerVerificationMidVNotSupported + @InsertionOrderingRequired + Scenario: g_V_hasXage_VXvid1X_outXknowsX_valuesXageXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("age", __.V(vid1).out("knows").values("age")) + """ + When iterated to list + Then the result should be unordered + | result | + | v[vadas] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_VXvid1X_valuesXnonexistentXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("name", __.V(vid1).values("nonexistent")) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_notXidentityXX + Given the modern graph + And the traversal of + """ + g.V().has("name", __.not(__.identity())) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXage_gtXVXvid1X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("age", P.gt(__.V(vid1).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[josh] | + | v[peter] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXage_lteXVXvid2X_valuesXageXXX + Given the modern graph + And using the parameter vid2 defined as "v[vadas].id" + And the traversal of + """ + g.V().has("age", P.lte(__.V(vid2).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[vadas] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXage_neqXVXvid3X_valuesXageXXX + Given the modern graph + And using the parameter vid3 defined as "v[josh].id" + And the traversal of + """ + g.V().has("age", P.neq(__.V(vid3).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | + | v[vadas] | + | v[peter] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_eqXVXvid1X_valuesXnameXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("name", P.eq(__.V(vid1).values("name"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXage_ltXVXvid1X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("age", P.lt(__.V(vid1).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[vadas] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXage_gteXVXvid3X_valuesXageXXX + Given the modern graph + And using the parameter vid3 defined as "v[josh].id" + And the traversal of + """ + g.V().has("age", P.gte(__.V(vid3).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[josh] | + | v[peter] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXage_eqXVXvid1X_valuesXnonexistentXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("age", P.eq(__.V(vid1).values("nonexistent"))) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXlabel_VXvid1X_labelXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has(T.label, __.V(vid1).label()) + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | + | v[vadas] | + | v[josh] | + | v[peter] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXperson_name_VXvid1X_valuesXnameXX_age + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("person", "name", __.V(vid1).values("name")).values("age") + """ + When iterated to list + Then the result should be unordered + | result | + | d[29].i | + + # Multi-traversal within() where one traversal produces multiple results - use fold() to collect + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_withinXVXvid1X_outXknowsX_valuesXnameX_constantXpeterXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("name", P.within(__.V(vid1).out("knows").values("name").fold(), __.constant("peter"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[vadas] | + | v[josh] | + | v[peter] | + + # Multi-traversal within() where one traversal produces no results - still matches on the other + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_withinXVXvid1X_valuesXnonexistentX_constantXmarkoXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("name", P.within(__.V(vid1).values("nonexistent"), __.constant("marko"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | + + # Multi-traversal within() where all traversals produce no results - filters everything + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_withinXVXvid1X_valuesXnonexistentX_VXvid1X_valuesXnonexistentXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().has("name", P.within(__.V(vid1).values("nonexistent"), __.V(vid1).values("nonexistent"))) + """ + When iterated to list + Then the result should be empty + + # Multi-traversal without() with three traversals + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_withoutXVXvid1X_valuesXnameX_VXvid2X_valuesXnameX_VXvid3X_valuesXnameXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid2 defined as "v[vadas].id" + And using the parameter vid3 defined as "v[peter].id" + And the traversal of + """ + g.V().has("name", P.without(__.V(vid1).values("name"), __.V(vid2).values("name"), __.V(vid3).values("name"))) + """ + When iterated to list + Then the result should be unordered + | result | + | v[josh] | + | v[lop] | + | v[ripple] | + + # Multi-traversal within() - union of relationship traversals from different sources + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_withinXVXvid1X_outXknowsX_valuesXnameX_VXvid3X_outXcreatedX_valuesXnameXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid3 defined as "v[josh].id" + And the traversal of + """ + g.V().has("name", P.within(__.V(vid1).out("knows").values("name").fold(), __.V(vid3).out("created").values("name").fold())) + """ + When iterated to list + Then the result should be unordered + | result | + | v[vadas] | + | v[josh] | + | v[ripple] | + | v[lop] | + + # Multi-traversal without() - exclusion from multiple relationship sources + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasLabelXsoftwareX_hasXname_withoutXVXvid1X_outXcreatedX_valuesXnameX_VXvid3X_outXcreatedX_valuesXnameXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid3 defined as "v[josh].id" + And the traversal of + """ + g.V().hasLabel("software").has("name", P.without(__.V(vid1).out("created").values("name").fold(), __.V(vid3).out("created").values("name").fold())) + """ + When iterated to list + Then the result should be empty + + # Multi-traversal within() with is() - cross-label dynamic filtering, use fold() for multi-result + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasLabelXpersonX_valuesXageX_isXwithinXVXvid1X_valuesXageX_V_hasXname_lopX_inXcreatedX_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().hasLabel("person").values("age").is(P.within(__.V(vid1).values("age").fold(), __.V().has("name","lop").in("created").values("age").fold())) + """ + When iterated to list + Then the result should be unordered + | result | + | d[29].i | + | d[32].i | + | d[35].i | + + # Multi-traversal within() - dynamic edge filtering via inV property check + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_outEXknowsX_filterXinV_hasXname_withinXV_hasXname_lopX_inXcreatedX_valuesXnameX_V_hasXname_rippleX_inXcreatedX_valuesXnameXXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).outE("knows").filter(__.inV().has("name", P.within(__.V().has("name","lop").in("created").values("name").fold(), __.V().has("name","ripple").in("created").values("name").fold()))) + """ + When iterated to list + Then the result should be unordered + | result | + | e[marko-knows->josh] | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/IsTraversal.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/IsTraversal.feature new file mode 100644 index 0000000000..6872769c9b --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/IsTraversal.feature @@ -0,0 +1,273 @@ +# 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. + +@StepClassFilter @StepIs +Feature: Step - is() with traversal-bearing predicates + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXVXvid1X_valuesXageXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("age").is(__.V(vid1).values("age")) + """ + When iterated to list + Then the result should be unordered + | result | + | d[29].i | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXnameX_isXVXvid1X_valuesXnameXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("name").is(__.V(vid1).values("name")) + """ + When iterated to list + Then the result should be unordered + | result | + | marko | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXgtXVXvid1X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("age").is(P.gt(__.V(vid1).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | d[32].i | + | d[35].i | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXltXVXvid3X_valuesXageXXX + Given the modern graph + And using the parameter vid3 defined as "v[josh].id" + And the traversal of + """ + g.V().values("age").is(P.lt(__.V(vid3).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | d[29].i | + | d[27].i | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXneqXVXvid4X_valuesXageXXX + Given the modern graph + And using the parameter vid4 defined as "v[peter].id" + And the traversal of + """ + g.V().values("age").is(P.neq(__.V(vid4).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | d[29].i | + | d[27].i | + | d[32].i | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXwithinXVXvid1X_outXknowsX_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("age").is(P.within(__.V(vid1).out("knows").values("age").fold())) + """ + When iterated to list + Then the result should be unordered + | result | + | d[27].i | + | d[32].i | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXVXvid1X_valuesXnonexistentXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("age").is(__.V(vid1).values("nonexistent")) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasLabelXpersonX_valuesXageX_chooseXgtXVXvid1X_valuesXageXX_olderX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().hasLabel("person").values("age").choose(__.is(P.gt(__.V(vid1).values("age"))), __.constant("older than marko"), __.constant("not older")) + """ + When iterated to list + Then the result should be unordered + | result | + | not older | + | not older | + | older than marko | + | older than marko | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasLabelXpersonX_valuesXageX_chooseXgteXmeanAgeX_aboveX + Given the modern graph + And the traversal of + """ + g.V().hasLabel("person").values("age").choose(__.is(P.gte(__.V().hasLabel("person").values("age").mean())), __.constant("above average"), __.constant("below average")) + """ + When iterated to list + Then the result should be unordered + | result | + | below average | + | below average | + | above average | + | above average | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_allXgteXVXvid2X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid2 defined as "v[vadas].id" + And the traversal of + """ + g.V(vid1).out("knows").values("age").fold().all(P.gte(__.V(vid2).values("age"))) + """ + When iterated to list + Then the result should have a count of 1 + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_allXgtXVXvid2X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid2 defined as "v[vadas].id" + And the traversal of + """ + g.V(vid1).out("knows").values("age").fold().all(P.gt(__.V(vid2).values("age"))) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_anyXeqXVXvid3X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid3 defined as "v[josh].id" + And the traversal of + """ + g.V(vid1).out("knows").values("age").fold().any(P.eq(__.V(vid3).values("age"))) + """ + When iterated to list + Then the result should have a count of 1 + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_noneXeqXVXvid4X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid4 defined as "v[peter].id" + And the traversal of + """ + g.V(vid1).out("knows").values("age").fold().none(P.eq(__.V(vid4).values("age"))) + """ + When iterated to list + Then the result should have a count of 1 + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_outXknowsX_valuesXageX_fold_noneXeqXVXvid3X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid3 defined as "v[josh].id" + And the traversal of + """ + g.V(vid1).out("knows").values("age").fold().none(P.eq(__.V(vid3).values("age"))) + """ + When iterated to list + Then the result should be empty + + # Empty-result handling: predicate should treat "no results" as "no match", not null. + @GraphComputerVerificationMidVNotSupported + Scenario: g_injectXnullX_isXeqXV9999_valuesXnameXXX + Given the modern graph + And the traversal of + """ + g.inject(null).is(P.eq(__.V(9999).values("name"))) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationMidVNotSupported + Scenario: g_injectXmarkoX_isXV9999_valuesXnameXX + Given the modern graph + And the traversal of + """ + g.inject("marko").is(__.V(9999).values("name")) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationMidVNotSupported + Scenario: g_injectXmarkoX_chooseXeqXV9999_valuesXnameXX_matched_unmatchedX + Given the modern graph + And the traversal of + """ + g.inject("marko").choose(__.is(P.eq(__.V(9999).values("name"))), __.constant("matched"), __.constant("unmatched")) + """ + When iterated to list + Then the result should be unordered + | result | + | unmatched | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_injectXlistX_noneXeqXV9999_valuesXnameXXX + Given the modern graph + And the traversal of + """ + g.inject(["marko","josh"]).none(P.eq(__.V(9999).values("name"))) + """ + When iterated to list + Then the result should have a count of 1 + + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_hasXname_eqXV9999_valuesXnameXXX + Given the modern graph + And the traversal of + """ + g.V().has("name", P.eq(__.V(9999).values("name"))) + """ + When iterated to list + Then the result should be empty + + # Multi-traversal without() in is() context + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_isXwithoutXVXvid1X_valuesXageX_VXvid2X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid2 defined as "v[josh].id" + And the traversal of + """ + g.V().values("age").is(P.without(__.V(vid1).values("age"), __.V(vid2).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | d[27].i | + | d[35].i | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/WhereTraversal.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/WhereTraversal.feature new file mode 100644 index 0000000000..d9529d6be8 --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/WhereTraversal.feature @@ -0,0 +1,122 @@ +# 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. + +@StepClassFilter @StepWhere +Feature: Step - where(P) with traversal-bearing predicates + + # where(P.gt(traversal)) - compare current traverser value against resolved traversal result + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_whereXgtXVXvid1X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("age").where(P.gt(__.V(vid1).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | d[32].i | + | d[35].i | + + # where(P.lt(traversal)) - filter ages less than josh's age + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_whereXltXVXvid3X_valuesXageXXX + Given the modern graph + And using the parameter vid3 defined as "v[josh].id" + And the traversal of + """ + g.V().values("age").where(P.lt(__.V(vid3).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | d[29].i | + | d[27].i | + + # where(P.eq(traversal)) - exact match against resolved value + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_whereXeqXVXvid1X_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("age").where(P.eq(__.V(vid1).values("age"))) + """ + When iterated to list + Then the result should be unordered + | result | + | d[29].i | + + # where(P.within(traversal)) - collection membership against resolved traversal results + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_whereXwithinXVXvid1X_outXknowsX_valuesXageXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("age").where(P.within(__.V(vid1).out("knows").values("age").fold())) + """ + When iterated to list + Then the result should be unordered + | result | + | d[27].i | + | d[32].i | + + # where(P.neq(traversal)) - not equal to resolved value + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXnameX_whereXneqXVXvid1X_valuesXnameXXX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().values("name").where(P.neq(__.V(vid1).values("name"))) + """ + When iterated to list + Then the result should be unordered + | result | + | vadas | + | lop | + | josh | + | ripple | + | peter | + + # Empty traversal result - filters out (no match) + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_valuesXageX_whereXeqXV9999_valuesXageXXX + Given the modern graph + And the traversal of + """ + g.V().values("age").where(P.eq(__.V(9999).values("age"))) + """ + When iterated to list + Then the result should be empty + + # where(P.gt(traversal)) with by() modulator - compare property values + @GraphComputerVerificationMidVNotSupported + Scenario: g_V_whereXgtXVXvid1X_valuesXageXXX_byXageX_valuesXnameX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V().where(P.gt(__.V(vid1).values("age"))).by("age").values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | josh | + | peter | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature new file mode 100644 index 0000000000..c8535d6a74 --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/VETraversal.feature @@ -0,0 +1,149 @@ +# 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 @StepVertex @StepE +Feature: Step - V(traversal) and E(traversal) + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_id_VXidentityX_name + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).id().V(__.identity()).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | marko | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1_vid2X_id_VXidentityX_name + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid2 defined as "v[josh].id" + And the traversal of + """ + g.V(vid1, vid2).id().V(__.identity()).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | marko | + | josh | + + # Use as()/select() to bookmark a vertex ID and return to it later via V(select()). + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_id_asXbookmarkX_V_hasXname_joshX_VXselectXbookmarkXX_name + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).id().as("bookmark").V().has("name","josh").V(__.select("bookmark")).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | marko | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_VXoutXknowsX_idX_name + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).V(__.out("knows").id()).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | vadas | + | josh | + + @GraphComputerVerificationMidENotSupported + Scenario: g_VXvid1X_EXoutE_idX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).E(__.outE().id()) + """ + When iterated to list + Then the result should be unordered + | result | + | e[marko-created->lop] | + | e[marko-knows->josh] | + | e[marko-knows->vadas] | + + @GraphComputerVerificationMidVNotSupported + Scenario: g_injectX9999X_VXidentityX + Given the modern graph + And the traversal of + """ + g.inject(9999).V(__.identity()) + """ + When iterated to list + Then the result should be empty + + @GraphComputerVerificationMidENotSupported + Scenario: g_VXvid1X_outEXknowsX_hasXinV_name_vadasX_id_EXidentityX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).outE("knows").filter(__.inV().has("name","vadas")).id().E(__.identity()) + """ + When iterated to list + Then the result should be unordered + | result | + | e[marko-knows->vadas] | + + @GraphComputerVerificationMidENotSupported + Scenario: g_injectX9999X_EXidentityX + Given the modern graph + And the traversal of + """ + g.inject(9999).E(__.identity()) + """ + When iterated to list + Then the result should be empty + + Scenario: g_VXVXvid1X_idX_name + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(__.V(vid1).id()).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | marko | + + Scenario: g_EXVXvid1X_outE_idX + Given the modern graph + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.E(__.V(vid1).outE().id()) + """ + When iterated to list + Then the result should be unordered + | result | + | e[marko-created->lop] | + | e[marko-knows->josh] | + | e[marko-knows->vadas] | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/PropertyTraversal.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/PropertyTraversal.feature new file mode 100644 index 0000000000..8c420205bf --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/PropertyTraversal.feature @@ -0,0 +1,253 @@ +# 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. + +@StepClassSideEffect @StepAddProperty +Feature: Step - property() with traversal arguments + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid2X_propertyXalias_VXvid1X_valuesXnameXX + Given the empty graph + And the graph initializer of + """ + g.addV("person").property("name","marko").property("age",29). + addV("person").property("name","vadas").property("age",27). + addV("software").property("name","lop").property("lang","java"). + addV("person").property("name","josh").property("age",32). + addV("person").property("name","peter").property("age",35). + addV("software").property("name","ripple").property("lang","java") + """ + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid2 defined as "v[josh].id" + And the traversal of + """ + g.V(vid2).property("alias", __.V(vid1).values("name")) + """ + When iterated to list + Then the result should have a count of 1 + And the graph should return 1 for count of "g.V().has(\"alias\", \"marko\")" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_propertyXcreatorCount_VXvid1X_inXcreatedX_countX + Given the empty graph + And the graph initializer of + """ + g.addV("person").property("name","marko").property("age",29).as("marko"). + addV("person").property("name","vadas").property("age",27). + addV("software").property("name","lop").property("lang","java").as("lop"). + addV("person").property("name","josh").property("age",32).as("josh"). + addV("person").property("name","peter").property("age",35).as("peter"). + addV("software").property("name","ripple").property("lang","java").as("ripple"). + addE("knows").from("marko").to(__.V().has("name","vadas")). + addE("knows").from("marko").to("josh"). + addE("created").from("marko").to("lop"). + addE("created").from("josh").to("lop"). + addE("created").from("josh").to("ripple"). + addE("created").from("peter").to("lop") + """ + And using the parameter vid1 defined as "v[lop].id" + And the traversal of + """ + g.V(vid1).property("creatorCount", __.V(vid1).in("created").count()) + """ + When iterated to list + Then the result should have a count of 1 + And the graph should return 1 for count of "g.V().has(\"creatorCount\", 3L)" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_propertyXknownCount_outXknowsX_countX + Given the empty graph + And the graph initializer of + """ + 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"). + addV("person").property("name","josh").property("age",32).as("josh"). + addV("person").property("name","peter").property("age",35). + addV("software").property("name","ripple").property("lang","java"). + addE("knows").from("marko").to("vadas"). + addE("knows").from("marko").to("josh") + """ + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).property("knownCount", __.out("knows").count()) + """ + When iterated to list + Then the result should have a count of 1 + And the graph should return 1 for count of "g.V().has(\"knownCount\", 2L)" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_propertyXcreator_inXcreatedX_valuesXnameXX + Given the empty graph + And the graph initializer of + """ + g.addV("person").property("name","marko").property("age",29).as("marko"). + addV("person").property("name","vadas").property("age",27). + addV("software").property("name","lop").property("lang","java").as("lop"). + addV("person").property("name","josh").property("age",32).as("josh"). + addV("person").property("name","peter").property("age",35).as("peter"). + addV("software").property("name","ripple").property("lang","java").as("ripple"). + addE("knows").from("marko").to(__.V().has("name","vadas")). + addE("knows").from("marko").to("josh"). + addE("created").from("marko").to("lop"). + addE("created").from("josh").to("lop"). + addE("created").from("josh").to("ripple"). + addE("created").from("peter").to("lop") + """ + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).property("creator", __.in("created").values("name")) + """ + When iterated to list + Then the result should have a count of 1 + And the graph should return 0 for count of "g.V().has(\"creator\")" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid2X_propertyXVXvid1X_projectXfriendCount_softwareCreatedXX + Given the empty graph + And the graph initializer of + """ + 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("person").property("name","peter").property("age",35).as("peter"). + addV("software").property("name","ripple").property("lang","java").as("ripple"). + addE("knows").from("marko").to("vadas"). + addE("knows").from("marko").to("josh"). + addE("created").from("marko").to("lop"). + addE("created").from("josh").to("lop"). + addE("created").from("josh").to("ripple"). + addE("created").from("peter").to("lop") + """ + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid2 defined as "v[josh].id" + And the traversal of + """ + g.V(vid2).property(__.V(vid1).project("friendCount","softwareCreated").by(__.out("knows").count()).by(__.out("created").values("name"))) + """ + When iterated to list + Then the result should have a count of 1 + And the graph should return 1 for count of "g.V().has(\"friendCount\", 2L)" + And the graph should return 1 for count of "g.V().has(\"softwareCreated\", \"lop\")" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid2X_propertyXVXvid1X_projectXoriginalName_originalLabelX_byXnameX_byXlabelXX + Given the empty graph + And the graph initializer of + """ + g.addV("person").property("name","marko").property("age",29).as("marko"). + addV("person").property("name","vadas").property("age",27). + addV("software").property("name","lop").property("lang","java").as("lop"). + addV("person").property("name","josh").property("age",32). + addV("person").property("name","peter").property("age",35). + addV("software").property("name","ripple").property("lang","java") + """ + And using the parameter vid1 defined as "v[marko].id" + And using the parameter vid2 defined as "v[lop].id" + And the traversal of + """ + g.V(vid2).property(__.V(vid1).project("originalName","originalLabel").by(__.values("name")).by(__.label())) + """ + When iterated to list + Then the result should have a count of 1 + And the graph should return 1 for count of "g.V(vid2).has(\"originalName\", \"marko\")" + And the graph should return 1 for count of "g.V(vid2).has(\"originalLabel\", \"person\")" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_propertyXlist_friends_outXknowsX_valuesXnameXX + Given the empty graph + And the graph initializer of + """ + 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"). + addV("person").property("name","josh").property("age",32).as("josh"). + addV("person").property("name","peter").property("age",35). + addV("software").property("name","ripple").property("lang","java"). + addE("knows").from("marko").to("vadas"). + addE("knows").from("marko").to("josh") + """ + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).property(Cardinality.list, "friends", __.out("knows").values("name")) + """ + When iterated to list + Then the result should have a count of 1 + And the graph should return 2 for count of "g.V(vid1).properties(\"friends\")" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_propertyXset_langs_outXcreatedX_valuesXlangXX + Given the empty graph + And the graph initializer of + """ + g.addV("person").property("name","marko").property("age",29). + addV("person").property("name","vadas").property("age",27). + addV("software").property("name","lop").property("lang","java").as("lop"). + addV("person").property("name","josh").property("age",32).as("josh"). + addV("person").property("name","peter").property("age",35). + addV("software").property("name","ripple").property("lang","java").as("ripple"). + addE("created").from("josh").to("lop"). + addE("created").from("josh").to("ripple") + """ + And using the parameter vid1 defined as "v[josh].id" + And the traversal of + """ + g.V(vid1).property(Cardinality.set, "langs", __.out("created").values("lang")) + """ + When iterated to list + Then the result should have a count of 1 + And the graph should return 1 for count of "g.V(vid1).properties(\"langs\")" + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_propertyXsingle_friend_outXknowsX_valuesXnameXX + Given the empty graph + And the graph initializer of + """ + 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"). + addV("person").property("name","josh").property("age",32).as("josh"). + addV("person").property("name","peter").property("age",35). + addV("software").property("name","ripple").property("lang","java"). + addE("knows").from("marko").to("vadas"). + addE("knows").from("marko").to("josh") + """ + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).property(Cardinality.single, "friend", __.out("knows").values("name")) + """ + When iterated to list + Then the traversal will raise an error + + @GraphComputerVerificationMidVNotSupported + Scenario: g_VXvid1X_propertyXVXvid1X_groupX_byXidX_byXvaluesXnameXXX + Given the empty graph + And the graph initializer of + """ + g.addV("person").property("name","marko").property("age",29) + """ + And using the parameter vid1 defined as "v[marko].id" + And the traversal of + """ + g.V(vid1).property(__.V(vid1).group().by(__.id()).by(__.values("name"))) + """ + When iterated to list + Then the traversal will raise an error
