From 0485ab37768530ecd049e5ad8d9d339267af726b Mon Sep 17 00:00:00 2001 From: xiazcy Date: Sat, 27 Jun 2026 12:07:41 -0700 Subject: [PATCH 01/10] Add an example zoo graph --- .../tinkergraph/structure/TinkerFactory.java | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerFactory.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerFactory.java index fc6105e9f81..15b1c99322b 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerFactory.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerFactory.java @@ -21,6 +21,7 @@ import org.apache.commons.configuration2.BaseConfiguration; import org.apache.commons.configuration2.Configuration; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.LabelCardinality; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; @@ -252,6 +253,191 @@ public static void generateAirRoutes(final AbstractTinkerGraph graph) { } } + /** + * Create "the zoo" graph — a TinkerPop 4.x toy graph showcasing multi-label vertex support + * and diverse property types. Contains 13 vertices, 20 edges with animals classified across + * taxonomy, behavior, and conservation axes. + */ + public static TinkerGraph createTheZoo() { + final Configuration conf = getConfigurationWithCurrentNumberManager(); + conf.setProperty(AbstractTinkerGraph.GREMLIN_TINKERGRAPH_VERTEX_LABEL_CARDINALITY, LabelCardinality.ZERO_OR_MORE.name()); + final TinkerGraph g = TinkerGraph.open(conf); + generateTheZoo(g); + return g; + } + + /** + * Generate the graph in {@link #createTheZoo()} into an existing graph. The graph must support + * multi-label vertices (LabelCardinality of ONE_OR_MORE or ZERO_OR_MORE). + */ + public static void generateTheZoo(final AbstractTinkerGraph g) { + // Animals + final Vertex tux = g.addVertex(T.id, 1, T.label, "animal"); + tux.addLabel("bird", "aquatic", "endangered"); + tux.property("name", "tux"); + tux.property("species", "african penguin"); + tux.property("weight", 3.7d); + tux.property("age", 5); + tux.property("captiveBorn", true); + tux.property(VertexProperty.Cardinality.list, "diet", "fish"); + tux.property(VertexProperty.Cardinality.list, "diet", "krill"); + tux.property(VertexProperty.Cardinality.list, "diet", "squid"); + + final Vertex atlas = g.addVertex(T.id, 2, T.label, "animal"); + atlas.addLabel("reptile", "aquatic", "endangered"); + atlas.property("name", "atlas"); + atlas.property("species", "green sea turtle"); + atlas.property("weight", 180.5d); + atlas.property("age", 30); + atlas.property("captiveBorn", false); + atlas.property(VertexProperty.Cardinality.list, "diet", "seagrass"); + atlas.property(VertexProperty.Cardinality.list, "diet", "algae"); + + final Vertex ripple = g.addVertex(T.id, 3, T.label, "animal"); + ripple.addLabel("mammal", "aquatic"); + ripple.property("name", "ripple"); + ripple.property("species", "bottlenose dolphin"); + ripple.property("weight", 220.0d); + ripple.property("age", 12); + ripple.property("captiveBorn", true); + ripple.property(VertexProperty.Cardinality.list, "diet", "fish"); + ripple.property(VertexProperty.Cardinality.list, "diet", "squid"); + + final Vertex monty = g.addVertex(T.id, 4, T.label, "animal"); + monty.addLabel("reptile", "nocturnal"); + monty.property("name", "monty"); + monty.property("species", "ball python"); + monty.property("weight", 1.8d); + monty.property("age", 8); + monty.property("captiveBorn", true); + monty.property("venomous", false); + monty.property(VertexProperty.Cardinality.list, "diet", "mice"); + monty.property(VertexProperty.Cardinality.list, "diet", "rats"); + + final Vertex echo = g.addVertex(T.id, 5, T.label, "animal"); + echo.addLabel("mammal", "flying", "nocturnal"); + echo.property("name", "echo"); + echo.property("species", "fruit bat"); + echo.property("weight", 0.3d); + echo.property("age", 3); + echo.property("captiveBorn", true); + echo.property(VertexProperty.Cardinality.list, "diet", "fruit"); + echo.property(VertexProperty.Cardinality.list, "diet", "nectar"); + + final Vertex blaze = g.addVertex(T.id, 6, T.label, "animal"); + blaze.addLabel("mammal", "endangered", "nocturnal"); + blaze.property("name", "blaze"); + blaze.property("species", "red panda"); + blaze.property("weight", 5.4d); + blaze.property("age", 4); + blaze.property("captiveBorn", false); + blaze.property(VertexProperty.Cardinality.list, "diet", "bamboo"); + blaze.property(VertexProperty.Cardinality.list, "diet", "fruit"); + blaze.property(VertexProperty.Cardinality.list, "diet", "insects"); + + final Vertex titan = g.addVertex(T.id, 7, T.label, "animal"); + titan.addLabel("mammal", "endangered"); + titan.property("name", "titan"); + titan.property("species", "african elephant"); + titan.property("weight", 4000.0d); + titan.property("age", 15); + titan.property("captiveBorn", false); + titan.property(VertexProperty.Cardinality.list, "diet", "grass"); + titan.property(VertexProperty.Cardinality.list, "diet", "leaves"); + titan.property(VertexProperty.Cardinality.list, "diet", "bark"); + + final Vertex bitsy = g.addVertex(T.id, 8, T.label, "animal"); + bitsy.addLabel("mammal", "nocturnal"); + bitsy.property("name", "bitsy"); + bitsy.property("species", "harvest mouse"); + bitsy.property("weight", 0.006d); + bitsy.property("age", 1); + bitsy.property("captiveBorn", true); + bitsy.property(VertexProperty.Cardinality.list, "diet", "seeds"); + bitsy.property(VertexProperty.Cardinality.list, "diet", "insects"); + + final Vertex splash = g.addVertex(T.id, 9, T.label, "animal"); + splash.addLabel("mammal", "aquatic", "nocturnal", "endangered"); + splash.property("name", "splash"); + splash.property("species", "fishing cat"); + splash.property("weight", 8.2d); + splash.property("age", 2); + splash.property("captiveBorn", false); + splash.property(VertexProperty.Cardinality.list, "diet", "fish"); + splash.property(VertexProperty.Cardinality.list, "diet", "frogs"); + splash.property(VertexProperty.Cardinality.list, "diet", "crustaceans"); + + final Vertex tinker = g.addVertex(T.id, 10, T.label, "animal"); + tinker.addLabel("mammal", "nocturnal", "endangered"); + tinker.property("name", "tinker"); + tinker.property("species", "bengal tiger"); + tinker.property("weight", 220.0d); + tinker.property("age", 5); + tinker.property("captiveBorn", false); + tinker.property(VertexProperty.Cardinality.list, "diet", "deer"); + tinker.property(VertexProperty.Cardinality.list, "diet", "boar"); + tinker.property(VertexProperty.Cardinality.list, "diet", "snakes"); + + // Habitats + final Vertex lagoon = g.addVertex(T.id, 11, T.label, "habitat"); + lagoon.addLabel("aquatic"); + lagoon.property("name", "lagoon"); + lagoon.property("biome", "marine"); + lagoon.property("capacity", 8); + lagoon.property("openAir", true); + + final Vertex canopy = g.addVertex(T.id, 12, T.label, "habitat"); + canopy.property("name", "canopy"); + canopy.property("biome", "tropical"); + canopy.property("capacity", 8); + canopy.property("openAir", false); + + // People + final Vertex drGremlin = g.addVertex(T.id, 13, T.label, "person"); + drGremlin.addLabel("veterinarian", "keeper"); + drGremlin.property("name", "dr_gremlin"); + drGremlin.property("since", 2015); + drGremlin.property(VertexProperty.Cardinality.list, "specialties", "conservation"); + drGremlin.property(VertexProperty.Cardinality.list, "specialties", "surgery"); + drGremlin.property(VertexProperty.Cardinality.list, "specialties", "nutrition"); + + // Edges: livesIn + tux.addEdge("livesIn", lagoon, T.id, 14, "since", 2020); + atlas.addEdge("livesIn", lagoon, T.id, 15, "since", 2018); + ripple.addEdge("livesIn", lagoon, T.id, 16, "since", 2019); + splash.addEdge("livesIn", lagoon, T.id, 17, "since", 2023); + monty.addEdge("livesIn", canopy, T.id, 18, "since", 2021); + echo.addEdge("livesIn", canopy, T.id, 19, "since", 2022); + blaze.addEdge("livesIn", canopy, T.id, 20, "since", 2023); + bitsy.addEdge("livesIn", canopy, T.id, 21, "since", 2024); + tinker.addEdge("livesIn", canopy, T.id, 22, "since", 2020); + titan.addEdge("livesIn", canopy, T.id, 23, "since", 2019); + + // Edges: careFor + drGremlin.addEdge("careFor", atlas, T.id, 24, "specialty", "conservation"); + drGremlin.addEdge("careFor", blaze, T.id, 25, "specialty", "conservation"); + drGremlin.addEdge("careFor", splash, T.id, 26, "specialty", "conservation"); + drGremlin.addEdge("careFor", tinker, T.id, 27, "specialty", "conservation"); + + // Edges: friendsWith + tux.addEdge("friendsWith", atlas, T.id, 28, "since", 2020); + ripple.addEdge("friendsWith", tux, T.id, 29, "since", 2020); + titan.addEdge("friendsWith", blaze, T.id, 30, "since", 2022); + + // Edges: eats (food chain: tinker → monty → bitsy) + tinker.addEdge("eats", monty, T.id, 31); + monty.addEdge("eats", bitsy, T.id, 32); + + // Edges: avoids + echo.addEdge("avoids", bitsy, T.id, 33); + + g.variables().set("name", "the-zoo"); + g.variables().set("creator", "tinkerpop"); + g.variables().set("comment", + "this graph showcases multi-label vertex support and diverse " + + "property types introduced in tinkerpop 4.x"); + } + private static TinkerGraph getTinkerGraphWithCurrentNumberManager() { return TinkerGraph.open(getConfigurationWithCurrentNumberManager()); } From 9ed8d031c5d1a7e1402c25c54bdd6e2794fc5911 Mon Sep 17 00:00:00 2001 From: Cole Greer Date: Mon, 6 Jul 2026 13:08:05 -0700 Subject: [PATCH 02/10] Wire up the zoo graph and convert read-only multi-label scenarios Adds ZOO to the GraphData enum and wires it into every test harness that already supports the other named toy graphs: TinkerWorld (embedded, transaction, and shuffle graph variants), RemoteWorld/TinkerFactoryDataLoader for server-backed tests, both gremlin-server-integration.yaml configs, and the Python, JavaScript, Go, and .NET GLV Gherkin runners. The zoo graph uses tinkergraph-multilabel.properties since it requires ZERO_OR_MORE vertex label cardinality. Updated the two graph-manager tests whose assertions hardcoded the total configured graph count, which shifted by one with the new entry. Converts scenarios in ElementMap.feature, ValueMap.feature, and Labels.feature from a bespoke empty-graph-plus-initializer fixture to the zoo graph wherever the traversal under test is read-only and the zoo's pre-populated multi-label vertices/edges (tux, lagoon, canopy, atlas, etc.) can express the same shape deterministically. Scenarios that mutate the graph, need a vertex with zero or exactly-one label, or depend on a provider's implicit default label-rendering mode (which is a per-World configuration switch independent of graph data) remain on the empty graph. Assisted-by: Claude Code:claude-sonnet-5 --- .../gremlin-server-integration.yaml | 4 + .../Gherkin/ScenarioData.cs | 2 +- gremlin-go/driver/cucumber/cucumberWorld.go | 2 +- .../test/cucumber/gremlin.js | 16 +- .../gremlin-javascript/test/cucumber/world.js | 2 +- .../src/main/python/tests/feature/terrain.py | 4 +- .../server/util/TinkerFactoryDataLoader.java | 5 +- .../gremlin/driver/remote/RemoteWorld.java | 3 + .../server/util/CheckedGraphManagerTest.java | 2 +- .../server/util/DefaultGraphManagerTest.java | 13 +- .../server/gremlin-server-integration.yaml | 4 + .../tinkerpop/gremlin/LoadGraphWith.java | 20 +- .../language/translator/translations.json | 256 ++++++------------ .../test/features/map/ElementMap.feature | 34 +-- .../gremlin/test/features/map/Label.feature | 16 +- .../gremlin/test/features/map/Labels.feature | 16 +- .../test/features/map/ValueMap.feature | 22 +- .../gremlin/tinkergraph/TinkerWorld.java | 17 ++ 18 files changed, 182 insertions(+), 256 deletions(-) diff --git a/docker/gremlin-server/gremlin-server-integration.yaml b/docker/gremlin-server/gremlin-server-integration.yaml index a730970f851..4f2f226b420 100644 --- a/docker/gremlin-server/gremlin-server-integration.yaml +++ b/docker/gremlin-server/gremlin-server-integration.yaml @@ -44,6 +44,9 @@ graphs: { sink: { configuration: conf/tinkergraph-service.properties, traversalSources: [{name: gsink}]}, + zoo: { + configuration: conf/tinkergraph-multilabel.properties, + traversalSources: [{name: gzoo}]}, tx: { configuration: conf/tinkertransactiongraph-service.properties, traversalSources: [{name: gtx}]}} @@ -53,6 +56,7 @@ lifecycleHooks: - { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: crew, dataset: crew}} - { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: grateful, dataset: grateful}} - { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: sink, dataset: sink}} + - { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: zoo, dataset: zoo}} scriptEngines: { gremlin-lang : {}, gremlin-groovy: { diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/ScenarioData.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/ScenarioData.cs index b337594415b..5cf427fe927 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/ScenarioData.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/ScenarioData.cs @@ -39,7 +39,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin { internal class ScenarioData : IDisposable { - private static readonly string[] GraphNames = {"modern", "classic", "crew", "grateful", "sink"}; + private static readonly string[] GraphNames = {"modern", "classic", "crew", "grateful", "sink", "zoo"}; private static readonly IDictionary EmptyVertices = new ReadOnlyDictionary(new Dictionary()); diff --git a/gremlin-go/driver/cucumber/cucumberWorld.go b/gremlin-go/driver/cucumber/cucumberWorld.go index ecc50d34c5d..a817cf11a84 100644 --- a/gremlin-go/driver/cucumber/cucumberWorld.go +++ b/gremlin-go/driver/cucumber/cucumberWorld.go @@ -87,7 +87,7 @@ func NewCucumberWorld() *CucumberWorld { } } -var graphNames = []string{"modern", "classic", "crew", "grateful", "sink", "empty", "multilabel"} +var graphNames = []string{"modern", "classic", "crew", "grateful", "sink", "zoo", "empty", "multilabel"} func (t *CucumberWorld) getDataGraphFromMap(name string) *DataGraph { if val, ok := t.graphDataMap[name]; ok { diff --git a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js index 2f86b2734d4..a615f91d136 100644 --- a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js @@ -1295,15 +1295,15 @@ const gremlins = { g_V_elementMapXname_age_nullX: [function({g}) { return g.with_("singlelabel").V().elementMap("name", "age", null) }], g_withXmultilabelX_VXmarkoX_elementMap: [function({g}) { return g.with_("multilabel").V().has("name", "marko").elementMap() }], g_V_elementMap_single_label_default: [function({g}) { return g.addV("person").addLabel("employee").property("name", "marko") }, function({g}) { return g.V().elementMap() }], - g_withXmultilabelX_V_elementMap_multilabel: [function({g}) { return g.addV("person").addLabel("employee").property("name", "marko") }, function({g}) { return g.with_("multilabel").V().elementMap() }], + g_withXmultilabelX_V_elementMap_multilabel: [function({g}) { return g.with_("multilabel").V().has("name", "tux").elementMap("name", "species") }], g_V_elementMap_multi_label_default: [function({g}) { return g.addV("person").addLabel("employee").property("name", "marko") }, function({g}) { return g.V().elementMap() }], - g_withXsinglelabelX_V_elementMap_multi_label_default: [function({g}) { return g.addV("person").addLabel("employee").property("name", "marko") }, function({g}) { return g.with_("singlelabel").V().elementMap() }], + g_withXsinglelabelX_V_elementMap_multi_label_default: [function({g}) { return g.with_("singlelabel").V().has("name", "lagoon").elementMap("name", "biome") }], g_V_elementMap_single_label_vertex_multi_label_default: [function({g}) { return g.addV("person").property("name", "marko") }, function({g}) { return g.V().elementMap() }], g_withXsinglelabelX_V_elementMap_zero_label_vertex: [function({g}) { return g.addV().property("name", "nobody") }, function({g}) { return g.with_("singlelabel").V().elementMap() }], g_withXmultilabelX_V_elementMap_zero_label_vertex: [function({g}) { return g.addV().property("name", "nobody") }, function({g}) { return g.with_("multilabel").V().elementMap() }], g_V_elementMap_zero_label_vertex_multi_label_default: [function({g}) { return g.addV().property("name", "nobody") }, function({g}) { return g.V().elementMap() }], g_V_elementMap_zero_label_vertex_single_label_default: [function({g}) { return g.addV().property("name", "nobody") }, function({g}) { return g.V().elementMap() }], - g_withXmultilabelX_E_elementMap: [function({g}) { return g.addV("person").property("name", "marko").as("a").addV("person").property("name", "josh").as("b").addE("knows").from_("a").to("b").property("weight", 0.5) }, function({g}) { return g.with_("multilabel").E().elementMap() }], + g_withXmultilabelX_E_elementMap: [function({g}) { return g.with_("multilabel").E().has("since", 2018).hasLabel("livesIn").elementMap() }], g_E_elementMap_multi_label_default: [function({g}) { return g.addV("person").property("name", "marko").as("a").addV("person").property("name", "josh").as("b").addE("knows").from_("a").to("b").property("weight", 0.5) }, function({g}) { return g.E().elementMap() }], g_E_elementMap_single_label_default: [function({g}) { return g.addV("person").property("name", "marko").as("a").addV("person").property("name", "josh").as("b").addE("knows").from_("a").to("b").property("weight", 0.5) }, function({g}) { return g.E().elementMap() }], g_withXsinglelabelX_E_elementMap: [function({g}) { return g.addV("person").property("name", "marko").as("a").addV("person").property("name", "josh").as("b").addE("knows").from_("a").to("b").property("weight", 0.5) }, function({g}) { return g.with_("singlelabel").E().elementMap() }], @@ -1358,11 +1358,11 @@ const gremlins = { g_V_valuesXnameX_lTrim: [function({g}) { return 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("software").property("name", " ripple ").property("lang", "java").as("ripple").addV("person").property("name", "peter").property("age", 35).as("peter").addE("knows").from_("marko").to("vadas").property("weight", 0.5).addE("knows").from_("marko").to("josh").property("weight", 1.0).addE("created").from_("marko").to("lop").property("weight", 0.4).addE("created").from_("josh").to("ripple").property("weight", 1.0).addE("created").from_("josh").to("lop").property("weight", 0.4).addE("created").from_("peter").to("lop").property("weight", 0.2) }, function({g}) { return g.V().values("name").lTrim() }], g_V_valuesXnameX_order_fold_lTrimXlocalX: [function({g}) { return 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("software").property("name", " ripple ").property("lang", "java").as("ripple").addV("person").property("name", "peter").property("age", 35).as("peter").addE("knows").from_("marko").to("vadas").property("weight", 0.5).addE("knows").from_("marko").to("josh").property("weight", 1.0).addE("created").from_("marko").to("lop").property("weight", 0.4).addE("created").from_("josh").to("ripple").property("weight", 1.0).addE("created").from_("josh").to("lop").property("weight", 0.4).addE("created").from_("peter").to("lop").property("weight", 0.2) }, function({g}) { return g.V().values("name").order().fold().lTrim(Scope.local) }], g_V_label_single_label_graph: [function({g}) { return g.V().hasLabel("person").label() }], - g_V_label_deprecated_multilabel: [function({g}) { return g.addV("a", "b").property("name", "test") }, function({g}) { return g.V().label().count() }], - g_V_label_deprecated_multilabel_value_is_one_of_labels: [function({g}) { return g.addV("a", "b").property("name", "test") }, function({g}) { return g.V().filter(__.label().is(P.within("a", "b"))) }], + g_V_label_deprecated_multilabel: [function({g}) { return g.V().has("name", "tux").label().count() }], + g_V_label_deprecated_multilabel_value_is_one_of_labels: [function({g}) { return g.V().has("name", "tux").filter(__.label().is(P.within("animal", "bird", "aquatic", "endangered"))) }], g_E_label_deprecated_multilabel_graph: [function({g}) { return g.addV("person").as("a").addV("person").as("b").addE("knows").from_("a").to("b") }, function({g}) { return g.E().label() }], g_V_hasLabelXpersonX_labels: [function({g}) { return g.V().hasLabel("person").labels() }], - g_V_labels_multilabel: [function({g}) { return g.addV("a", "b") }, function({g}) { return g.V().labels() }], + g_V_labels_multilabel: [function({g}) { return g.V().has("name", "tux").labels() }], g_addVXa_bX_labels_count: [function({g}) { return g.addV("a", "b") }, function({g}) { return g.V().labels().count() }], g_addV_labels: [function({g}) { return g.addV() }, function({g}) { return g.V().labels() }], g_E_labels: [function({g}) { return g.E().hasLabel("knows").labels() }], @@ -1953,9 +1953,9 @@ const gremlins = { g_V_valueMapXname_ageX_byXisXxXXbyXunfoldX: [function({g}) { return g.V().valueMap("name", "age").by(__.is("x")).by(__.unfold()) }], g_withXmultilabelX_VXmarkoX_valueMap_withXtokensX: [function({g}) { return g.with_("multilabel").V().has("name", "marko").valueMap().with_(WithOptions.tokens) }], g_V_valueMap_withXtokensX_single_label_default: [function({g}) { return g.addV("person").addLabel("employee").property("name", "marko") }, function({g}) { return g.V().valueMap().with_(WithOptions.tokens) }], - g_withXmultilabelX_V_valueMap_withXtokensX_multilabel: [function({g}) { return g.addV("person").addLabel("employee").property("name", "marko") }, function({g}) { return g.with_("multilabel").V().valueMap().with_(WithOptions.tokens) }], + g_withXmultilabelX_V_valueMap_withXtokensX_multilabel: [function({g}) { return g.with_("multilabel").V().has("name", "tux").valueMap("name", "species").with_(WithOptions.tokens) }], g_V_valueMap_withXtokensX_multi_label_default: [function({g}) { return g.addV("person").addLabel("employee").property("name", "marko") }, function({g}) { return g.V().valueMap().with_(WithOptions.tokens) }], - g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default: [function({g}) { return g.addV("person").addLabel("employee").property("name", "marko") }, function({g}) { return g.with_("singlelabel").V().valueMap().with_(WithOptions.tokens) }], + g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default: [function({g}) { return g.with_("singlelabel").V().has("name", "lagoon").valueMap("name", "biome").with_(WithOptions.tokens) }], g_withXsinglelabelX_V_valueMapXtrueX_zero_label_vertex: [function({g}) { return g.addV().property("name", "nobody") }, function({g}) { return g.with_("singlelabel").V().valueMap(true) }], g_withXmultilabelX_V_valueMapXtrueX_zero_label_vertex: [function({g}) { return g.addV().property("name", "nobody") }, function({g}) { return g.with_("multilabel").V().valueMap(true) }], g_V_valueMapXtrueX_zero_label_vertex_multi_label_default: [function({g}) { return g.addV().property("name", "nobody") }, function({g}) { return g.V().valueMap(true) }], diff --git a/gremlin-js/gremlin-javascript/test/cucumber/world.js b/gremlin-js/gremlin-javascript/test/cucumber/world.js index 7a52ea5a341..dff02721d49 100644 --- a/gremlin-js/gremlin-javascript/test/cucumber/world.js +++ b/gremlin-js/gremlin-javascript/test/cucumber/world.js @@ -84,7 +84,7 @@ setWorldConstructor(TinkerPopWorld); BeforeAll(function () { // load all traversals - const promises = ['modern', 'classic', 'crew', 'grateful', 'sink', 'empty', 'multilabel'].map(graphName => { + const promises = ['modern', 'classic', 'crew', 'grateful', 'sink', 'zoo', 'empty', 'multilabel'].map(graphName => { let connection = null; if (graphName === 'empty') { connection = getConnection('ggraph'); diff --git a/gremlin-python/src/main/python/tests/feature/terrain.py b/gremlin-python/src/main/python/tests/feature/terrain.py index 55489c7807c..a5035879ea8 100644 --- a/gremlin-python/src/main/python/tests/feature/terrain.py +++ b/gremlin-python/src/main/python/tests/feature/terrain.py @@ -42,7 +42,7 @@ def prepare_static_traversal_source(features, marker): # as the various traversal sources for testing do not change their data, there is no need to re-create remotes # and client side lookup data over and over. it can be created once for all tests and be reused. cache = {} - for graph_name in (("modern", "gmodern"), ("classic", "gclassic"), ("crew", "gcrew"), ("grateful", "ggrateful"), ("sink", "gsink")): + for graph_name in (("modern", "gmodern"), ("classic", "gclassic"), ("crew", "gcrew"), ("grateful", "ggrateful"), ("sink", "gsink"), ("zoo", "gzoo")): cache[graph_name[0]] = {} remote = __create_remote(graph_name[1]) cache[graph_name[0]]["remote_conn"] = __create_remote(graph_name[1]) @@ -62,7 +62,7 @@ def prepare_static_traversal_source(features, marker): scenario.context.lookup_e = {} scenario.context.lookup_vp = {} - for graph_name in ("modern", "classic", "crew", "grateful", "sink"): + for graph_name in ("modern", "classic", "crew", "grateful", "sink", "zoo"): scenario.context.remote_conn[graph_name] = cache[graph_name]["remote_conn"] scenario.context.lookup_v[graph_name] = cache[graph_name]["lookup_v"] scenario.context.lookup_e[graph_name] = cache[graph_name]["lookup_e"] diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TinkerFactoryDataLoader.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TinkerFactoryDataLoader.java index a211d814263..3e01a887f4e 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TinkerFactoryDataLoader.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TinkerFactoryDataLoader.java @@ -31,7 +31,7 @@ *

Configuration parameters (via {@code config} in YAML): *

    *
  • {@code graph} — name of the graph (as defined in {@code graphs:} config) to load data into
  • - *
  • {@code dataset} — which dataset to load: modern, classic, crew, grateful, sink, airroutes
  • + *
  • {@code dataset} — which dataset to load: modern, classic, crew, grateful, sink, airroutes, zoo
  • *
* *

Example YAML usage: @@ -88,6 +88,9 @@ public void onStartUp(final Context c) { case "airroutes": TinkerFactory.generateAirRoutes(tinkerGraph); break; + case "zoo": + TinkerFactory.generateTheZoo(tinkerGraph); + break; default: c.getLogger().warn("TinkerFactoryDataLoader unknown dataset [{}]", dataset); return; diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/RemoteWorld.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/RemoteWorld.java index f1fc8ea9624..50fa4b517a1 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/RemoteWorld.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/RemoteWorld.java @@ -92,6 +92,9 @@ public GraphTraversalSource getGraphTraversalSource(final LoadGraphWith.GraphDat case GRATEFUL: remoteTraversalSource = "ggrateful"; break; + case ZOO: + remoteTraversalSource = "gzoo"; + break; default: throw new UnsupportedOperationException("GraphData not supported: " + graphData.name()); } diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/CheckedGraphManagerTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/CheckedGraphManagerTest.java index 490ead1423f..f440a3bd5a1 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/CheckedGraphManagerTest.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/CheckedGraphManagerTest.java @@ -66,7 +66,7 @@ public void singleGraphFails() { public void justAGraphFails() { settings.graphs.put("invalid", "conf/invalidPath"); final GraphManager manager = new CheckedGraphManager(settings); - assertThat(manager.getGraphNames(), hasSize(8)); + assertThat(manager.getGraphNames(), hasSize(9)); } /** diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/DefaultGraphManagerTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/DefaultGraphManagerTest.java index 87af675779f..b401b12e792 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/DefaultGraphManagerTest.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/DefaultGraphManagerTest.java @@ -48,7 +48,7 @@ public void shouldReturnGraphs() { final Set graphNames = graphManager.getGraphNames(); assertNotNull(graphNames); - assertEquals(8, graphNames.size()); + assertEquals(9, graphNames.size()); assertThat(graphNames.contains("graph"), is(true)); assertThat(graphNames.contains("classic"), is(true)); @@ -56,6 +56,7 @@ public void shouldReturnGraphs() { assertThat(graphNames.contains("crew"), is(true)); assertThat(graphNames.contains("sink"), is(true)); assertThat(graphNames.contains("grateful"), is(true)); + assertThat(graphNames.contains("zoo"), is(true)); assertThat(graphNames.contains("tx"), is(true)); assertThat(graphManager.getGraph("graph"), instanceOf(TinkerGraph.class)); assertThat(graphManager.getGraph("tx"), instanceOf(TinkerTransactionGraph.class)); @@ -68,13 +69,14 @@ public void shouldGetAsBindings() { final Bindings bindings = graphManager.getAsBindings(); assertNotNull(bindings); - assertEquals(8, bindings.size()); + assertEquals(9, bindings.size()); assertThat(bindings.containsKey("graph"), is(true)); assertThat(bindings.containsKey("classic"), is(true)); assertThat(bindings.containsKey("modern"), is(true)); assertThat(bindings.containsKey("crew"), is(true)); assertThat(bindings.containsKey("sink"), is(true)); assertThat(bindings.containsKey("grateful"), is(true)); + assertThat(bindings.containsKey("zoo"), is(true)); assertThat(bindings.containsKey("tx"), is(true)); assertThat(bindings.get("graph"), instanceOf(TinkerGraph.class)); assertThat(bindings.get("tx"), instanceOf(TinkerTransactionGraph.class)); @@ -99,7 +101,7 @@ public void shouldGetDynamicallyAddedGraph() { final Set graphNames = graphManager.getGraphNames(); assertNotNull(graphNames); - assertEquals(9, graphNames.size()); + assertEquals(10, graphNames.size()); assertThat(graphNames.contains("newGraph"), is(true)); assertThat(graphNames.contains("graph"), is(true)); assertThat(graphNames.contains("classic"), is(true)); @@ -107,6 +109,7 @@ public void shouldGetDynamicallyAddedGraph() { assertThat(graphNames.contains("crew"), is(true)); assertThat(graphNames.contains("sink"), is(true)); assertThat(graphNames.contains("grateful"), is(true)); + assertThat(graphNames.contains("zoo"), is(true)); assertThat(graphNames.contains("tx"), is(true)); assertThat(graphManager.getGraph("newGraph"), instanceOf(TinkerGraph.class)); assertThat(graphManager.getGraph("tx"), instanceOf(TinkerTransactionGraph.class)); @@ -120,14 +123,14 @@ public void shouldNotGetRemovedGraph() throws Exception { graphManager.putGraph("newGraph", graph); final Set graphNames = graphManager.getGraphNames(); assertNotNull(graphNames); - assertEquals(9, graphNames.size()); + assertEquals(10, graphNames.size()); assertThat(graphNames.contains("newGraph"), is(true)); assertThat(graphManager.getGraph("newGraph"), instanceOf(TinkerGraph.class)); graphManager.removeGraph("newGraph"); final Set graphNames2 = graphManager.getGraphNames(); - assertEquals(8, graphNames2.size()); + assertEquals(9, graphNames2.size()); assertThat(graphNames2.contains("newGraph"), is(false)); } diff --git a/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml b/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml index 6cc1a710fc1..9adf783b780 100644 --- a/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml +++ b/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml @@ -55,6 +55,9 @@ graphs: { sink: { configuration: conf/tinkergraph-empty.properties, traversalSources: [{name: gsink}]}, + zoo: { + configuration: conf/tinkergraph-multilabel.properties, + traversalSources: [{name: gzoo}]}, tx: { configuration: conf/tinkertransactiongraph-empty.properties, traversalSources: [{name: gtx}]}} @@ -64,6 +67,7 @@ lifecycleHooks: - { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: crew, dataset: crew}} - { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: grateful, dataset: grateful}} - { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: sink, dataset: sink}} + - { className: org.apache.tinkerpop.gremlin.server.util.TinkerFactoryDataLoader, config: {graph: zoo, dataset: zoo}} scriptEngines: { gremlin-lang : {}, groovy-test: {}, diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/LoadGraphWith.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/LoadGraphWith.java index b11de7411bc..5418dff87a8 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/LoadGraphWith.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/LoadGraphWith.java @@ -94,7 +94,13 @@ public enum GraphData { * Loads the air-routes graph, a real-world dataset of ~3,500 airports and ~50,000 routes suitable for * benchmarking and integration testing of traversals that benefit from larger, denser graphs. */ - AIR_ROUTES; + AIR_ROUTES, + + /** + * Loads "The Zoo" TinkerPop4 toy graph which showcases multi-label vertices and a variety of property + * types. Requires a graph configured with {@code LabelCardinality.ZERO_OR_MORE} (or {@code ONE_OR_MORE}). + */ + ZOO; private static final List featuresRequiredByClassic = new ArrayList() {{ add(FeatureRequirement.Factory.create(FEATURE_STRING_VALUES, VertexPropertyFeatures.class)); @@ -132,6 +138,14 @@ public enum GraphData { add(FeatureRequirement.Factory.create(FEATURE_INTEGER_VALUES, EdgePropertyFeatures.class)); }}; + private static final List featuresRequiredByZoo = new ArrayList() {{ + add(FeatureRequirement.Factory.create(FEATURE_STRING_VALUES, VertexPropertyFeatures.class)); + add(FeatureRequirement.Factory.create(FEATURE_INTEGER_VALUES, VertexPropertyFeatures.class)); + add(FeatureRequirement.Factory.create(FEATURE_DOUBLE_VALUES, VertexPropertyFeatures.class)); + add(FeatureRequirement.Factory.create(FEATURE_BOOLEAN_VALUES, VertexPropertyFeatures.class)); + add(FeatureRequirement.Factory.create(FEATURE_MULTI_PROPERTIES, Graph.Features.VertexFeatures.class)); + }}; + public String location() { switch (this) { case CLASSIC: @@ -146,6 +160,8 @@ public String location() { return RESOURCE_PATH_PREFIX + "tinkerpop-sink-v3.kryo"; case AIR_ROUTES: return RESOURCE_PATH_PREFIX + "air-routes-v3.kryo"; + case ZOO: + return RESOURCE_PATH_PREFIX + "tinkerpop-zoo-v3.kryo"; } throw new RuntimeException("No file for this GraphData type"); @@ -165,6 +181,8 @@ public List featuresRequired() { return featuresRequiredBySink; case AIR_ROUTES: return featuresRequiredByAirRoutes; + case ZOO: + return featuresRequiredByZoo; } throw new RuntimeException("No features for this GraphData type"); diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json index bdf0e9fb6c1..830e2e4fd92 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json @@ -25172,28 +25172,16 @@ "scenario": "g_withXmultilabelX_V_elementMap_multilabel", "traversals": [ { - "original": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "language": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "canonical": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "anonymized": "g.addV(string0).addLabel(string1).property(string2, string3)", - "dotnet": "g.AddV((string) \"person\").AddLabel(\"employee\").Property(\"name\", \"marko\")", - "go": "g.AddV(\"person\").AddLabel(\"employee\").Property(\"name\", \"marko\")", - "groovy": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "java": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "javascript": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "python": "g.add_v('person').add_label('employee').property('name', 'marko')" - }, - { - "original": "g.with(\"multilabel\").V().elementMap()", - "language": "g.with(\"multilabel\").V().elementMap()", - "canonical": "g.with(\"multilabel\").V().elementMap()", - "anonymized": "g.with(string0).V().elementMap()", - "dotnet": "g.With(\"multilabel\").V().ElementMap()", - "go": "g.With(\"multilabel\").V().ElementMap()", - "groovy": "g.with(\"multilabel\").V().elementMap()", - "java": "g.with(\"multilabel\").V().elementMap()", - "javascript": "g.with_(\"multilabel\").V().elementMap()", - "python": "g.with_('multilabel').V().element_map()" + "original": "g.with(\"multilabel\").V().has(\"name\", \"tux\").elementMap(\"name\", \"species\")", + "language": "g.with(\"multilabel\").V().has(\"name\", \"tux\").elementMap(\"name\", \"species\")", + "canonical": "g.with(\"multilabel\").V().has(\"name\", \"tux\").elementMap(\"name\", \"species\")", + "anonymized": "g.with(string0).V().has(string1, string2).elementMap(string1, string3)", + "dotnet": "g.With(\"multilabel\").V().Has(\"name\", \"tux\").ElementMap(\"name\", \"species\")", + "go": "g.With(\"multilabel\").V().Has(\"name\", \"tux\").ElementMap(\"name\", \"species\")", + "groovy": "g.with(\"multilabel\").V().has(\"name\", \"tux\").elementMap(\"name\", \"species\")", + "java": "g.with(\"multilabel\").V().has(\"name\", \"tux\").elementMap(\"name\", \"species\")", + "javascript": "g.with_(\"multilabel\").V().has(\"name\", \"tux\").elementMap(\"name\", \"species\")", + "python": "g.with_('multilabel').V().has('name', 'tux').element_map('name', 'species')" } ] }, @@ -25230,28 +25218,16 @@ "scenario": "g_withXsinglelabelX_V_elementMap_multi_label_default", "traversals": [ { - "original": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "language": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "canonical": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "anonymized": "g.addV(string0).addLabel(string1).property(string2, string3)", - "dotnet": "g.AddV((string) \"person\").AddLabel(\"employee\").Property(\"name\", \"marko\")", - "go": "g.AddV(\"person\").AddLabel(\"employee\").Property(\"name\", \"marko\")", - "groovy": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "java": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "javascript": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "python": "g.add_v('person').add_label('employee').property('name', 'marko')" - }, - { - "original": "g.with(\"singlelabel\").V().elementMap()", - "language": "g.with(\"singlelabel\").V().elementMap()", - "canonical": "g.with(\"singlelabel\").V().elementMap()", - "anonymized": "g.with(string0).V().elementMap()", - "dotnet": "g.With(\"singlelabel\").V().ElementMap()", - "go": "g.With(\"singlelabel\").V().ElementMap()", - "groovy": "g.with(\"singlelabel\").V().elementMap()", - "java": "g.with(\"singlelabel\").V().elementMap()", - "javascript": "g.with_(\"singlelabel\").V().elementMap()", - "python": "g.with_('singlelabel').V().element_map()" + "original": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").elementMap(\"name\", \"biome\")", + "language": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").elementMap(\"name\", \"biome\")", + "canonical": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").elementMap(\"name\", \"biome\")", + "anonymized": "g.with(string0).V().has(string1, string2).elementMap(string1, string3)", + "dotnet": "g.With(\"singlelabel\").V().Has(\"name\", \"lagoon\").ElementMap(\"name\", \"biome\")", + "go": "g.With(\"singlelabel\").V().Has(\"name\", \"lagoon\").ElementMap(\"name\", \"biome\")", + "groovy": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").elementMap(\"name\", \"biome\")", + "java": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").elementMap(\"name\", \"biome\")", + "javascript": "g.with_(\"singlelabel\").V().has(\"name\", \"lagoon\").elementMap(\"name\", \"biome\")", + "python": "g.with_('singlelabel').V().has('name', 'lagoon').element_map('name', 'biome')" } ] }, @@ -25404,28 +25380,16 @@ "scenario": "g_withXmultilabelX_E_elementMap", "traversals": [ { - "original": "g.addV(\"person\").property(\"name\", \"marko\").as(\"a\").addV(\"person\").property(\"name\", \"josh\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\").property(\"weight\", 0.5)", - "language": "g.addV(\"person\").property(\"name\", \"marko\").as(\"a\").addV(\"person\").property(\"name\", \"josh\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\").property(\"weight\", 0.5)", - "canonical": "g.addV(\"person\").property(\"name\", \"marko\").as(\"a\").addV(\"person\").property(\"name\", \"josh\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\").property(\"weight\", 0.5)", - "anonymized": "g.addV(string0).property(string1, string2).as(string3).addV(string0).property(string1, string4).as(string5).addE(string6).from(string3).to(string5).property(string7, number0)", - "dotnet": "g.AddV((string) \"person\").Property(\"name\", \"marko\").As(\"a\").AddV((string) \"person\").Property(\"name\", \"josh\").As(\"b\").AddE((string) \"knows\").From(\"a\").To(\"b\").Property(\"weight\", 0.5)", - "go": "g.AddV(\"person\").Property(\"name\", \"marko\").As(\"a\").AddV(\"person\").Property(\"name\", \"josh\").As(\"b\").AddE(\"knows\").From(\"a\").To(\"b\").Property(\"weight\", 0.5)", - "groovy": "g.addV(\"person\").property(\"name\", \"marko\").as(\"a\").addV(\"person\").property(\"name\", \"josh\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\").property(\"weight\", 0.5)", - "java": "g.addV(\"person\").property(\"name\", \"marko\").as(\"a\").addV(\"person\").property(\"name\", \"josh\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\").property(\"weight\", 0.5)", - "javascript": "g.addV(\"person\").property(\"name\", \"marko\").as(\"a\").addV(\"person\").property(\"name\", \"josh\").as(\"b\").addE(\"knows\").from_(\"a\").to(\"b\").property(\"weight\", 0.5)", - "python": "g.add_v('person').property('name', 'marko').as_('a').add_v('person').property('name', 'josh').as_('b').add_e('knows').from_('a').to('b').property('weight', 0.5)" - }, - { - "original": "g.with(\"multilabel\").E().elementMap()", - "language": "g.with(\"multilabel\").E().elementMap()", - "canonical": "g.with(\"multilabel\").E().elementMap()", - "anonymized": "g.with(string0).E().elementMap()", - "dotnet": "g.With(\"multilabel\").E().ElementMap()", - "go": "g.With(\"multilabel\").E().ElementMap()", - "groovy": "g.with(\"multilabel\").E().elementMap()", - "java": "g.with(\"multilabel\").E().elementMap()", - "javascript": "g.with_(\"multilabel\").E().elementMap()", - "python": "g.with_('multilabel').E().element_map()" + "original": "g.with(\"multilabel\").E().has(\"since\", 2018).hasLabel(\"livesIn\").elementMap()", + "language": "g.with(\"multilabel\").E().has(\"since\", 2018).hasLabel(\"livesIn\").elementMap()", + "canonical": "g.with(\"multilabel\").E().has(\"since\", 2018).hasLabel(\"livesIn\").elementMap()", + "anonymized": "g.with(string0).E().has(string1, number0).hasLabel(string2).elementMap()", + "dotnet": "g.With(\"multilabel\").E().Has(\"since\", 2018).HasLabel(\"livesIn\").ElementMap()", + "go": "g.With(\"multilabel\").E().Has(\"since\", 2018).HasLabel(\"livesIn\").ElementMap()", + "groovy": "g.with(\"multilabel\").E().has(\"since\", 2018).hasLabel(\"livesIn\").elementMap()", + "java": "g.with(\"multilabel\").E().has(\"since\", 2018).hasLabel(\"livesIn\").elementMap()", + "javascript": "g.with_(\"multilabel\").E().has(\"since\", 2018).hasLabel(\"livesIn\").elementMap()", + "python": "g.with_('multilabel').E().has('since', 2018).has_label('livesIn').element_map()" } ] }, @@ -26411,28 +26375,16 @@ "scenario": "g_V_label_deprecated_multilabel", "traversals": [ { - "original": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "language": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "canonical": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "anonymized": "g.addV(string0, string1).property(string2, string3)", - "dotnet": "g.AddV((string) \"a\", (string) \"b\").Property(\"name\", \"test\")", - "go": "g.AddV(\"a\", \"b\").Property(\"name\", \"test\")", - "groovy": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "java": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "javascript": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "python": "g.add_v('a', 'b').property('name', 'test')" - }, - { - "original": "g.V().label().count()", - "language": "g.V().label().count()", - "canonical": "g.V().label().count()", - "anonymized": "g.V().label().count()", - "dotnet": "g.V().Label().Count()", - "go": "g.V().Label().Count()", - "groovy": "g.V().label().count()", - "java": "g.V().label().count()", - "javascript": "g.V().label().count()", - "python": "g.V().label().count()" + "original": "g.V().has(\"name\", \"tux\").label().count()", + "language": "g.V().has(\"name\", \"tux\").label().count()", + "canonical": "g.V().has(\"name\", \"tux\").label().count()", + "anonymized": "g.V().has(string0, string1).label().count()", + "dotnet": "g.V().Has(\"name\", \"tux\").Label().Count()", + "go": "g.V().Has(\"name\", \"tux\").Label().Count()", + "groovy": "g.V().has(\"name\", \"tux\").label().count()", + "java": "g.V().has(\"name\", \"tux\").label().count()", + "javascript": "g.V().has(\"name\", \"tux\").label().count()", + "python": "g.V().has('name', 'tux').label().count()" } ] }, @@ -26440,28 +26392,16 @@ "scenario": "g_V_label_deprecated_multilabel_value_is_one_of_labels", "traversals": [ { - "original": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "language": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "canonical": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "anonymized": "g.addV(string0, string1).property(string2, string3)", - "dotnet": "g.AddV((string) \"a\", (string) \"b\").Property(\"name\", \"test\")", - "go": "g.AddV(\"a\", \"b\").Property(\"name\", \"test\")", - "groovy": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "java": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "javascript": "g.addV(\"a\", \"b\").property(\"name\", \"test\")", - "python": "g.add_v('a', 'b').property('name', 'test')" - }, - { - "original": "g.V().filter(__.label().is(P.within(\"a\", \"b\")))", - "language": "g.V().filter(__.label().is(P.within(\"a\", \"b\")))", - "canonical": "g.V().filter(__.label().is(P.within(\"a\", \"b\")))", - "anonymized": "g.V().filter(__.label().is(P.within(string0, string1)))", - "dotnet": "g.V().Filter(__.Label().Is(P.Within(\"a\", \"b\")))", - "go": "g.V().Filter(gremlingo.T__.Label().Is(gremlingo.P.Within(\"a\", \"b\")))", - "groovy": "g.V().filter(__.label().is(P.within(\"a\", \"b\")))", - "java": "g.V().filter(__.label().is(P.within(\"a\", \"b\")))", - "javascript": "g.V().filter(__.label().is(P.within(\"a\", \"b\")))", - "python": "g.V().filter_(__.label().is_(P.within('a', 'b')))" + "original": "g.V().has(\"name\", \"tux\").filter(__.label().is(P.within(\"animal\", \"bird\", \"aquatic\", \"endangered\")))", + "language": "g.V().has(\"name\", \"tux\").filter(__.label().is(P.within(\"animal\", \"bird\", \"aquatic\", \"endangered\")))", + "canonical": "g.V().has(\"name\", \"tux\").filter(__.label().is(P.within(\"animal\", \"bird\", \"aquatic\", \"endangered\")))", + "anonymized": "g.V().has(string0, string1).filter(__.label().is(P.within(string2, string3, string4, string5)))", + "dotnet": "g.V().Has(\"name\", \"tux\").Filter(__.Label().Is(P.Within(\"animal\", \"bird\", \"aquatic\", \"endangered\")))", + "go": "g.V().Has(\"name\", \"tux\").Filter(gremlingo.T__.Label().Is(gremlingo.P.Within(\"animal\", \"bird\", \"aquatic\", \"endangered\")))", + "groovy": "g.V().has(\"name\", \"tux\").filter(__.label().is(P.within(\"animal\", \"bird\", \"aquatic\", \"endangered\")))", + "java": "g.V().has(\"name\", \"tux\").filter(__.label().is(P.within(\"animal\", \"bird\", \"aquatic\", \"endangered\")))", + "javascript": "g.V().has(\"name\", \"tux\").filter(__.label().is(P.within(\"animal\", \"bird\", \"aquatic\", \"endangered\")))", + "python": "g.V().has('name', 'tux').filter_(__.label().is_(P.within('animal', 'bird', 'aquatic', 'endangered')))" } ] }, @@ -26515,28 +26455,16 @@ "scenario": "g_V_labels_multilabel", "traversals": [ { - "original": "g.addV(\"a\", \"b\")", - "language": "g.addV(\"a\", \"b\")", - "canonical": "g.addV(\"a\", \"b\")", - "anonymized": "g.addV(string0, string1)", - "dotnet": "g.AddV((string) \"a\", (string) \"b\")", - "go": "g.AddV(\"a\", \"b\")", - "groovy": "g.addV(\"a\", \"b\")", - "java": "g.addV(\"a\", \"b\")", - "javascript": "g.addV(\"a\", \"b\")", - "python": "g.add_v('a', 'b')" - }, - { - "original": "g.V().labels()", - "language": "g.V().labels()", - "canonical": "g.V().labels()", - "anonymized": "g.V().labels()", - "dotnet": "g.V().Labels()", - "go": "g.V().Labels()", - "groovy": "g.V().labels()", - "java": "g.V().labels()", - "javascript": "g.V().labels()", - "python": "g.V().labels()" + "original": "g.V().has(\"name\", \"tux\").labels()", + "language": "g.V().has(\"name\", \"tux\").labels()", + "canonical": "g.V().has(\"name\", \"tux\").labels()", + "anonymized": "g.V().has(string0, string1).labels()", + "dotnet": "g.V().Has(\"name\", \"tux\").Labels()", + "go": "g.V().Has(\"name\", \"tux\").Labels()", + "groovy": "g.V().has(\"name\", \"tux\").labels()", + "java": "g.V().has(\"name\", \"tux\").labels()", + "javascript": "g.V().has(\"name\", \"tux\").labels()", + "python": "g.V().has('name', 'tux').labels()" } ] }, @@ -40858,28 +40786,16 @@ "scenario": "g_withXmultilabelX_V_valueMap_withXtokensX_multilabel", "traversals": [ { - "original": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "language": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "canonical": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "anonymized": "g.addV(string0).addLabel(string1).property(string2, string3)", - "dotnet": "g.AddV((string) \"person\").AddLabel(\"employee\").Property(\"name\", \"marko\")", - "go": "g.AddV(\"person\").AddLabel(\"employee\").Property(\"name\", \"marko\")", - "groovy": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "java": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "javascript": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "python": "g.add_v('person').add_label('employee').property('name', 'marko')" - }, - { - "original": "g.with(\"multilabel\").V().valueMap().with(WithOptions.tokens)", - "language": "g.with(\"multilabel\").V().valueMap().with(WithOptions.tokens)", - "canonical": "g.with(\"multilabel\").V().valueMap().with(WithOptions.tokens)", - "anonymized": "g.with(string0).V().valueMap().with(WithOptions.tokens)", - "dotnet": "g.With(\"multilabel\").V().ValueMap().With(WithOptions.Tokens)", - "go": "g.With(\"multilabel\").V().ValueMap().With(gremlingo.WithOptions.Tokens)", - "groovy": "g.with(\"multilabel\").V().valueMap().with(WithOptions.tokens)", - "java": "g.with(\"multilabel\").V().valueMap().with(WithOptions.tokens)", - "javascript": "g.with_(\"multilabel\").V().valueMap().with_(WithOptions.tokens)", - "python": "g.with_('multilabel').V().value_map().with_(WithOptions.tokens)" + "original": "g.with(\"multilabel\").V().has(\"name\", \"tux\").valueMap(\"name\", \"species\").with(WithOptions.tokens)", + "language": "g.with(\"multilabel\").V().has(\"name\", \"tux\").valueMap(\"name\", \"species\").with(WithOptions.tokens)", + "canonical": "g.with(\"multilabel\").V().has(\"name\", \"tux\").valueMap(\"name\", \"species\").with(WithOptions.tokens)", + "anonymized": "g.with(string0).V().has(string1, string2).valueMap(string1, string3).with(WithOptions.tokens)", + "dotnet": "g.With(\"multilabel\").V().Has(\"name\", \"tux\").ValueMap(\"name\", \"species\").With(WithOptions.Tokens)", + "go": "g.With(\"multilabel\").V().Has(\"name\", \"tux\").ValueMap(\"name\", \"species\").With(gremlingo.WithOptions.Tokens)", + "groovy": "g.with(\"multilabel\").V().has(\"name\", \"tux\").valueMap(\"name\", \"species\").with(WithOptions.tokens)", + "java": "g.with(\"multilabel\").V().has(\"name\", \"tux\").valueMap(\"name\", \"species\").with(WithOptions.tokens)", + "javascript": "g.with_(\"multilabel\").V().has(\"name\", \"tux\").valueMap(\"name\", \"species\").with_(WithOptions.tokens)", + "python": "g.with_('multilabel').V().has('name', 'tux').value_map('name', 'species').with_(WithOptions.tokens)" } ] }, @@ -40916,28 +40832,16 @@ "scenario": "g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default", "traversals": [ { - "original": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "language": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "canonical": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "anonymized": "g.addV(string0).addLabel(string1).property(string2, string3)", - "dotnet": "g.AddV((string) \"person\").AddLabel(\"employee\").Property(\"name\", \"marko\")", - "go": "g.AddV(\"person\").AddLabel(\"employee\").Property(\"name\", \"marko\")", - "groovy": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "java": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "javascript": "g.addV(\"person\").addLabel(\"employee\").property(\"name\", \"marko\")", - "python": "g.add_v('person').add_label('employee').property('name', 'marko')" - }, - { - "original": "g.with(\"singlelabel\").V().valueMap().with(WithOptions.tokens)", - "language": "g.with(\"singlelabel\").V().valueMap().with(WithOptions.tokens)", - "canonical": "g.with(\"singlelabel\").V().valueMap().with(WithOptions.tokens)", - "anonymized": "g.with(string0).V().valueMap().with(WithOptions.tokens)", - "dotnet": "g.With(\"singlelabel\").V().ValueMap().With(WithOptions.Tokens)", - "go": "g.With(\"singlelabel\").V().ValueMap().With(gremlingo.WithOptions.Tokens)", - "groovy": "g.with(\"singlelabel\").V().valueMap().with(WithOptions.tokens)", - "java": "g.with(\"singlelabel\").V().valueMap().with(WithOptions.tokens)", - "javascript": "g.with_(\"singlelabel\").V().valueMap().with_(WithOptions.tokens)", - "python": "g.with_('singlelabel').V().value_map().with_(WithOptions.tokens)" + "original": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").valueMap(\"name\", \"biome\").with(WithOptions.tokens)", + "language": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").valueMap(\"name\", \"biome\").with(WithOptions.tokens)", + "canonical": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").valueMap(\"name\", \"biome\").with(WithOptions.tokens)", + "anonymized": "g.with(string0).V().has(string1, string2).valueMap(string1, string3).with(WithOptions.tokens)", + "dotnet": "g.With(\"singlelabel\").V().Has(\"name\", \"lagoon\").ValueMap(\"name\", \"biome\").With(WithOptions.Tokens)", + "go": "g.With(\"singlelabel\").V().Has(\"name\", \"lagoon\").ValueMap(\"name\", \"biome\").With(gremlingo.WithOptions.Tokens)", + "groovy": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").valueMap(\"name\", \"biome\").with(WithOptions.tokens)", + "java": "g.with(\"singlelabel\").V().has(\"name\", \"lagoon\").valueMap(\"name\", \"biome\").with(WithOptions.tokens)", + "javascript": "g.with_(\"singlelabel\").V().has(\"name\", \"lagoon\").valueMap(\"name\", \"biome\").with_(WithOptions.tokens)", + "python": "g.with_('singlelabel').V().has('name', 'lagoon').value_map('name', 'biome').with_(WithOptions.tokens)" } ] }, diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ElementMap.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ElementMap.feature index b4a827271af..ea7e30297b0 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ElementMap.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ElementMap.feature @@ -144,19 +144,15 @@ Feature: Step - elementMap() @MultiLabel Scenario: g_withXmultilabelX_V_elementMap_multilabel - Given the empty graph - And the graph initializer of - """ - g.addV("person").addLabel("employee").property("name", "marko") - """ + Given the zoo graph And the traversal of """ - g.with("multilabel").V().elementMap() + g.with("multilabel").V().has("name", "tux").elementMap("name", "species") """ When iterated to list Then the result should be unordered | result | - | m[{"t[id]": "v[marko].id", "t[label]": "s[person,employee]", "name": "marko"}] | + | m[{"t[id]": "v[tux].id", "t[label]": "s[animal,bird,aquatic,endangered]", "name": "tux", "species": "african penguin"}] | @MultiLabel @MultiLabelDefault Scenario: g_V_elementMap_multi_label_default @@ -176,21 +172,17 @@ Feature: Step - elementMap() @MultiLabel @MultiLabelDefault Scenario: g_withXsinglelabelX_V_elementMap_multi_label_default - Given the empty graph - And the graph initializer of - """ - g.addV("person").addLabel("employee").property("name", "marko") - """ + Given the zoo graph And the traversal of """ - g.with("singlelabel").V().elementMap() + g.with("singlelabel").V().has("name", "lagoon").elementMap("name", "biome") """ When iterated to list Then the result should have a count of 1 And the result should be of | result | - | m[{"t[id]": "v[marko].id", "t[label]": "person", "name": "marko"}] | - | m[{"t[id]": "v[marko].id", "t[label]": "employee", "name": "marko"}] | + | m[{"t[id]": "v[lagoon].id", "t[label]": "habitat", "name": "lagoon", "biome": "marine"}] | + | m[{"t[id]": "v[lagoon].id", "t[label]": "aquatic", "name": "lagoon", "biome": "marine"}] | @MultiLabel @MultiLabelDefault Scenario: g_V_elementMap_single_label_vertex_multi_label_default @@ -272,21 +264,17 @@ Feature: Step - elementMap() | result | | m[{"t[id]": "v[nobody].id", "name": "nobody"}] | - @MultiLabel + @GraphComputerVerificationReferenceOnly @MultiLabel Scenario: g_withXmultilabelX_E_elementMap - Given the empty graph - And the graph initializer of - """ - g.addV("person").property("name", "marko").as("a").addV("person").property("name", "josh").as("b").addE("knows").from("a").to("b").property("weight", 0.5) - """ + Given the zoo graph And the traversal of """ - g.with("multilabel").E().elementMap() + g.with("multilabel").E().has("since", 2018).hasLabel("livesIn").elementMap() """ When iterated to list Then the result should be unordered | result | - | m[{"t[id]": "e[marko-knows->josh].id", "t[label]": "s[knows]", "weight": "d[0.5].d", "D[OUT]": "m[{\\"t[id]\\": \\"v[marko].id\\", \\"t[label]\\": \\"s[person]\\"}]", "D[IN]": "m[{\\"t[id]\\": \\"v[josh].id\\", \\"t[label]\\": \\"s[person]\\"}]"}] | + | m[{"t[id]": "e[atlas-livesIn->lagoon].id", "t[label]": "s[livesIn]", "since": 2018, "D[OUT]": "m[{\\"t[id]\\": \\"v[atlas].id\\", \\"t[label]\\": \\"s[animal,reptile,aquatic,endangered]\\"}]", "D[IN]": "m[{\\"t[id]\\": \\"v[lagoon].id\\", \\"t[label]\\": \\"s[habitat,aquatic]\\"}]"}] | @GraphComputerVerificationReferenceOnly @MultiLabel @MultiLabelDefault Scenario: g_E_elementMap_multi_label_default diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Label.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Label.feature index 276c967fcb8..c93d7ebe44d 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Label.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Label.feature @@ -34,14 +34,10 @@ Feature: Step - label() @MultiLabel Scenario: g_V_label_deprecated_multilabel - Given the empty graph - And the graph initializer of - """ - g.addV("a", "b").property("name", "test") - """ + Given the zoo graph And the traversal of """ - g.V().label().count() + g.V().has("name", "tux").label().count() """ When iterated to list Then the result should be unordered @@ -50,14 +46,10 @@ Feature: Step - label() @MultiLabel Scenario: g_V_label_deprecated_multilabel_value_is_one_of_labels - Given the empty graph - And the graph initializer of - """ - g.addV("a", "b").property("name", "test") - """ + Given the zoo graph And the traversal of """ - g.V().filter(__.label().is(P.within("a", "b"))) + g.V().has("name", "tux").filter(__.label().is(P.within("animal", "bird", "aquatic", "endangered"))) """ When iterated to list Then the result should have a count of 1 diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Labels.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Labels.feature index 60869bdbcad..26fb1db50da 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Labels.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Labels.feature @@ -35,20 +35,18 @@ Feature: Step - labels() @MultiLabel Scenario: g_V_labels_multilabel - Given the empty graph - And the graph initializer of - """ - g.addV("a", "b") - """ + Given the zoo graph And the traversal of """ - g.V().labels() + g.V().has("name", "tux").labels() """ When iterated to list Then the result should be unordered | result | - | a | - | b | + | animal | + | bird | + | aquatic | + | endangered | @MultiLabel Scenario: g_addVXa_bX_labels_count @@ -133,4 +131,4 @@ Feature: Step - labels() Then the result should be unordered | result | | knows | - | knows | \ No newline at end of file + | knows | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ValueMap.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ValueMap.feature index c339aae4e9f..791d6333e8c 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ValueMap.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/ValueMap.feature @@ -286,19 +286,15 @@ Feature: Step - valueMap() @MultiLabel Scenario: g_withXmultilabelX_V_valueMap_withXtokensX_multilabel - Given the empty graph - And the graph initializer of - """ - g.addV("person").addLabel("employee").property("name", "marko") - """ + Given the zoo graph And the traversal of """ - g.with("multilabel").V().valueMap().with(WithOptions.tokens) + g.with("multilabel").V().has("name", "tux").valueMap("name", "species").with(WithOptions.tokens) """ When iterated to list Then the result should be unordered | result | - | m[{"t[id]": "v[marko].id", "t[label]": "s[person,employee]", "name": ["marko"]}] | + | m[{"t[id]": "v[tux].id", "t[label]": "s[animal,bird,aquatic,endangered]", "name": ["tux"], "species": ["african penguin"]}] | @MultiLabel @MultiLabelDefault Scenario: g_V_valueMap_withXtokensX_multi_label_default @@ -318,21 +314,17 @@ Feature: Step - valueMap() @MultiLabel @MultiLabelDefault Scenario: g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default - Given the empty graph - And the graph initializer of - """ - g.addV("person").addLabel("employee").property("name", "marko") - """ + Given the zoo graph And the traversal of """ - g.with("singlelabel").V().valueMap().with(WithOptions.tokens) + g.with("singlelabel").V().has("name", "lagoon").valueMap("name", "biome").with(WithOptions.tokens) """ When iterated to list Then the result should have a count of 1 And the result should be of | result | - | m[{"t[id]": "v[marko].id", "t[label]": "person", "name": ["marko"]}] | - | m[{"t[id]": "v[marko].id", "t[label]": "employee", "name": ["marko"]}] | + | m[{"t[id]": "v[lagoon].id", "t[label]": "habitat", "name": ["lagoon"], "biome": ["marine"]}] | + | m[{"t[id]": "v[lagoon].id", "t[label]": "aquatic", "name": ["lagoon"], "biome": ["marine"]}] | @MultiLabel Scenario: g_withXsinglelabelX_V_valueMapXtrueX_zero_label_vertex diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java index d28caa3c580..4377303292f 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java @@ -96,6 +96,7 @@ public static class TinkerGraphWorld extends TinkerWorld { private static final AbstractTinkerGraph crew = registerTestServices(TinkerFactory.createTheCrew()); private static final AbstractTinkerGraph sink = registerTestServices(TinkerFactory.createKitchenSink()); private static final AbstractTinkerGraph grateful = registerTestServices(TinkerFactory.createGratefulDead()); + private static final AbstractTinkerGraph zoo = registerTestServices(TinkerFactory.createTheZoo()); @Override public GraphTraversalSource getGraphTraversalSource(final LoadGraphWith.GraphData graphData) { @@ -111,6 +112,8 @@ else if (graphData == LoadGraphWith.GraphData.SINK) return sink.traversal(); else if (graphData == LoadGraphWith.GraphData.GRATEFUL) return grateful.traversal(); + else if (graphData == LoadGraphWith.GraphData.ZOO) + return zoo.traversal(); else throw new UnsupportedOperationException("GraphData not supported: " + graphData.name()); } @@ -163,6 +166,7 @@ public static class TinkerTransactionGraphWorld extends TinkerWorld { private static final AbstractTinkerGraph crew; private static final AbstractTinkerGraph sink; private static final AbstractTinkerGraph grateful; + private static final AbstractTinkerGraph zoo; static { modern = TinkerTransactionGraph.open(getNumberIdManagerConfiguration()); @@ -189,6 +193,11 @@ public static class TinkerTransactionGraphWorld extends TinkerWorld { TinkerFactory.generateGratefulDead(grateful); grateful.tx().commit(); registerTestServices(grateful); + + zoo = TinkerTransactionGraph.open(getMultiLabelConfiguration()); + TinkerFactory.generateTheZoo(zoo); + zoo.tx().commit(); + registerTestServices(zoo); } @Override @@ -205,6 +214,8 @@ else if (graphData == LoadGraphWith.GraphData.SINK) return sink.traversal(); else if (graphData == LoadGraphWith.GraphData.GRATEFUL) return grateful.traversal(); + else if (graphData == LoadGraphWith.GraphData.ZOO) + return zoo.traversal(); else throw new UnsupportedOperationException("GraphData not supported: " + graphData.name()); } @@ -232,6 +243,7 @@ public static class TinkerShuffleGraphWorld extends TinkerWorld { private static final TinkerGraph crew; private static final TinkerGraph sink; private static final TinkerGraph grateful; + private static final TinkerGraph zoo; static { modern = TinkerShuffleGraph.open(); @@ -249,6 +261,9 @@ public static class TinkerShuffleGraphWorld extends TinkerWorld { grateful = TinkerShuffleGraph.open(); TinkerFactory.generateGratefulDead(grateful); registerTestServices(grateful); + zoo = TinkerShuffleGraph.open(getMultiLabelConfiguration()); + TinkerFactory.generateTheZoo(zoo); + registerTestServices(zoo); } @Override @@ -265,6 +280,8 @@ else if (graphData == LoadGraphWith.GraphData.SINK) return sink.traversal(); else if (graphData == LoadGraphWith.GraphData.GRATEFUL) return grateful.traversal(); + else if (graphData == LoadGraphWith.GraphData.ZOO) + return zoo.traversal(); else throw new UnsupportedOperationException("GraphData not supported: " + graphData.name()); } From 9760260d2034d7a23ecfdd36b88011c07ca72afa Mon Sep 17 00:00:00 2001 From: Cole Greer Date: Tue, 7 Jul 2026 19:44:15 -0700 Subject: [PATCH 03/10] Convert read-only multi-label doc examples to use the zoo graph Converts 7 of the 14 executable gremlin-groovy examples added for multi-label support in the-traversal.asciidoc to use [gremlin-groovy,theZoo] instead of hand-building a single-vertex TinkerGraph inline: - By Step: group().by(label) vs by(labels().fold()) - Choose Step: choose(T.label) non-determinism - ElementMap: with("multilabel") example - Has Step: chained hasLabel() AND/OR semantics - Label Step - Labels Step - ValueMap: with("multilabel") example Each converted example is read-only after the block's own setup, so there is no risk of mutation leaking across examples: the docs preprocessor opens a fresh TinkerFactory.createTheZoo() graph for every [gremlin-groovy,theZoo] block. Kept the AddLabel, DropLabel, and mergeV examples on hand-built empty graphs since they depend on precise, narrative-driven mutation sequences or a LabelCardinality.ONE case the zoo graph can't represent. Assisted-by: Kiro:claude-sonnet-5 --- docs/src/reference/the-traversal.asciidoc | 104 +++++++--------------- 1 file changed, 34 insertions(+), 70 deletions(-) diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index d0c4cb849cb..669ef4ccaa0 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -1133,23 +1133,17 @@ multi-label vertex returns only one label chosen non-deterministically. Steps li `dedup()`, `order()`, `path()`, `simplePath()`, and `tree()` that modulate `by(label)` will therefore behave unpredictably on multi-label vertices. -[gremlin-groovy] +[gremlin-groovy,theZoo] ---- -conf = new BaseConfiguration() -conf.setProperty("gremlin.tinkergraph.vertexLabelCardinality", "ZERO_OR_MORE") -graph = TinkerGraph.open(conf) -g = traversal().with(graph) -g.addV('person','employee').property('name','marko') -g.addV('person').property('name','vadas') g.V().group().by(label).by(values('name').fold()) <1> g.V().group().by(labels().fold()).by(values('name').fold()) <2> ---- -<1> marko has both the "person" and "employee" labels, but is grouped under only one of them, chosen -non-deterministically, so this grouping is not reliable for multi-label vertices. -<2> Grouping by the full set of labels instead keeps marko under `[person, employee]`, distinct from vadas under -`[person]`, regardless of which order the labels were assigned in. Prefer this pattern with the -<> step over `by(label)` for graphs that support multiple labels per vertex. +<1> `atlas` carries the "animal", "reptile", "aquatic", and "endangered" labels, but is grouped under only one of +them, chosen non-deterministically, so this grouping is not reliable for multi-label vertices. +<2> Grouping by the full set of labels instead keeps each animal under its own distinct combination of labels. +Prefer this pattern with the <> step over `by(label)` for graphs that support multiple +labels per vertex. *Additional References* @@ -1344,22 +1338,17 @@ On the "modern" graph, that works reliably because each vertex has exactly one l tells a different story, since `choose(T.label)` still selects a branch based on a single label chosen non-deterministically from the full set: -[gremlin-groovy] +[gremlin-groovy,theZoo] ---- -conf = new BaseConfiguration() -conf.setProperty("gremlin.tinkergraph.vertexLabelCardinality", "ZERO_OR_MORE") -graph = TinkerGraph.open(conf) -g = traversal().with(graph) -g.addV('person','employee').property('name','marko') -g.V().has('name','marko').choose(T.label). - option('person', constant('routed as a person')). - option('employee', constant('routed as an employee')) <1> +g.V().has('name','tux').choose(T.label). + option('bird', constant('routed as a bird')). + option('aquatic', constant('routed as aquatic')) <1> ---- -<1> marko carries both the "person" and "employee" labels, so which `option()` branch is taken depends on which -label the graph happens to return from `label()` and is not guaranteed to be consistent. Filtering with -<> beforehand, or restructuring the choice around `labels()`, gives predictable results for -multi-label vertices. +<1> `tux` carries the "animal", "bird", "aquatic", and "endangered" labels, so which `option()` branch is taken +depends on which label the graph happens to return from `label()` and is not guaranteed to be consistent. Filtering +with <> beforehand, or restructuring the choice around `labels()`, gives predictable results +for multi-label vertices. The `Pick` enum was introduced in an example earlier to handle non-matching scenarios. The following `Pick` options may be used with `choose()`: @@ -1981,18 +1970,13 @@ as the `id` is the only available data to the star graph. For multi-label graphs, `elementMap()` returns labels as a single string by default. To receive all labels as a set, use `with("multilabel")` either per-traversal or on a persistent source: -[gremlin-groovy] +[gremlin-groovy,theZoo] ---- -conf = new BaseConfiguration() -conf.setProperty("gremlin.tinkergraph.vertexLabelCardinality", "ZERO_OR_MORE") -graph = TinkerGraph.open(conf) -g = traversal().with(graph) -g.addV('person').property('name','marko').addLabel('employee').addLabel('manager').iterate() -g.V().has('name','marko').elementMap() <1> -g.with("multilabel").V().has('name','marko').elementMap() <2> +g.V().has('name','tux').elementMap() <1> +g.with("multilabel").V().has('name','tux').elementMap() <2> gml = g.with("multilabel") -gml.V().has('name','marko').elementMap() <3> -gml.V().has('name','marko').valueMap(true) <4> +gml.V().has('name','tux').elementMap() <3> +gml.V().has('name','tux').valueMap(true) <4> ---- <1> Without `"multilabel"`, the label entry is a single string. @@ -2366,20 +2350,15 @@ is evaluated per-traverser and its first result becomes the comparison value. Chained `hasLabel()` calls requires that a vertex carry all of the specified labels, which matters in cases where a vertex can have more than one label: -[gremlin-groovy] +[gremlin-groovy,theZoo] ---- -conf = new BaseConfiguration() -conf.setProperty("gremlin.tinkergraph.vertexLabelCardinality", "ZERO_OR_MORE") -graph = TinkerGraph.open(conf) -g = traversal().with(graph) -g.addV('person','employee').property('name','marko') -g.addV('person').property('name','vadas') -g.V().hasLabel('person','employee').values('name') <1> -g.V().hasLabel('person').hasLabel('employee').values('name') <2> +g.V().hasLabel('bird','reptile').values('name') <1> +g.V().hasLabel('bird').hasLabel('aquatic').values('name') <2> ---- -<1> OR semantics: matches both marko and vadas, since each has at least one of "person" or "employee". -<2> Chaining `hasLabel()` calls requires both labels to be present, matching only marko. +<1> OR semantics: matches `tux`, `atlas`, and `monty`, since each has at least one of "bird" or "reptile". +<2> Chaining `hasLabel()` calls requires both labels to be present, matching only `tux`, which has both "bird" and +"aquatic". NOTE: When a `Traversal` is provided directly as the value argument (not inside a `P`), it is internally wrapped in `P.eq(traversal)`. Child traversals must be read-only. Mutating steps like `addV()` or `property()` are rejected with @@ -2784,19 +2763,14 @@ g.V(1).properties().label() For elements that carry exactly one label, as in the "modern" graph above, `label()` behaves as expected. A multi-label vertex complicates matters, since `label()` still returns only a single `String`: -[gremlin-groovy] +[gremlin-groovy,theZoo] ---- -conf = new BaseConfiguration() -conf.setProperty("gremlin.tinkergraph.vertexLabelCardinality", "ZERO_OR_MORE") -graph = TinkerGraph.open(conf) -g = traversal().with(graph) -g.addV('person','employee').property('name','marko') -g.V().has('name','marko').label() <1> -g.V().has('name','marko').labels() <2> +g.V().has('name','tux').label() <1> +g.V().has('name','tux').labels() <2> ---- -<1> marko carries both the "person" and "employee" labels, but `label()` returns only one of them, chosen -non-deterministically. +<1> `tux` carries the "animal", "bird", "aquatic", and "endangered" labels, but `label()` returns only one of them, +chosen non-deterministically. <2> Use the <> step instead to reliably retrieve every label a vertex carries. *Additional References* @@ -2809,13 +2783,8 @@ link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre The `labels()`-step (*flatMap*) takes an `Element` and emits each of its labels as a separate traverser. For elements with a single label this behaves identically to `label()`. For multi-label elements, it emits one traverser per label. -[gremlin-groovy] +[gremlin-groovy,theZoo] ---- -conf = new BaseConfiguration() -conf.setProperty("gremlin.tinkergraph.vertexLabelCardinality", "ZERO_OR_MORE") -graph = TinkerGraph.open(conf) -g = traversal().with(graph) -g.addV('person').property('name','marko').addLabel('employee').iterate() g.V().labels() g.V().labels().count() ---- @@ -5747,15 +5716,10 @@ g.V().hasLabel('person').properties('location').valueMap().with(WithOptions.toke For multi-label graphs, the label value in the map is a single string by default. Use `with("multilabel")` to receive all labels as a set: -[gremlin-groovy] +[gremlin-groovy,theZoo] ---- -conf = new BaseConfiguration() -conf.setProperty("gremlin.tinkergraph.vertexLabelCardinality", "ZERO_OR_MORE") -graph = TinkerGraph.open(conf) -g = traversal().with(graph) -g.addV('person').property('name','marko').addLabel('employee').iterate() -g.V().has('name','marko').valueMap(true) <1> -g.with("multilabel").V().has('name','marko').valueMap(true) <2> +g.V().has('name','tux').valueMap(true) <1> +g.with("multilabel").V().has('name','tux').valueMap(true) <2> ---- <1> Without `"multilabel"`, the label entry is a single string. From a5dddaf8a3aba4e9999e5ffbf39ce500aaf5f79d Mon Sep 17 00:00:00 2001 From: Cole Greer Date: Wed, 8 Jul 2026 10:01:47 -0700 Subject: [PATCH 04/10] Fix order-sensitive set comparison in .NET Gherkin DeepEqual DeepEqualityExtensions.DeepEqual compared IEnumerable values with an order-sensitive SequenceEqual, including HashSet values nested inside a result Dictionary (e.g. the t[label] entry of an elementMap()/valueMap() result under with("multilabel")). Single-element label sets never surfaced this, but the zoo graph's multi-label vertices (e.g. tux with 4 labels) exposed it: the expected HashSet built from the s[...] Gherkin token and the actual HashSet deserialized by GraphBinary4's SetSerializer can differ in iteration order despite containing identical elements, causing SequenceEqual to spuriously fail. Adds an ISet-aware branch that uses SetEquals before falling back to SequenceEqual for ordered collections. Fixes 3 failing scenarios under the GraphBinary4MessageSerializer variant of RunGherkinBasedTests: g_withXmultilabelX_V_elementMap_multilabel, g_withXmultilabelX_V_valueMap_withXtokensX_multilabel, and g_withXmultilabelX_E_elementMap. Also includes the regenerated Gremlin.cs reflecting the zoo-graph feature file conversions already committed to this branch. Assisted-by: Kiro:claude-sonnet-5 --- .../Gherkin/DeepEqualityExtensions.cs | 7 +++++++ .../Gherkin/Gremlin.cs | 16 ++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/DeepEqualityExtensions.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/DeepEqualityExtensions.cs index af7114c7564..93a7e6570ef 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/DeepEqualityExtensions.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/DeepEqualityExtensions.cs @@ -76,6 +76,13 @@ private static bool DeepEqual(this IEnumerable first, IEnumerable second) { return second is Dictionary dict2 && dict1.DeepEqual(dict2); } + if (first is ISet set1) + { + // Sets are unordered by definition, so comparing them with a SequenceEqual below is not reliable - + // different insertion histories/capacities can yield different enumeration orders for otherwise + // identical sets (e.g. a multi-label vertex's set of labels). + return second is ISet set2 && set1.SetEquals(set2); + } var objectEnum1 = first.ToObjectEnumerable(); var objectEnum2 = second.ToObjectEnumerable(); diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index e7494ebe331..7e1091cbd0e 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -1311,15 +1311,15 @@ private static IDictionary, ITraversal>> {(g,p) =>g.With("singlelabel").V().ElementMap("name", "age", null)}}, {"g_withXmultilabelX_VXmarkoX_elementMap", new List, ITraversal>> {(g,p) =>g.With("multilabel").V().Has("name", "marko").ElementMap()}}, {"g_V_elementMap_single_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.V().ElementMap()}}, - {"g_withXmultilabelX_V_elementMap_multilabel", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.With("multilabel").V().ElementMap()}}, + {"g_withXmultilabelX_V_elementMap_multilabel", new List, ITraversal>> {(g,p) =>g.With("multilabel").V().Has("name", "tux").ElementMap("name", "species")}}, {"g_V_elementMap_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.V().ElementMap()}}, - {"g_withXsinglelabelX_V_elementMap_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.With("singlelabel").V().ElementMap()}}, + {"g_withXsinglelabelX_V_elementMap_multi_label_default", new List, ITraversal>> {(g,p) =>g.With("singlelabel").V().Has("name", "lagoon").ElementMap("name", "biome")}}, {"g_V_elementMap_single_label_vertex_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko"), (g,p) =>g.V().ElementMap()}}, {"g_withXsinglelabelX_V_elementMap_zero_label_vertex", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.With("singlelabel").V().ElementMap()}}, {"g_withXmultilabelX_V_elementMap_zero_label_vertex", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.With("multilabel").V().ElementMap()}}, {"g_V_elementMap_zero_label_vertex_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.V().ElementMap()}}, {"g_V_elementMap_zero_label_vertex_single_label_default", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.V().ElementMap()}}, - {"g_withXmultilabelX_E_elementMap", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b").Property("weight", 0.5), (g,p) =>g.With("multilabel").E().ElementMap()}}, + {"g_withXmultilabelX_E_elementMap", new List, ITraversal>> {(g,p) =>g.With("multilabel").E().Has("since", 2018).HasLabel("livesIn").ElementMap()}}, {"g_E_elementMap_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b").Property("weight", 0.5), (g,p) =>g.E().ElementMap()}}, {"g_E_elementMap_single_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b").Property("weight", 0.5), (g,p) =>g.E().ElementMap()}}, {"g_withXsinglelabelX_E_elementMap", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b").Property("weight", 0.5), (g,p) =>g.With("singlelabel").E().ElementMap()}}, @@ -1374,11 +1374,11 @@ private static IDictionary, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", " marko ").Property("age", 29).As("marko").AddV((string) "person").Property("name", " vadas ").Property("age", 27).As("vadas").AddV((string) "software").Property("name", " lop").Property("lang", "java").As("lop").AddV((string) "person").Property("name", "josh ").Property("age", 32).As("josh").AddV((string) "software").Property("name", " ripple ").Property("lang", "java").As("ripple").AddV((string) "person").Property("name", "peter").Property("age", 35).As("peter").AddE((string) "knows").From("marko").To("vadas").Property("weight", 0.5d).AddE((string) "knows").From("marko").To("josh").Property("weight", 1.0d).AddE((string) "created").From("marko").To("lop").Property("weight", 0.4d).AddE((string) "created").From("josh").To("ripple").Property("weight", 1.0d).AddE((string) "created").From("josh").To("lop").Property("weight", 0.4d).AddE((string) "created").From("peter").To("lop").Property("weight", 0.2d), (g,p) =>g.V().Values("name").LTrim()}}, {"g_V_valuesXnameX_order_fold_lTrimXlocalX", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", " marko ").Property("age", 29).As("marko").AddV((string) "person").Property("name", " vadas ").Property("age", 27).As("vadas").AddV((string) "software").Property("name", " lop").Property("lang", "java").As("lop").AddV((string) "person").Property("name", "josh ").Property("age", 32).As("josh").AddV((string) "software").Property("name", " ripple ").Property("lang", "java").As("ripple").AddV((string) "person").Property("name", "peter").Property("age", 35).As("peter").AddE((string) "knows").From("marko").To("vadas").Property("weight", 0.5d).AddE((string) "knows").From("marko").To("josh").Property("weight", 1.0d).AddE((string) "created").From("marko").To("lop").Property("weight", 0.4d).AddE((string) "created").From("josh").To("ripple").Property("weight", 1.0d).AddE((string) "created").From("josh").To("lop").Property("weight", 0.4d).AddE((string) "created").From("peter").To("lop").Property("weight", 0.2d), (g,p) =>g.V().Values("name").Order().Fold().LTrim(Scope.Local)}}, {"g_V_label_single_label_graph", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Label()}}, - {"g_V_label_deprecated_multilabel", new List, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b").Property("name", "test"), (g,p) =>g.V().Label().Count()}}, - {"g_V_label_deprecated_multilabel_value_is_one_of_labels", new List, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b").Property("name", "test"), (g,p) =>g.V().Filter(__.Label().Is(P.Within("a", "b")))}}, + {"g_V_label_deprecated_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Label().Count()}}, + {"g_V_label_deprecated_multilabel_value_is_one_of_labels", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Filter(__.Label().Is(P.Within("animal", "bird", "aquatic", "endangered")))}}, {"g_E_label_deprecated_multilabel_graph", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").As("a").AddV((string) "person").As("b").AddE((string) "knows").From("a").To("b"), (g,p) =>g.E().Label()}}, {"g_V_hasLabelXpersonX_labels", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Labels()}}, - {"g_V_labels_multilabel", new List, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b"), (g,p) =>g.V().Labels()}}, + {"g_V_labels_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Labels()}}, {"g_addVXa_bX_labels_count", new List, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b"), (g,p) =>g.V().Labels().Count()}}, {"g_addV_labels", new List, ITraversal>> {(g,p) =>g.AddV(), (g,p) =>g.V().Labels()}}, {"g_E_labels", new List, ITraversal>> {(g,p) =>g.E().HasLabel("knows").Labels()}}, @@ -1969,9 +1969,9 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().ValueMap("name", "age").By(__.Is("x")).By(__.Unfold())}}, {"g_withXmultilabelX_VXmarkoX_valueMap_withXtokensX", new List, ITraversal>> {(g,p) =>g.With("multilabel").V().Has("name", "marko").ValueMap().With(WithOptions.Tokens)}}, {"g_V_valueMap_withXtokensX_single_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.V().ValueMap().With(WithOptions.Tokens)}}, - {"g_withXmultilabelX_V_valueMap_withXtokensX_multilabel", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.With("multilabel").V().ValueMap().With(WithOptions.Tokens)}}, + {"g_withXmultilabelX_V_valueMap_withXtokensX_multilabel", new List, ITraversal>> {(g,p) =>g.With("multilabel").V().Has("name", "tux").ValueMap("name", "species").With(WithOptions.Tokens)}}, {"g_V_valueMap_withXtokensX_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.V().ValueMap().With(WithOptions.Tokens)}}, - {"g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.With("singlelabel").V().ValueMap().With(WithOptions.Tokens)}}, + {"g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default", new List, ITraversal>> {(g,p) =>g.With("singlelabel").V().Has("name", "lagoon").ValueMap("name", "biome").With(WithOptions.Tokens)}}, {"g_withXsinglelabelX_V_valueMapXtrueX_zero_label_vertex", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.With("singlelabel").V().ValueMap(true)}}, {"g_withXmultilabelX_V_valueMapXtrueX_zero_label_vertex", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.With("multilabel").V().ValueMap(true)}}, {"g_V_valueMapXtrueX_zero_label_vertex_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.V().ValueMap(true)}}, From c5cee93d0ac86bb411213048488f4cf427788317 Mon Sep 17 00:00:00 2001 From: Cole Greer Date: Wed, 8 Jul 2026 11:21:42 -0700 Subject: [PATCH 05/10] Document the zoo toy graph with a diagram Introduces the zoo graph to readers in the reference docs, following the same TIP + diagram + example pattern used for "The Crew" (createTheZoo(), data/tinkerpop-zoo*, LabelCardinality requirements), and adds it to the Gremlin Console tutorial's toy graph catalogue alongside classic, modern, and the crew. The diagram (the-zoo-graph.png) is generated with Graphviz's fdp layout engine, styled to match the existing modern/crew diagrams: filled circles labeled with vertex id + labels, rounded property callouts, and curved edges labeled with the edge label plus its id. It reflects all 13 vertices and 20 edges from TinkerFactory.generateTheZoo(). Assisted-by: Kiro:claude-sonnet-5 --- docs/src/reference/the-graph.asciidoc | 28 ++++++++++++++++++ .../the-gremlin-console/index.asciidoc | 3 ++ docs/static/images/the-zoo-graph.png | Bin 0 -> 169045 bytes 3 files changed, 31 insertions(+) create mode 100644 docs/static/images/the-zoo-graph.png diff --git a/docs/src/reference/the-graph.asciidoc b/docs/src/reference/the-graph.asciidoc index beddc43feca..1c04da4b97d 100644 --- a/docs/src/reference/the-graph.asciidoc +++ b/docs/src/reference/the-graph.asciidoc @@ -212,6 +212,34 @@ supported, what its default is or whether the choice is left to the user to conf <>, <>, <>, and <> steps for traversal access. +[[the-zoo-toy-graph]] +TIP: A toy graph demonstrating multi-label vertices alongside a variety of property types is available at +`TinkerFactory.createTheZoo()` and `data/tinkerpop-zoo*`. Unlike "The Crew" above, which requires a graph configured +for vertex multi-properties and meta-properties, "The Zoo" requires a `LabelCardinality` of `ONE_OR_MORE` or +`ZERO_OR_MORE`. + +.The Zoo +image::the-zoo-graph.png[width=685] + +"The Zoo" models animals, their habitats, and the people who care for them. Animals carry the "animal" label plus +any number of descriptive labels - such as "aquatic", "nocturnal", or "endangered" - reflecting the traits that +apply to them, which is where the graph's multi-label vertices come into play: + +[gremlin-groovy,theZoo] +---- +g.V().hasLabel('animal').valueMap('name','species') +g.V().has('name','tux').labels() <1> +g.V().hasLabel('endangered').values('name') <2> +g.V().has('name','tux').out('livesIn').values('name','biome') <3> +---- + +<1> `tux` carries several labels at once - "animal", "bird", "aquatic", and "endangered" - since vertex labels are +not limited to exactly one for this graph. +<2> Filtering by a single label, such as "endangered", surfaces every animal that carries it, regardless of what +other labels are also present. +<3> Habitats, like "lagoon" and "canopy", are modeled as ordinary single-labeled vertices connected to their +resident animals with a "livesIn" edge. + [[nullable-property-values]] === Nullable Property Values diff --git a/docs/src/tutorials/the-gremlin-console/index.asciidoc b/docs/src/tutorials/the-gremlin-console/index.asciidoc index 195ba630ade..659ffbdf74c 100644 --- a/docs/src/tutorials/the-gremlin-console/index.asciidoc +++ b/docs/src/tutorials/the-gremlin-console/index.asciidoc @@ -113,6 +113,9 @@ labels are defined and the "weight" edge property is a `double` rather than a `f * `createTheCrew()` - A graph that demonstrates usage of the new structural features of TinkerPop 3.x such as link:https://tinkerpop.apache.org/docs/x.y.z/reference/#vertex-properties[vertex meta-properties and multi-properties] (link:https://tinkerpop.apache.org/docs/x.y.z/images/the-crew-graph.png[diagram]). +* `createTheZoo()` - A graph that demonstrates usage of TinkerPop 4.x +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#vertex-labels[multi-label vertices] alongside a variety of +property types (link:https://tinkerpop.apache.org/docs/x.y.z/images/the-zoo-graph.png[diagram]). [gremlin-groovy] ---- diff --git a/docs/static/images/the-zoo-graph.png b/docs/static/images/the-zoo-graph.png new file mode 100644 index 0000000000000000000000000000000000000000..7f09a860e7adc41501d6ddc250dcbada62449aec GIT binary patch literal 169045 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⩔6Ccv86tVH=OEsj`qH=J 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 Date: Wed, 8 Jul 2026 11:21:56 -0700 Subject: [PATCH 06/10] Disable ZOO location()/featuresRequired() wiring Removes the tinkerpop-zoo-v3.kryo file mapping and feature-requirement list for LoadGraphWith.GraphData.ZOO. Neither the current Gryo nor GraphSON writers preserve multi-label vertices when serializing a whole graph (both StarGraphGraphSONSerializer and the Gryo VertexSerializer write a single label string, not the full label set), so a generated data file for the zoo graph would silently drop the multi-label vertices it exists to showcase. ZOO remains in the GraphData enum since it's still used successfully via the Gherkin 'Given the zoo graph' step, which builds the graph directly in memory through TinkerFactory.createTheZoo()/generateTheZoo() rather than deserializing a file, and is unaffected by this gap. Calling location() or featuresRequired() on ZOO now falls through to the existing 'No file/features for this GraphData type' exceptions rather than pointing at a nonexistent resource. Assisted-by: Kiro:claude-sonnet-5 --- .../apache/tinkerpop/gremlin/LoadGraphWith.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/LoadGraphWith.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/LoadGraphWith.java index 5418dff87a8..dc774165141 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/LoadGraphWith.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/LoadGraphWith.java @@ -99,6 +99,11 @@ public enum GraphData { /** * Loads "The Zoo" TinkerPop4 toy graph which showcases multi-label vertices and a variety of property * types. Requires a graph configured with {@code LabelCardinality.ZERO_OR_MORE} (or {@code ONE_OR_MORE}). + *

+ * Note that this {@code GraphData} does not currently support {@link #location()} or + * {@link #featuresRequired()} since none of the serialization formats used to produce a data file for it + * (Gryo, GraphSON) correctly round-trip multi-label vertices yet. It is only usable via a + * {@link GraphProvider} that builds the graph directly, e.g. with {@code TinkerFactory.createTheZoo()}. */ ZOO; @@ -138,14 +143,6 @@ public enum GraphData { add(FeatureRequirement.Factory.create(FEATURE_INTEGER_VALUES, EdgePropertyFeatures.class)); }}; - private static final List featuresRequiredByZoo = new ArrayList() {{ - add(FeatureRequirement.Factory.create(FEATURE_STRING_VALUES, VertexPropertyFeatures.class)); - add(FeatureRequirement.Factory.create(FEATURE_INTEGER_VALUES, VertexPropertyFeatures.class)); - add(FeatureRequirement.Factory.create(FEATURE_DOUBLE_VALUES, VertexPropertyFeatures.class)); - add(FeatureRequirement.Factory.create(FEATURE_BOOLEAN_VALUES, VertexPropertyFeatures.class)); - add(FeatureRequirement.Factory.create(FEATURE_MULTI_PROPERTIES, Graph.Features.VertexFeatures.class)); - }}; - public String location() { switch (this) { case CLASSIC: @@ -160,8 +157,6 @@ public String location() { return RESOURCE_PATH_PREFIX + "tinkerpop-sink-v3.kryo"; case AIR_ROUTES: return RESOURCE_PATH_PREFIX + "air-routes-v3.kryo"; - case ZOO: - return RESOURCE_PATH_PREFIX + "tinkerpop-zoo-v3.kryo"; } throw new RuntimeException("No file for this GraphData type"); @@ -181,8 +176,6 @@ public List featuresRequired() { return featuresRequiredBySink; case AIR_ROUTES: return featuresRequiredByAirRoutes; - case ZOO: - return featuresRequiredByZoo; } throw new RuntimeException("No features for this GraphData type"); From 4631d012e7d01f3e0f85552fca51565ced2b74ae Mon Sep 17 00:00:00 2001 From: Cole Greer Date: Wed, 8 Jul 2026 15:36:46 -0700 Subject: [PATCH 07/10] Wire up the zoo graph for Hadoop via multi-label GraphSON V4 StarGraphGraphSONSerializerV4/StarGraphGraphSONDeserializer only ever wrote/read a vertex's label as a single string, even under GraphSON V4, so a multi-label vertex silently lost all but one of its labels when serialized through the StarGraph codec. Fixes the V4 serializer to write the full label set as an array (matching the convention already used by the element-level V4 serializers for the driver protocol) and the shared deserializer to read either a single string (V1/V2/V3) or an array (V4) back into a StarVertex's label set. Adds a tinkerpop-zoo-v4.json GraphSON V4 fixture (generated from TinkerFactory.createTheZoo()) and registers it in TestFiles so HadoopGraphWorld can load the zoo graph for @MultiLabel Gherkin scenarios, using GraphSONInputFormat/V4_0 instead of Gryo since Gryo's StarGraph codec (StarGraphSerializer) has the same single-label limitation and is being left as-is for a later follow-up. Also fixes HadoopElement, which delegated label() to its wrapped element but never overrode labels(), silently falling back to Element's single-label default (Collections.singleton(label())) regardless of how many labels the underlying vertex actually had. Spark's equivalent SparkGraphWorld wiring is left for a follow-up; it shares the same HadoopGraph/StarGraph machinery as Hadoop but hasn't been updated yet. Assisted-by: Kiro:claude-sonnet-5 --- .../tinkerpop/gremlin/features/TestFiles.java | 9 ++++++- .../io/graphson/tinkerpop-zoo-v4.json | 13 ++++++++++ .../HadoopGraphFeatureIntegrateTest.java | 24 +++++++++++++++++-- 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/tinkerpop-zoo-v4.json diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/TestFiles.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/TestFiles.java index ddf67a5fce2..b5de9855449 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/TestFiles.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/TestFiles.java @@ -59,7 +59,8 @@ public class TestFiles { "tinkerpop-classic-byteid-typed-v2.json", "tinkerpop-crew-typed-v2.json", "tinkerpop-crew-v3.json", - "tinkerpop-sink-v3.json"); + "tinkerpop-sink-v3.json", + "tinkerpop-zoo-v4.json"); for (final String fileName : graphsonResources) { PATHS.put(fileName, Storage.toPath(TestHelper.generateTempFileFromResource(GraphSONResourceAccess.class, fileName, ""))); @@ -81,6 +82,12 @@ public class TestFiles { } public static String getInputLocation(final LoadGraphWith.GraphData graphData, final boolean useGraphSON) { + // the zoo graph is multi-label and only GraphSON V4 preserves the full label set for a vertex - Gryo (and + // GraphSON V1/V2/V3) can only carry a single label, so the zoo graph is always read from its V4 GraphSON + // resource regardless of which format the caller requested. + if (graphData.equals(LoadGraphWith.GraphData.ZOO)) + return PATHS.get("tinkerpop-zoo-v4.json"); + final String type = useGraphSON ? "-v3.json" : "-v3.kryo"; if (graphData.equals(LoadGraphWith.GraphData.GRATEFUL)) diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/tinkerpop-zoo-v4.json b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/tinkerpop-zoo-v4.json new file mode 100644 index 00000000000..b86a868375a --- /dev/null +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/tinkerpop-zoo-v4.json @@ -0,0 +1,13 @@ +{"id":{"@type":"g:Int32","@value":1},"label":["bird","endangered","animal","aquatic"],"inE":{"friendsWith":[{"id":{"@type":"g:Int32","@value":29},"outV":{"@type":"g:Int32","@value":3},"properties":{"since":{"@type":"g:Int32","@value":2020}}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":14},"inV":{"@type":"g:Int32","@value":11},"properties":{"since":{"@type":"g:Int32","@value":2020}}}],"friendsWith":[{"id":{"@type":"g:Int32","@value":28},"inV":{"@type":"g:Int32","@value":2},"properties":{"since":{"@type":"g:Int32","@value":2020}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":1},"value":"african penguin"}],"name":[{"id":{"@type":"g:Int64","@value":0},"value":"tux"}],"weight":[{"id":{"@type":"g:Int64","@value":2},"value":{"@type":"g:Double","@value":3.7}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":4},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":5},"value":"fish"},{"id":{"@type":"g:Int64","@value":6},"value":"krill"},{"id":{"@type":"g:Int64","@value":7},"value":"squid"}],"age":[{"id":{"@type":"g:Int64","@value":3},"value":{"@type":"g:Int32","@value":5}}]}} +{"id":{"@type":"g:Int32","@value":2},"label":["endangered","animal","reptile","aquatic"],"inE":{"friendsWith":[{"id":{"@type":"g:Int32","@value":28},"outV":{"@type":"g:Int32","@value":1},"properties":{"since":{"@type":"g:Int32","@value":2020}}}],"careFor":[{"id":{"@type":"g:Int32","@value":24},"outV":{"@type":"g:Int32","@value":13},"properties":{"specialty":"conservation"}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":15},"inV":{"@type":"g:Int32","@value":11},"properties":{"since":{"@type":"g:Int32","@value":2018}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":9},"value":"green sea turtle"}],"name":[{"id":{"@type":"g:Int64","@value":8},"value":"atlas"}],"weight":[{"id":{"@type":"g:Int64","@value":10},"value":{"@type":"g:Double","@value":180.5}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":12},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":13},"value":"seagrass"},{"id":{"@type":"g:Int64","@value":14},"value":"algae"}],"age":[{"id":{"@type":"g:Int64","@value":11},"value":{"@type":"g:Int32","@value":30}}]}} +{"id":{"@type":"g:Int32","@value":3},"label":["mammal","animal","aquatic"],"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":16},"inV":{"@type":"g:Int32","@value":11},"properties":{"since":{"@type":"g:Int32","@value":2019}}}],"friendsWith":[{"id":{"@type":"g:Int32","@value":29},"inV":{"@type":"g:Int32","@value":1},"properties":{"since":{"@type":"g:Int32","@value":2020}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":16},"value":"bottlenose dolphin"}],"name":[{"id":{"@type":"g:Int64","@value":15},"value":"ripple"}],"weight":[{"id":{"@type":"g:Int64","@value":17},"value":{"@type":"g:Double","@value":220.0}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":19},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":20},"value":"fish"},{"id":{"@type":"g:Int64","@value":21},"value":"squid"}],"age":[{"id":{"@type":"g:Int64","@value":18},"value":{"@type":"g:Int32","@value":12}}]}} +{"id":{"@type":"g:Int32","@value":4},"label":["nocturnal","animal","reptile"],"inE":{"eats":[{"id":{"@type":"g:Int32","@value":31},"outV":{"@type":"g:Int32","@value":10}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":18},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2021}}}],"eats":[{"id":{"@type":"g:Int32","@value":32},"inV":{"@type":"g:Int32","@value":8}}]},"properties":{"venomous":[{"id":{"@type":"g:Int64","@value":27},"value":false}],"species":[{"id":{"@type":"g:Int64","@value":23},"value":"ball python"}],"name":[{"id":{"@type":"g:Int64","@value":22},"value":"monty"}],"weight":[{"id":{"@type":"g:Int64","@value":24},"value":{"@type":"g:Double","@value":1.8}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":26},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":28},"value":"mice"},{"id":{"@type":"g:Int64","@value":29},"value":"rats"}],"age":[{"id":{"@type":"g:Int64","@value":25},"value":{"@type":"g:Int32","@value":8}}]}} +{"id":{"@type":"g:Int32","@value":5},"label":["nocturnal","mammal","flying","animal"],"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":19},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2022}}}],"avoids":[{"id":{"@type":"g:Int32","@value":33},"inV":{"@type":"g:Int32","@value":8}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":31},"value":"fruit bat"}],"name":[{"id":{"@type":"g:Int64","@value":30},"value":"echo"}],"weight":[{"id":{"@type":"g:Int64","@value":32},"value":{"@type":"g:Double","@value":0.3}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":34},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":35},"value":"fruit"},{"id":{"@type":"g:Int64","@value":36},"value":"nectar"}],"age":[{"id":{"@type":"g:Int64","@value":33},"value":{"@type":"g:Int32","@value":3}}]}} +{"id":{"@type":"g:Int32","@value":6},"label":["nocturnal","mammal","endangered","animal"],"inE":{"friendsWith":[{"id":{"@type":"g:Int32","@value":30},"outV":{"@type":"g:Int32","@value":7},"properties":{"since":{"@type":"g:Int32","@value":2022}}}],"careFor":[{"id":{"@type":"g:Int32","@value":25},"outV":{"@type":"g:Int32","@value":13},"properties":{"specialty":"conservation"}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":20},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2023}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":38},"value":"red panda"}],"name":[{"id":{"@type":"g:Int64","@value":37},"value":"blaze"}],"weight":[{"id":{"@type":"g:Int64","@value":39},"value":{"@type":"g:Double","@value":5.4}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":41},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":42},"value":"bamboo"},{"id":{"@type":"g:Int64","@value":43},"value":"fruit"},{"id":{"@type":"g:Int64","@value":44},"value":"insects"}],"age":[{"id":{"@type":"g:Int64","@value":40},"value":{"@type":"g:Int32","@value":4}}]}} +{"id":{"@type":"g:Int32","@value":7},"label":["mammal","endangered","animal"],"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":23},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2019}}}],"friendsWith":[{"id":{"@type":"g:Int32","@value":30},"inV":{"@type":"g:Int32","@value":6},"properties":{"since":{"@type":"g:Int32","@value":2022}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":46},"value":"african elephant"}],"name":[{"id":{"@type":"g:Int64","@value":45},"value":"titan"}],"weight":[{"id":{"@type":"g:Int64","@value":47},"value":{"@type":"g:Double","@value":4000.0}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":49},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":50},"value":"grass"},{"id":{"@type":"g:Int64","@value":51},"value":"leaves"},{"id":{"@type":"g:Int64","@value":52},"value":"bark"}],"age":[{"id":{"@type":"g:Int64","@value":48},"value":{"@type":"g:Int32","@value":15}}]}} +{"id":{"@type":"g:Int32","@value":8},"label":["nocturnal","mammal","animal"],"inE":{"eats":[{"id":{"@type":"g:Int32","@value":32},"outV":{"@type":"g:Int32","@value":4}}],"avoids":[{"id":{"@type":"g:Int32","@value":33},"outV":{"@type":"g:Int32","@value":5}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":21},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2024}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":54},"value":"harvest mouse"}],"name":[{"id":{"@type":"g:Int64","@value":53},"value":"bitsy"}],"weight":[{"id":{"@type":"g:Int64","@value":55},"value":{"@type":"g:Double","@value":0.006}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":57},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":58},"value":"seeds"},{"id":{"@type":"g:Int64","@value":59},"value":"insects"}],"age":[{"id":{"@type":"g:Int64","@value":56},"value":{"@type":"g:Int32","@value":1}}]}} +{"id":{"@type":"g:Int32","@value":9},"label":["nocturnal","mammal","endangered","animal","aquatic"],"inE":{"careFor":[{"id":{"@type":"g:Int32","@value":26},"outV":{"@type":"g:Int32","@value":13},"properties":{"specialty":"conservation"}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":17},"inV":{"@type":"g:Int32","@value":11},"properties":{"since":{"@type":"g:Int32","@value":2023}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":61},"value":"fishing cat"}],"name":[{"id":{"@type":"g:Int64","@value":60},"value":"splash"}],"weight":[{"id":{"@type":"g:Int64","@value":62},"value":{"@type":"g:Double","@value":8.2}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":64},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":65},"value":"fish"},{"id":{"@type":"g:Int64","@value":66},"value":"frogs"},{"id":{"@type":"g:Int64","@value":67},"value":"crustaceans"}],"age":[{"id":{"@type":"g:Int64","@value":63},"value":{"@type":"g:Int32","@value":2}}]}} +{"id":{"@type":"g:Int32","@value":10},"label":["nocturnal","mammal","endangered","animal"],"inE":{"careFor":[{"id":{"@type":"g:Int32","@value":27},"outV":{"@type":"g:Int32","@value":13},"properties":{"specialty":"conservation"}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":22},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2020}}}],"eats":[{"id":{"@type":"g:Int32","@value":31},"inV":{"@type":"g:Int32","@value":4}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":69},"value":"bengal tiger"}],"name":[{"id":{"@type":"g:Int64","@value":68},"value":"tinker"}],"weight":[{"id":{"@type":"g:Int64","@value":70},"value":{"@type":"g:Double","@value":220.0}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":72},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":73},"value":"deer"},{"id":{"@type":"g:Int64","@value":74},"value":"boar"},{"id":{"@type":"g:Int64","@value":75},"value":"snakes"}],"age":[{"id":{"@type":"g:Int64","@value":71},"value":{"@type":"g:Int32","@value":5}}]}} +{"id":{"@type":"g:Int32","@value":11},"label":["habitat","aquatic"],"inE":{"livesIn":[{"id":{"@type":"g:Int32","@value":16},"outV":{"@type":"g:Int32","@value":3},"properties":{"since":{"@type":"g:Int32","@value":2019}}},{"id":{"@type":"g:Int32","@value":17},"outV":{"@type":"g:Int32","@value":9},"properties":{"since":{"@type":"g:Int32","@value":2023}}},{"id":{"@type":"g:Int32","@value":14},"outV":{"@type":"g:Int32","@value":1},"properties":{"since":{"@type":"g:Int32","@value":2020}}},{"id":{"@type":"g:Int32","@value":15},"outV":{"@type":"g:Int32","@value":2},"properties":{"since":{"@type":"g:Int32","@value":2018}}}]},"properties":{"openAir":[{"id":{"@type":"g:Int64","@value":79},"value":true}],"biome":[{"id":{"@type":"g:Int64","@value":77},"value":"marine"}],"name":[{"id":{"@type":"g:Int64","@value":76},"value":"lagoon"}],"capacity":[{"id":{"@type":"g:Int64","@value":78},"value":{"@type":"g:Int32","@value":8}}]}} +{"id":{"@type":"g:Int32","@value":12},"label":["habitat"],"inE":{"livesIn":[{"id":{"@type":"g:Int32","@value":18},"outV":{"@type":"g:Int32","@value":4},"properties":{"since":{"@type":"g:Int32","@value":2021}}},{"id":{"@type":"g:Int32","@value":19},"outV":{"@type":"g:Int32","@value":5},"properties":{"since":{"@type":"g:Int32","@value":2022}}},{"id":{"@type":"g:Int32","@value":20},"outV":{"@type":"g:Int32","@value":6},"properties":{"since":{"@type":"g:Int32","@value":2023}}},{"id":{"@type":"g:Int32","@value":21},"outV":{"@type":"g:Int32","@value":8},"properties":{"since":{"@type":"g:Int32","@value":2024}}},{"id":{"@type":"g:Int32","@value":22},"outV":{"@type":"g:Int32","@value":10},"properties":{"since":{"@type":"g:Int32","@value":2020}}},{"id":{"@type":"g:Int32","@value":23},"outV":{"@type":"g:Int32","@value":7},"properties":{"since":{"@type":"g:Int32","@value":2019}}}]},"properties":{"openAir":[{"id":{"@type":"g:Int64","@value":83},"value":false}],"biome":[{"id":{"@type":"g:Int64","@value":81},"value":"tropical"}],"name":[{"id":{"@type":"g:Int64","@value":80},"value":"canopy"}],"capacity":[{"id":{"@type":"g:Int64","@value":82},"value":{"@type":"g:Int32","@value":8}}]}} +{"id":{"@type":"g:Int32","@value":13},"label":["person","veterinarian","keeper"],"outE":{"careFor":[{"id":{"@type":"g:Int32","@value":24},"inV":{"@type":"g:Int32","@value":2},"properties":{"specialty":"conservation"}},{"id":{"@type":"g:Int32","@value":25},"inV":{"@type":"g:Int32","@value":6},"properties":{"specialty":"conservation"}},{"id":{"@type":"g:Int32","@value":26},"inV":{"@type":"g:Int32","@value":9},"properties":{"specialty":"conservation"}},{"id":{"@type":"g:Int32","@value":27},"inV":{"@type":"g:Int32","@value":10},"properties":{"specialty":"conservation"}}]},"properties":{"specialties":[{"id":{"@type":"g:Int64","@value":86},"value":"conservation"},{"id":{"@type":"g:Int64","@value":87},"value":"surgery"},{"id":{"@type":"g:Int64","@value":88},"value":"nutrition"}],"name":[{"id":{"@type":"g:Int64","@value":84},"value":"dr_gremlin"}],"since":[{"id":{"@type":"g:Int64","@value":85},"value":{"@type":"g:Int32","@value":2015}}]}} diff --git a/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/HadoopGraphFeatureIntegrateTest.java b/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/HadoopGraphFeatureIntegrateTest.java index 03132751de5..91320b3a205 100644 --- a/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/HadoopGraphFeatureIntegrateTest.java +++ b/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/HadoopGraphFeatureIntegrateTest.java @@ -33,21 +33,21 @@ import org.apache.tinkerpop.gremlin.features.TestFiles; import org.apache.tinkerpop.gremlin.features.World; import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONInputFormat; import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoInputFormat; import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoOutputFormat; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; import org.javatuples.Pair; import org.junit.AssumptionViolatedException; import org.junit.runner.RunWith; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData; @@ -91,6 +91,7 @@ public static class HadoopGraphWorld implements World { private static final HadoopGraph crew = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.CREW))); private static final HadoopGraph sink = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.SINK))); private static final HadoopGraph grateful = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.GRATEFUL))); + private static final HadoopGraph zoo = HadoopGraph.open(new MapConfiguration(getZooConfiguration())); static { readIntoGraph(modern, GraphData.MODERN); @@ -98,6 +99,7 @@ public static class HadoopGraphWorld implements World { readIntoGraph(crew, GraphData.CREW); readIntoGraph(sink, GraphData.SINK); readIntoGraph(grateful, GraphData.GRATEFUL); + readIntoGraph(zoo, GraphData.ZOO); } @Override @@ -114,6 +116,8 @@ else if (graphData == GraphData.SINK) return sink.traversal(); else if (graphData == GraphData.GRATEFUL) return grateful.traversal(); + else if (graphData == GraphData.ZOO) + return zoo.traversal(); else throw new UnsupportedOperationException("GraphData not supported: " + graphData.name()); } @@ -144,6 +148,22 @@ private static Map getBaseConfiguration(final GraphData graphDat }}; } + /** + * The zoo graph is multi-label and only GraphSON V4 preserves the full label set for a vertex - Gryo (and + * GraphSON V1/V2/V3) can only carry a single label - so, unlike the other reference graphs, it is read via + * {@link GraphSONInputFormat} configured for {@link GraphSONVersion#V4_0} rather than Gryo. + */ + private static Map getZooConfiguration() { + return new HashMap() {{ + put(Graph.GRAPH, HadoopGraph.class.getName()); + put(Constants.GREMLIN_HADOOP_GRAPH_READER, GraphSONInputFormat.class.getCanonicalName()); + put(Constants.GREMLIN_HADOOP_GRAPHSON_VERSION, GraphSONVersion.V4_0.name()); + put(Constants.GREMLIN_HADOOP_GRAPH_WRITER, GryoOutputFormat.class.getCanonicalName()); + put(Constants.GREMLIN_HADOOP_OUTPUT_LOCATION, getWorkingDirectory()); + put(Constants.GREMLIN_HADOOP_JARS_IN_DISTRIBUTED_CACHE, false); + }}; + } + @Override public boolean useParametersLiterally() { return false; From 19d4d42e815435d704deedf437d7391995d46a3e Mon Sep 17 00:00:00 2001 From: Cole Greer Date: Thu, 9 Jul 2026 09:08:10 -0700 Subject: [PATCH 08/10] Support multi-label vertices in Gryo; wire up zoo graph for Hadoop and Spark StarGraphSerializer's Kryo format only ever wrote/read a vertex's label as a single string, so a multi-label vertex silently lost all but one label when serialized. Introduces a VERSION_2 format byte that writes the full label set as a list; VERSION_1 (single label) data written by prior versions of this serializer remains readable via a version-branched reader, so existing .kryo fixtures are unaffected. Attachable.Method.createVertex (used by GryoReader.readGraph and other providers' attach-to-host-graph logic) had the same gap - it read a multi-label source vertex correctly but only ever passed a single label to the new vertex. Fixed to call addLabel() for any labels beyond the first. Adds a tinkerpop-zoo-v3.kryo fixture (generated via the existing IoDataGenerationTest convention) and registers it in TestFiles, replacing the GraphSON V4-only workaround from the prior commit. HadoopGraphWorld and SparkGraphWorld now both load the zoo graph the same way as every other reference graph, via plain Gryo. GraphSON V1/V2/V3 remain single-label-only, so requesting the zoo graph via useGraphSON=true now fails with a clear error rather than silently truncating labels. The StarGraphGraphSONSerializerV4/StarGraphGraphSONDeserializer fix from the prior commit is unchanged and still exercised by its own test; it remains a legitimate improvement to the V4 GraphSON wire format independent of how Hadoop/Spark load their reference graphs. Verified against HadoopGraphFeatureIntegrateTest (2129 scenarios), HadoopGraphProcessStandardIntegrateTest (206 tests), and SparkGraphFeatureIntegrateTest (1448 scenarios) - all passing with zero failures/errors. Assisted-by: Kiro:claude-sonnet-5 --- .../Gherkin/Gremlin.cs | 16 +++++++------- gremlin-go/driver/cucumber/gremlin.go | 16 +++++++------- .../src/main/python/tests/feature/gremlin.py | 16 +++++++------- .../tinkerpop/gremlin/features/TestFiles.java | 20 +++++++++--------- .../io/graphson/tinkerpop-zoo-v4.json | 13 ------------ .../structure/io/gryo/tinkerpop-zoo-v3.kryo | Bin 0 -> 3253 bytes .../HadoopGraphFeatureIntegrateTest.java | 20 +----------------- .../spark/SparkGraphFeatureIntegrateTest.java | 4 ++++ .../structure/IoDataGenerationTest.java | 10 +++++++++ 9 files changed, 49 insertions(+), 66 deletions(-) delete mode 100644 gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/tinkerpop-zoo-v4.json create mode 100644 gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/tinkerpop-zoo-v3.kryo diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 7e1091cbd0e..0d3049a462f 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -3641,15 +3641,15 @@ private static IDictionary, ITraversal>> {(g,p) =>g.With("singlelabel").V().ElementMap("name", "age", null)}}, {"g_withXmultilabelX_VXmarkoX_elementMap", new List, ITraversal>> {(g,p) =>g.With("multilabel").V().Has("name", "marko").ElementMap()}}, {"g_V_elementMap_single_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.V().ElementMap()}}, - {"g_withXmultilabelX_V_elementMap_multilabel", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.With("multilabel").V().ElementMap()}}, + {"g_withXmultilabelX_V_elementMap_multilabel", new List, ITraversal>> {(g,p) =>g.With("multilabel").V().Has("name", "tux").ElementMap("name", "species")}}, {"g_V_elementMap_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.V().ElementMap()}}, - {"g_withXsinglelabelX_V_elementMap_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.With("singlelabel").V().ElementMap()}}, + {"g_withXsinglelabelX_V_elementMap_multi_label_default", new List, ITraversal>> {(g,p) =>g.With("singlelabel").V().Has("name", "lagoon").ElementMap("name", "biome")}}, {"g_V_elementMap_single_label_vertex_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko"), (g,p) =>g.V().ElementMap()}}, {"g_withXsinglelabelX_V_elementMap_zero_label_vertex", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.With("singlelabel").V().ElementMap()}}, {"g_withXmultilabelX_V_elementMap_zero_label_vertex", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.With("multilabel").V().ElementMap()}}, {"g_V_elementMap_zero_label_vertex_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.V().ElementMap()}}, {"g_V_elementMap_zero_label_vertex_single_label_default", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.V().ElementMap()}}, - {"g_withXmultilabelX_E_elementMap", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b").Property("weight", 0.5), (g,p) =>g.With("multilabel").E().ElementMap()}}, + {"g_withXmultilabelX_E_elementMap", new List, ITraversal>> {(g,p) =>g.With("multilabel").E().Has("since", 2018).HasLabel("livesIn").ElementMap()}}, {"g_E_elementMap_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b").Property("weight", 0.5), (g,p) =>g.E().ElementMap()}}, {"g_E_elementMap_single_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b").Property("weight", 0.5), (g,p) =>g.E().ElementMap()}}, {"g_withXsinglelabelX_E_elementMap", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "marko").As("a").AddV((string) "person").Property("name", "josh").As("b").AddE((string) "knows").From("a").To("b").Property("weight", 0.5), (g,p) =>g.With("singlelabel").E().ElementMap()}}, @@ -3704,11 +3704,11 @@ private static IDictionary, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", " marko ").Property("age", 29).As("marko").AddV((string) "person").Property("name", " vadas ").Property("age", 27).As("vadas").AddV((string) "software").Property("name", " lop").Property("lang", "java").As("lop").AddV((string) "person").Property("name", "josh ").Property("age", 32).As("josh").AddV((string) "software").Property("name", " ripple ").Property("lang", "java").As("ripple").AddV((string) "person").Property("name", "peter").Property("age", 35).As("peter").AddE((string) "knows").From("marko").To("vadas").Property("weight", 0.5d).AddE((string) "knows").From("marko").To("josh").Property("weight", 1.0d).AddE((string) "created").From("marko").To("lop").Property("weight", 0.4d).AddE((string) "created").From("josh").To("ripple").Property("weight", 1.0d).AddE((string) "created").From("josh").To("lop").Property("weight", 0.4d).AddE((string) "created").From("peter").To("lop").Property("weight", 0.2d), (g,p) =>g.V().Values("name").LTrim()}}, {"g_V_valuesXnameX_order_fold_lTrimXlocalX", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", " marko ").Property("age", 29).As("marko").AddV((string) "person").Property("name", " vadas ").Property("age", 27).As("vadas").AddV((string) "software").Property("name", " lop").Property("lang", "java").As("lop").AddV((string) "person").Property("name", "josh ").Property("age", 32).As("josh").AddV((string) "software").Property("name", " ripple ").Property("lang", "java").As("ripple").AddV((string) "person").Property("name", "peter").Property("age", 35).As("peter").AddE((string) "knows").From("marko").To("vadas").Property("weight", 0.5d).AddE((string) "knows").From("marko").To("josh").Property("weight", 1.0d).AddE((string) "created").From("marko").To("lop").Property("weight", 0.4d).AddE((string) "created").From("josh").To("ripple").Property("weight", 1.0d).AddE((string) "created").From("josh").To("lop").Property("weight", 0.4d).AddE((string) "created").From("peter").To("lop").Property("weight", 0.2d), (g,p) =>g.V().Values("name").Order().Fold().LTrim(Scope.Local)}}, {"g_V_label_single_label_graph", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Label()}}, - {"g_V_label_deprecated_multilabel", new List, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b").Property("name", "test"), (g,p) =>g.V().Label().Count()}}, - {"g_V_label_deprecated_multilabel_value_is_one_of_labels", new List, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b").Property("name", "test"), (g,p) =>g.V().Filter(__.Label().Is(P.Within("a", "b")))}}, + {"g_V_label_deprecated_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Label().Count()}}, + {"g_V_label_deprecated_multilabel_value_is_one_of_labels", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Filter(__.Label().Is(P.Within("animal", "bird", "aquatic", "endangered")))}}, {"g_E_label_deprecated_multilabel_graph", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").As("a").AddV((string) "person").As("b").AddE((string) "knows").From("a").To("b"), (g,p) =>g.E().Label()}}, {"g_V_hasLabelXpersonX_labels", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Labels()}}, - {"g_V_labels_multilabel", new List, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b"), (g,p) =>g.V().Labels()}}, + {"g_V_labels_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Labels()}}, {"g_addVXa_bX_labels_count", new List, ITraversal>> {(g,p) =>g.AddV((string) "a", (string) "b"), (g,p) =>g.V().Labels().Count()}}, {"g_addV_labels", new List, ITraversal>> {(g,p) =>g.AddV(), (g,p) =>g.V().Labels()}}, {"g_E_labels", new List, ITraversal>> {(g,p) =>g.E().HasLabel("knows").Labels()}}, @@ -4299,9 +4299,9 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().ValueMap("name", "age").By(__.Is("x")).By(__.Unfold())}}, {"g_withXmultilabelX_VXmarkoX_valueMap_withXtokensX", new List, ITraversal>> {(g,p) =>g.With("multilabel").V().Has("name", "marko").ValueMap().With(WithOptions.Tokens)}}, {"g_V_valueMap_withXtokensX_single_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.V().ValueMap().With(WithOptions.Tokens)}}, - {"g_withXmultilabelX_V_valueMap_withXtokensX_multilabel", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.With("multilabel").V().ValueMap().With(WithOptions.Tokens)}}, + {"g_withXmultilabelX_V_valueMap_withXtokensX_multilabel", new List, ITraversal>> {(g,p) =>g.With("multilabel").V().Has("name", "tux").ValueMap("name", "species").With(WithOptions.Tokens)}}, {"g_V_valueMap_withXtokensX_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.V().ValueMap().With(WithOptions.Tokens)}}, - {"g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").AddLabel("employee").Property("name", "marko"), (g,p) =>g.With("singlelabel").V().ValueMap().With(WithOptions.Tokens)}}, + {"g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default", new List, ITraversal>> {(g,p) =>g.With("singlelabel").V().Has("name", "lagoon").ValueMap("name", "biome").With(WithOptions.Tokens)}}, {"g_withXsinglelabelX_V_valueMapXtrueX_zero_label_vertex", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.With("singlelabel").V().ValueMap(true)}}, {"g_withXmultilabelX_V_valueMapXtrueX_zero_label_vertex", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.With("multilabel").V().ValueMap(true)}}, {"g_V_valueMapXtrueX_zero_label_vertex_multi_label_default", new List, ITraversal>> {(g,p) =>g.AddV().Property("name", "nobody"), (g,p) =>g.V().ValueMap(true)}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index e95ae9d3b56..d519063a7fb 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -1264,15 +1264,15 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_elementMapXname_age_nullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("singlelabel").V().ElementMap("name", "age", nil)}}, "g_withXmultilabelX_VXmarkoX_elementMap": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").V().Has("name", "marko").ElementMap()}}, "g_V_elementMap_single_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").AddLabel("employee").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ElementMap()}}, - "g_withXmultilabelX_V_elementMap_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").AddLabel("employee").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").V().ElementMap()}}, + "g_withXmultilabelX_V_elementMap_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").V().Has("name", "tux").ElementMap("name", "species")}}, "g_V_elementMap_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").AddLabel("employee").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ElementMap()}}, - "g_withXsinglelabelX_V_elementMap_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").AddLabel("employee").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("singlelabel").V().ElementMap()}}, + "g_withXsinglelabelX_V_elementMap_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("singlelabel").V().Has("name", "lagoon").ElementMap("name", "biome")}}, "g_V_elementMap_single_label_vertex_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ElementMap()}}, "g_withXsinglelabelX_V_elementMap_zero_label_vertex": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().Property("name", "nobody")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("singlelabel").V().ElementMap()}}, "g_withXmultilabelX_V_elementMap_zero_label_vertex": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().Property("name", "nobody")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").V().ElementMap()}}, "g_V_elementMap_zero_label_vertex_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().Property("name", "nobody")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ElementMap()}}, "g_V_elementMap_zero_label_vertex_single_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().Property("name", "nobody")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ElementMap()}}, - "g_withXmultilabelX_E_elementMap": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "josh").As("b").AddE("knows").From("a").To("b").Property("weight", 0.5)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").E().ElementMap()}}, + "g_withXmultilabelX_E_elementMap": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").E().Has("since", 2018).HasLabel("livesIn").ElementMap()}}, "g_E_elementMap_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "josh").As("b").AddE("knows").From("a").To("b").Property("weight", 0.5)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().ElementMap()}}, "g_E_elementMap_single_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "josh").As("b").AddE("knows").From("a").To("b").Property("weight", 0.5)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().ElementMap()}}, "g_withXsinglelabelX_E_elementMap": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").As("a").AddV("person").Property("name", "josh").As("b").AddE("knows").From("a").To("b").Property("weight", 0.5)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("singlelabel").E().ElementMap()}}, @@ -1327,11 +1327,11 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_valuesXnameX_lTrim": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return 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("software").Property("name", " ripple ").Property("lang", "java").As("ripple").AddV("person").Property("name", "peter").Property("age", 35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight", 0.5).AddE("knows").From("marko").To("josh").Property("weight", 1.0).AddE("created").From("marko").To("lop").Property("weight", 0.4).AddE("created").From("josh").To("ripple").Property("weight", 1.0).AddE("created").From("josh").To("lop").Property("weight", 0.4).AddE("created").From("peter").To("lop").Property("weight", 0.2)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").LTrim()}}, "g_V_valuesXnameX_order_fold_lTrimXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return 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("software").Property("name", " ripple ").Property("lang", "java").As("ripple").AddV("person").Property("name", "peter").Property("age", 35).As("peter").AddE("knows").From("marko").To("vadas").Property("weight", 0.5).AddE("knows").From("marko").To("josh").Property("weight", 1.0).AddE("created").From("marko").To("lop").Property("weight", 0.4).AddE("created").From("josh").To("ripple").Property("weight", 1.0).AddE("created").From("josh").To("lop").Property("weight", 0.4).AddE("created").From("peter").To("lop").Property("weight", 0.2)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Order().Fold().LTrim(gremlingo.Scope.Local)}}, "g_V_label_single_label_graph": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Label()}}, - "g_V_label_deprecated_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("a", "b").Property("name", "test")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Label().Count()}}, - "g_V_label_deprecated_multilabel_value_is_one_of_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("a", "b").Property("name", "test")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Filter(gremlingo.T__.Label().Is(gremlingo.P.Within("a", "b")))}}, + "g_V_label_deprecated_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "tux").Label().Count()}}, + "g_V_label_deprecated_multilabel_value_is_one_of_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "tux").Filter(gremlingo.T__.Label().Is(gremlingo.P.Within("animal", "bird", "aquatic", "endangered")))}}, "g_E_label_deprecated_multilabel_graph": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").As("a").AddV("person").As("b").AddE("knows").From("a").To("b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().Label()}}, "g_V_hasLabelXpersonX_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Labels()}}, - "g_V_labels_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("a", "b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Labels()}}, + "g_V_labels_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "tux").Labels()}}, "g_addVXa_bX_labels_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("a", "b")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Labels().Count()}}, "g_addV_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Labels()}}, "g_E_labels": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().HasLabel("knows").Labels()}}, @@ -1922,9 +1922,9 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_valueMapXname_ageX_byXisXxXXbyXunfoldX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ValueMap("name", "age").By(gremlingo.T__.Is("x")).By(gremlingo.T__.Unfold())}}, "g_withXmultilabelX_VXmarkoX_valueMap_withXtokensX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").V().Has("name", "marko").ValueMap().With(gremlingo.WithOptions.Tokens)}}, "g_V_valueMap_withXtokensX_single_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").AddLabel("employee").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ValueMap().With(gremlingo.WithOptions.Tokens)}}, - "g_withXmultilabelX_V_valueMap_withXtokensX_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").AddLabel("employee").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").V().ValueMap().With(gremlingo.WithOptions.Tokens)}}, + "g_withXmultilabelX_V_valueMap_withXtokensX_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").V().Has("name", "tux").ValueMap("name", "species").With(gremlingo.WithOptions.Tokens)}}, "g_V_valueMap_withXtokensX_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").AddLabel("employee").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ValueMap().With(gremlingo.WithOptions.Tokens)}}, - "g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").AddLabel("employee").Property("name", "marko")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("singlelabel").V().ValueMap().With(gremlingo.WithOptions.Tokens)}}, + "g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("singlelabel").V().Has("name", "lagoon").ValueMap("name", "biome").With(gremlingo.WithOptions.Tokens)}}, "g_withXsinglelabelX_V_valueMapXtrueX_zero_label_vertex": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().Property("name", "nobody")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("singlelabel").V().ValueMap(true)}}, "g_withXmultilabelX_V_valueMapXtrueX_zero_label_vertex": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().Property("name", "nobody")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.With("multilabel").V().ValueMap(true)}}, "g_V_valueMapXtrueX_zero_label_vertex_multi_label_default": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().Property("name", "nobody")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ValueMap(true)}}, diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py b/gremlin-python/src/main/python/tests/feature/gremlin.py index f810b3e3cd8..b6145bc1813 100644 --- a/gremlin-python/src/main/python/tests/feature/gremlin.py +++ b/gremlin-python/src/main/python/tests/feature/gremlin.py @@ -1269,15 +1269,15 @@ 'g_V_elementMapXname_age_nullX': [(lambda g:g.with_('singlelabel').V().element_map('name', 'age', None))], 'g_withXmultilabelX_VXmarkoX_elementMap': [(lambda g:g.with_('multilabel').V().has('name', 'marko').element_map())], 'g_V_elementMap_single_label_default': [(lambda g:g.add_v('person').add_label('employee').property('name', 'marko')), (lambda g:g.V().element_map())], - 'g_withXmultilabelX_V_elementMap_multilabel': [(lambda g:g.add_v('person').add_label('employee').property('name', 'marko')), (lambda g:g.with_('multilabel').V().element_map())], + 'g_withXmultilabelX_V_elementMap_multilabel': [(lambda g:g.with_('multilabel').V().has('name', 'tux').element_map('name', 'species'))], 'g_V_elementMap_multi_label_default': [(lambda g:g.add_v('person').add_label('employee').property('name', 'marko')), (lambda g:g.V().element_map())], - 'g_withXsinglelabelX_V_elementMap_multi_label_default': [(lambda g:g.add_v('person').add_label('employee').property('name', 'marko')), (lambda g:g.with_('singlelabel').V().element_map())], + 'g_withXsinglelabelX_V_elementMap_multi_label_default': [(lambda g:g.with_('singlelabel').V().has('name', 'lagoon').element_map('name', 'biome'))], 'g_V_elementMap_single_label_vertex_multi_label_default': [(lambda g:g.add_v('person').property('name', 'marko')), (lambda g:g.V().element_map())], 'g_withXsinglelabelX_V_elementMap_zero_label_vertex': [(lambda g:g.add_v().property('name', 'nobody')), (lambda g:g.with_('singlelabel').V().element_map())], 'g_withXmultilabelX_V_elementMap_zero_label_vertex': [(lambda g:g.add_v().property('name', 'nobody')), (lambda g:g.with_('multilabel').V().element_map())], 'g_V_elementMap_zero_label_vertex_multi_label_default': [(lambda g:g.add_v().property('name', 'nobody')), (lambda g:g.V().element_map())], 'g_V_elementMap_zero_label_vertex_single_label_default': [(lambda g:g.add_v().property('name', 'nobody')), (lambda g:g.V().element_map())], - 'g_withXmultilabelX_E_elementMap': [(lambda g:g.add_v('person').property('name', 'marko').as_('a').add_v('person').property('name', 'josh').as_('b').add_e('knows').from_('a').to('b').property('weight', 0.5)), (lambda g:g.with_('multilabel').E().element_map())], + 'g_withXmultilabelX_E_elementMap': [(lambda g:g.with_('multilabel').E().has('since', 2018).has_label('livesIn').element_map())], 'g_E_elementMap_multi_label_default': [(lambda g:g.add_v('person').property('name', 'marko').as_('a').add_v('person').property('name', 'josh').as_('b').add_e('knows').from_('a').to('b').property('weight', 0.5)), (lambda g:g.E().element_map())], 'g_E_elementMap_single_label_default': [(lambda g:g.add_v('person').property('name', 'marko').as_('a').add_v('person').property('name', 'josh').as_('b').add_e('knows').from_('a').to('b').property('weight', 0.5)), (lambda g:g.E().element_map())], 'g_withXsinglelabelX_E_elementMap': [(lambda g:g.add_v('person').property('name', 'marko').as_('a').add_v('person').property('name', 'josh').as_('b').add_e('knows').from_('a').to('b').property('weight', 0.5)), (lambda g:g.with_('singlelabel').E().element_map())], @@ -1332,11 +1332,11 @@ 'g_V_valuesXnameX_lTrim': [(lambda g:g.add_v('person').property('name', ' marko ').property('age', 29).as_('marko').add_v('person').property('name', ' vadas ').property('age', 27).as_('vadas').add_v('software').property('name', ' lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh ').property('age', 32).as_('josh').add_v('software').property('name', ' ripple ').property('lang', 'java').as_('ripple').add_v('person').property('name', 'peter').property('age', 35).as_('peter').add_e('knows').from_('marko').to('vadas').property('weight', 0.5).add_e('knows').from_('marko').to('josh').property('weight', 1.0).add_e('created').from_('marko').to('lop').property('weight', 0.4).add_e('created').from_('josh').to('ripple').property('weight', 1.0).add_e('created').from_('josh').to('lop').property('weight', 0.4).add_e('created').from_('peter').to('lop').property('weight', 0.2)), (lambda g:g.V().values('name').l_trim())], 'g_V_valuesXnameX_order_fold_lTrimXlocalX': [(lambda g:g.add_v('person').property('name', ' marko ').property('age', 29).as_('marko').add_v('person').property('name', ' vadas ').property('age', 27).as_('vadas').add_v('software').property('name', ' lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh ').property('age', 32).as_('josh').add_v('software').property('name', ' ripple ').property('lang', 'java').as_('ripple').add_v('person').property('name', 'peter').property('age', 35).as_('peter').add_e('knows').from_('marko').to('vadas').property('weight', 0.5).add_e('knows').from_('marko').to('josh').property('weight', 1.0).add_e('created').from_('marko').to('lop').property('weight', 0.4).add_e('created').from_('josh').to('ripple').property('weight', 1.0).add_e('created').from_('josh').to('lop').property('weight', 0.4).add_e('created').from_('peter').to('lop').property('weight', 0.2)), (lambda g:g.V().values('name').order().fold().l_trim(Scope.local))], 'g_V_label_single_label_graph': [(lambda g:g.V().has_label('person').label())], - 'g_V_label_deprecated_multilabel': [(lambda g:g.add_v('a', 'b').property('name', 'test')), (lambda g:g.V().label().count())], - 'g_V_label_deprecated_multilabel_value_is_one_of_labels': [(lambda g:g.add_v('a', 'b').property('name', 'test')), (lambda g:g.V().filter_(__.label().is_(P.within('a', 'b'))))], + 'g_V_label_deprecated_multilabel': [(lambda g:g.V().has('name', 'tux').label().count())], + 'g_V_label_deprecated_multilabel_value_is_one_of_labels': [(lambda g:g.V().has('name', 'tux').filter_(__.label().is_(P.within('animal', 'bird', 'aquatic', 'endangered'))))], 'g_E_label_deprecated_multilabel_graph': [(lambda g:g.add_v('person').as_('a').add_v('person').as_('b').add_e('knows').from_('a').to('b')), (lambda g:g.E().label())], 'g_V_hasLabelXpersonX_labels': [(lambda g:g.V().has_label('person').labels())], - 'g_V_labels_multilabel': [(lambda g:g.add_v('a', 'b')), (lambda g:g.V().labels())], + 'g_V_labels_multilabel': [(lambda g:g.V().has('name', 'tux').labels())], 'g_addVXa_bX_labels_count': [(lambda g:g.add_v('a', 'b')), (lambda g:g.V().labels().count())], 'g_addV_labels': [(lambda g:g.add_v()), (lambda g:g.V().labels())], 'g_E_labels': [(lambda g:g.E().has_label('knows').labels())], @@ -1927,9 +1927,9 @@ 'g_V_valueMapXname_ageX_byXisXxXXbyXunfoldX': [(lambda g:g.V().value_map('name', 'age').by(__.is_('x')).by(__.unfold()))], 'g_withXmultilabelX_VXmarkoX_valueMap_withXtokensX': [(lambda g:g.with_('multilabel').V().has('name', 'marko').value_map().with_(WithOptions.tokens))], 'g_V_valueMap_withXtokensX_single_label_default': [(lambda g:g.add_v('person').add_label('employee').property('name', 'marko')), (lambda g:g.V().value_map().with_(WithOptions.tokens))], - 'g_withXmultilabelX_V_valueMap_withXtokensX_multilabel': [(lambda g:g.add_v('person').add_label('employee').property('name', 'marko')), (lambda g:g.with_('multilabel').V().value_map().with_(WithOptions.tokens))], + 'g_withXmultilabelX_V_valueMap_withXtokensX_multilabel': [(lambda g:g.with_('multilabel').V().has('name', 'tux').value_map('name', 'species').with_(WithOptions.tokens))], 'g_V_valueMap_withXtokensX_multi_label_default': [(lambda g:g.add_v('person').add_label('employee').property('name', 'marko')), (lambda g:g.V().value_map().with_(WithOptions.tokens))], - 'g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default': [(lambda g:g.add_v('person').add_label('employee').property('name', 'marko')), (lambda g:g.with_('singlelabel').V().value_map().with_(WithOptions.tokens))], + 'g_withXsinglelabelX_V_valueMap_withXtokensX_multi_label_default': [(lambda g:g.with_('singlelabel').V().has('name', 'lagoon').value_map('name', 'biome').with_(WithOptions.tokens))], 'g_withXsinglelabelX_V_valueMapXtrueX_zero_label_vertex': [(lambda g:g.add_v().property('name', 'nobody')), (lambda g:g.with_('singlelabel').V().value_map(True))], 'g_withXmultilabelX_V_valueMapXtrueX_zero_label_vertex': [(lambda g:g.add_v().property('name', 'nobody')), (lambda g:g.with_('multilabel').V().value_map(True))], 'g_V_valueMapXtrueX_zero_label_vertex_multi_label_default': [(lambda g:g.add_v().property('name', 'nobody')), (lambda g:g.V().value_map(True))], diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/TestFiles.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/TestFiles.java index b5de9855449..865c7a7e8d7 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/TestFiles.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/TestFiles.java @@ -42,7 +42,8 @@ public class TestFiles { "grateful-dead-v3.kryo", "tinkerpop-classic-v3.kryo", "tinkerpop-crew-v3.kryo", - "tinkerpop-sink-v3.kryo"); + "tinkerpop-sink-v3.kryo", + "tinkerpop-zoo-v3.kryo"); for (final String fileName : kryoResources) { PATHS.put(fileName, Storage.toPath(TestHelper.generateTempFileFromResource(GryoResourceAccess.class, fileName, ""))); @@ -59,8 +60,7 @@ public class TestFiles { "tinkerpop-classic-byteid-typed-v2.json", "tinkerpop-crew-typed-v2.json", "tinkerpop-crew-v3.json", - "tinkerpop-sink-v3.json", - "tinkerpop-zoo-v4.json"); + "tinkerpop-sink-v3.json"); for (final String fileName : graphsonResources) { PATHS.put(fileName, Storage.toPath(TestHelper.generateTempFileFromResource(GraphSONResourceAccess.class, fileName, ""))); @@ -82,12 +82,6 @@ public class TestFiles { } public static String getInputLocation(final LoadGraphWith.GraphData graphData, final boolean useGraphSON) { - // the zoo graph is multi-label and only GraphSON V4 preserves the full label set for a vertex - Gryo (and - // GraphSON V1/V2/V3) can only carry a single label, so the zoo graph is always read from its V4 GraphSON - // resource regardless of which format the caller requested. - if (graphData.equals(LoadGraphWith.GraphData.ZOO)) - return PATHS.get("tinkerpop-zoo-v4.json"); - final String type = useGraphSON ? "-v3.json" : "-v3.kryo"; if (graphData.equals(LoadGraphWith.GraphData.GRATEFUL)) @@ -100,7 +94,13 @@ else if (graphData.equals(LoadGraphWith.GraphData.CREW)) return PATHS.get("tinkerpop-crew" + type); else if (graphData.equals(LoadGraphWith.GraphData.SINK)) return PATHS.get("tinkerpop-sink" + type); - else + else if (graphData.equals(LoadGraphWith.GraphData.ZOO)) { + // the zoo graph is multi-label; GraphSON V1/V2/V3 (like Gryo prior to multi-label support) can only + // carry a single label per vertex, so there is no "-v3.json" resource for it - only Gryo is supported. + if (useGraphSON) + throw new RuntimeException("The zoo graph is multi-label and cannot be read from GraphSON V1/V2/V3"); + return PATHS.get("tinkerpop-zoo" + type); + } else throw new RuntimeException("Could not load graph with " + graphData); } diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/tinkerpop-zoo-v4.json b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/tinkerpop-zoo-v4.json deleted file mode 100644 index b86a868375a..00000000000 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/tinkerpop-zoo-v4.json +++ /dev/null @@ -1,13 +0,0 @@ -{"id":{"@type":"g:Int32","@value":1},"label":["bird","endangered","animal","aquatic"],"inE":{"friendsWith":[{"id":{"@type":"g:Int32","@value":29},"outV":{"@type":"g:Int32","@value":3},"properties":{"since":{"@type":"g:Int32","@value":2020}}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":14},"inV":{"@type":"g:Int32","@value":11},"properties":{"since":{"@type":"g:Int32","@value":2020}}}],"friendsWith":[{"id":{"@type":"g:Int32","@value":28},"inV":{"@type":"g:Int32","@value":2},"properties":{"since":{"@type":"g:Int32","@value":2020}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":1},"value":"african penguin"}],"name":[{"id":{"@type":"g:Int64","@value":0},"value":"tux"}],"weight":[{"id":{"@type":"g:Int64","@value":2},"value":{"@type":"g:Double","@value":3.7}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":4},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":5},"value":"fish"},{"id":{"@type":"g:Int64","@value":6},"value":"krill"},{"id":{"@type":"g:Int64","@value":7},"value":"squid"}],"age":[{"id":{"@type":"g:Int64","@value":3},"value":{"@type":"g:Int32","@value":5}}]}} -{"id":{"@type":"g:Int32","@value":2},"label":["endangered","animal","reptile","aquatic"],"inE":{"friendsWith":[{"id":{"@type":"g:Int32","@value":28},"outV":{"@type":"g:Int32","@value":1},"properties":{"since":{"@type":"g:Int32","@value":2020}}}],"careFor":[{"id":{"@type":"g:Int32","@value":24},"outV":{"@type":"g:Int32","@value":13},"properties":{"specialty":"conservation"}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":15},"inV":{"@type":"g:Int32","@value":11},"properties":{"since":{"@type":"g:Int32","@value":2018}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":9},"value":"green sea turtle"}],"name":[{"id":{"@type":"g:Int64","@value":8},"value":"atlas"}],"weight":[{"id":{"@type":"g:Int64","@value":10},"value":{"@type":"g:Double","@value":180.5}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":12},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":13},"value":"seagrass"},{"id":{"@type":"g:Int64","@value":14},"value":"algae"}],"age":[{"id":{"@type":"g:Int64","@value":11},"value":{"@type":"g:Int32","@value":30}}]}} -{"id":{"@type":"g:Int32","@value":3},"label":["mammal","animal","aquatic"],"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":16},"inV":{"@type":"g:Int32","@value":11},"properties":{"since":{"@type":"g:Int32","@value":2019}}}],"friendsWith":[{"id":{"@type":"g:Int32","@value":29},"inV":{"@type":"g:Int32","@value":1},"properties":{"since":{"@type":"g:Int32","@value":2020}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":16},"value":"bottlenose dolphin"}],"name":[{"id":{"@type":"g:Int64","@value":15},"value":"ripple"}],"weight":[{"id":{"@type":"g:Int64","@value":17},"value":{"@type":"g:Double","@value":220.0}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":19},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":20},"value":"fish"},{"id":{"@type":"g:Int64","@value":21},"value":"squid"}],"age":[{"id":{"@type":"g:Int64","@value":18},"value":{"@type":"g:Int32","@value":12}}]}} -{"id":{"@type":"g:Int32","@value":4},"label":["nocturnal","animal","reptile"],"inE":{"eats":[{"id":{"@type":"g:Int32","@value":31},"outV":{"@type":"g:Int32","@value":10}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":18},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2021}}}],"eats":[{"id":{"@type":"g:Int32","@value":32},"inV":{"@type":"g:Int32","@value":8}}]},"properties":{"venomous":[{"id":{"@type":"g:Int64","@value":27},"value":false}],"species":[{"id":{"@type":"g:Int64","@value":23},"value":"ball python"}],"name":[{"id":{"@type":"g:Int64","@value":22},"value":"monty"}],"weight":[{"id":{"@type":"g:Int64","@value":24},"value":{"@type":"g:Double","@value":1.8}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":26},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":28},"value":"mice"},{"id":{"@type":"g:Int64","@value":29},"value":"rats"}],"age":[{"id":{"@type":"g:Int64","@value":25},"value":{"@type":"g:Int32","@value":8}}]}} -{"id":{"@type":"g:Int32","@value":5},"label":["nocturnal","mammal","flying","animal"],"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":19},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2022}}}],"avoids":[{"id":{"@type":"g:Int32","@value":33},"inV":{"@type":"g:Int32","@value":8}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":31},"value":"fruit bat"}],"name":[{"id":{"@type":"g:Int64","@value":30},"value":"echo"}],"weight":[{"id":{"@type":"g:Int64","@value":32},"value":{"@type":"g:Double","@value":0.3}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":34},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":35},"value":"fruit"},{"id":{"@type":"g:Int64","@value":36},"value":"nectar"}],"age":[{"id":{"@type":"g:Int64","@value":33},"value":{"@type":"g:Int32","@value":3}}]}} -{"id":{"@type":"g:Int32","@value":6},"label":["nocturnal","mammal","endangered","animal"],"inE":{"friendsWith":[{"id":{"@type":"g:Int32","@value":30},"outV":{"@type":"g:Int32","@value":7},"properties":{"since":{"@type":"g:Int32","@value":2022}}}],"careFor":[{"id":{"@type":"g:Int32","@value":25},"outV":{"@type":"g:Int32","@value":13},"properties":{"specialty":"conservation"}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":20},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2023}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":38},"value":"red panda"}],"name":[{"id":{"@type":"g:Int64","@value":37},"value":"blaze"}],"weight":[{"id":{"@type":"g:Int64","@value":39},"value":{"@type":"g:Double","@value":5.4}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":41},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":42},"value":"bamboo"},{"id":{"@type":"g:Int64","@value":43},"value":"fruit"},{"id":{"@type":"g:Int64","@value":44},"value":"insects"}],"age":[{"id":{"@type":"g:Int64","@value":40},"value":{"@type":"g:Int32","@value":4}}]}} -{"id":{"@type":"g:Int32","@value":7},"label":["mammal","endangered","animal"],"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":23},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2019}}}],"friendsWith":[{"id":{"@type":"g:Int32","@value":30},"inV":{"@type":"g:Int32","@value":6},"properties":{"since":{"@type":"g:Int32","@value":2022}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":46},"value":"african elephant"}],"name":[{"id":{"@type":"g:Int64","@value":45},"value":"titan"}],"weight":[{"id":{"@type":"g:Int64","@value":47},"value":{"@type":"g:Double","@value":4000.0}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":49},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":50},"value":"grass"},{"id":{"@type":"g:Int64","@value":51},"value":"leaves"},{"id":{"@type":"g:Int64","@value":52},"value":"bark"}],"age":[{"id":{"@type":"g:Int64","@value":48},"value":{"@type":"g:Int32","@value":15}}]}} -{"id":{"@type":"g:Int32","@value":8},"label":["nocturnal","mammal","animal"],"inE":{"eats":[{"id":{"@type":"g:Int32","@value":32},"outV":{"@type":"g:Int32","@value":4}}],"avoids":[{"id":{"@type":"g:Int32","@value":33},"outV":{"@type":"g:Int32","@value":5}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":21},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2024}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":54},"value":"harvest mouse"}],"name":[{"id":{"@type":"g:Int64","@value":53},"value":"bitsy"}],"weight":[{"id":{"@type":"g:Int64","@value":55},"value":{"@type":"g:Double","@value":0.006}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":57},"value":true}],"diet":[{"id":{"@type":"g:Int64","@value":58},"value":"seeds"},{"id":{"@type":"g:Int64","@value":59},"value":"insects"}],"age":[{"id":{"@type":"g:Int64","@value":56},"value":{"@type":"g:Int32","@value":1}}]}} -{"id":{"@type":"g:Int32","@value":9},"label":["nocturnal","mammal","endangered","animal","aquatic"],"inE":{"careFor":[{"id":{"@type":"g:Int32","@value":26},"outV":{"@type":"g:Int32","@value":13},"properties":{"specialty":"conservation"}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":17},"inV":{"@type":"g:Int32","@value":11},"properties":{"since":{"@type":"g:Int32","@value":2023}}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":61},"value":"fishing cat"}],"name":[{"id":{"@type":"g:Int64","@value":60},"value":"splash"}],"weight":[{"id":{"@type":"g:Int64","@value":62},"value":{"@type":"g:Double","@value":8.2}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":64},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":65},"value":"fish"},{"id":{"@type":"g:Int64","@value":66},"value":"frogs"},{"id":{"@type":"g:Int64","@value":67},"value":"crustaceans"}],"age":[{"id":{"@type":"g:Int64","@value":63},"value":{"@type":"g:Int32","@value":2}}]}} -{"id":{"@type":"g:Int32","@value":10},"label":["nocturnal","mammal","endangered","animal"],"inE":{"careFor":[{"id":{"@type":"g:Int32","@value":27},"outV":{"@type":"g:Int32","@value":13},"properties":{"specialty":"conservation"}}]},"outE":{"livesIn":[{"id":{"@type":"g:Int32","@value":22},"inV":{"@type":"g:Int32","@value":12},"properties":{"since":{"@type":"g:Int32","@value":2020}}}],"eats":[{"id":{"@type":"g:Int32","@value":31},"inV":{"@type":"g:Int32","@value":4}}]},"properties":{"species":[{"id":{"@type":"g:Int64","@value":69},"value":"bengal tiger"}],"name":[{"id":{"@type":"g:Int64","@value":68},"value":"tinker"}],"weight":[{"id":{"@type":"g:Int64","@value":70},"value":{"@type":"g:Double","@value":220.0}}],"captiveBorn":[{"id":{"@type":"g:Int64","@value":72},"value":false}],"diet":[{"id":{"@type":"g:Int64","@value":73},"value":"deer"},{"id":{"@type":"g:Int64","@value":74},"value":"boar"},{"id":{"@type":"g:Int64","@value":75},"value":"snakes"}],"age":[{"id":{"@type":"g:Int64","@value":71},"value":{"@type":"g:Int32","@value":5}}]}} -{"id":{"@type":"g:Int32","@value":11},"label":["habitat","aquatic"],"inE":{"livesIn":[{"id":{"@type":"g:Int32","@value":16},"outV":{"@type":"g:Int32","@value":3},"properties":{"since":{"@type":"g:Int32","@value":2019}}},{"id":{"@type":"g:Int32","@value":17},"outV":{"@type":"g:Int32","@value":9},"properties":{"since":{"@type":"g:Int32","@value":2023}}},{"id":{"@type":"g:Int32","@value":14},"outV":{"@type":"g:Int32","@value":1},"properties":{"since":{"@type":"g:Int32","@value":2020}}},{"id":{"@type":"g:Int32","@value":15},"outV":{"@type":"g:Int32","@value":2},"properties":{"since":{"@type":"g:Int32","@value":2018}}}]},"properties":{"openAir":[{"id":{"@type":"g:Int64","@value":79},"value":true}],"biome":[{"id":{"@type":"g:Int64","@value":77},"value":"marine"}],"name":[{"id":{"@type":"g:Int64","@value":76},"value":"lagoon"}],"capacity":[{"id":{"@type":"g:Int64","@value":78},"value":{"@type":"g:Int32","@value":8}}]}} -{"id":{"@type":"g:Int32","@value":12},"label":["habitat"],"inE":{"livesIn":[{"id":{"@type":"g:Int32","@value":18},"outV":{"@type":"g:Int32","@value":4},"properties":{"since":{"@type":"g:Int32","@value":2021}}},{"id":{"@type":"g:Int32","@value":19},"outV":{"@type":"g:Int32","@value":5},"properties":{"since":{"@type":"g:Int32","@value":2022}}},{"id":{"@type":"g:Int32","@value":20},"outV":{"@type":"g:Int32","@value":6},"properties":{"since":{"@type":"g:Int32","@value":2023}}},{"id":{"@type":"g:Int32","@value":21},"outV":{"@type":"g:Int32","@value":8},"properties":{"since":{"@type":"g:Int32","@value":2024}}},{"id":{"@type":"g:Int32","@value":22},"outV":{"@type":"g:Int32","@value":10},"properties":{"since":{"@type":"g:Int32","@value":2020}}},{"id":{"@type":"g:Int32","@value":23},"outV":{"@type":"g:Int32","@value":7},"properties":{"since":{"@type":"g:Int32","@value":2019}}}]},"properties":{"openAir":[{"id":{"@type":"g:Int64","@value":83},"value":false}],"biome":[{"id":{"@type":"g:Int64","@value":81},"value":"tropical"}],"name":[{"id":{"@type":"g:Int64","@value":80},"value":"canopy"}],"capacity":[{"id":{"@type":"g:Int64","@value":82},"value":{"@type":"g:Int32","@value":8}}]}} -{"id":{"@type":"g:Int32","@value":13},"label":["person","veterinarian","keeper"],"outE":{"careFor":[{"id":{"@type":"g:Int32","@value":24},"inV":{"@type":"g:Int32","@value":2},"properties":{"specialty":"conservation"}},{"id":{"@type":"g:Int32","@value":25},"inV":{"@type":"g:Int32","@value":6},"properties":{"specialty":"conservation"}},{"id":{"@type":"g:Int32","@value":26},"inV":{"@type":"g:Int32","@value":9},"properties":{"specialty":"conservation"}},{"id":{"@type":"g:Int32","@value":27},"inV":{"@type":"g:Int32","@value":10},"properties":{"specialty":"conservation"}}]},"properties":{"specialties":[{"id":{"@type":"g:Int64","@value":86},"value":"conservation"},{"id":{"@type":"g:Int64","@value":87},"value":"surgery"},{"id":{"@type":"g:Int64","@value":88},"value":"nutrition"}],"name":[{"id":{"@type":"g:Int64","@value":84},"value":"dr_gremlin"}],"since":[{"id":{"@type":"g:Int64","@value":85},"value":{"@type":"g:Int32","@value":2015}}]}} diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/tinkerpop-zoo-v3.kryo b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/gryo/tinkerpop-zoo-v3.kryo new file mode 100644 index 0000000000000000000000000000000000000000..57af38c990d43c2dfd1a2bea6d5c33c31f6ebc75 GIT binary patch literal 3253 zcma)9-)kII7~T8L?y@taF_t!jQj#H*5JCtgh7dw%*CItlL_}0X)SKCx-P_F0OlR)K zwn(WWB1NRA6kA0^L_|dW1H>mOB2^0h1!98*OA5PrFyez}e(dbbcH4Dca%Zx0_nz~e z^L=OQ(k)~!_BzvOWglZ23#4Oh(@#^h8s3(|)#2`{K*Wj~TbKS#jR~j5ox1P^*4&Xz zej9&Jx44pjFk&0N#0P@sq`F14N@Z4Lw%iiIV>?8LX?!2i3KiK13p`;-(IK7iHeRy0 zV|v1=x8x3UcypWdg2q(qZ}zH?^~NsgMWed!N_bsm7WWkPJmLB~q*qFe)uh-h>xRZQ zWN=F#(b%Rh?c4e&P@Qhcn~c}7Uzv;t7}Hk|UVPB|=*s2#AS#Z=RD@^aw%XH}*4iFzPL*%))l-Wt!;f3JmDyDT^DI=DAbt@)a!t1gtE>+?G|>!J}aF+A)5$# zuC6mQ!T6RdYhd66z-F9&3?=YeQkm;~H&N~yTw^uvq}Zy)@!uYbt~|C!Qfw#VO?LH1 zaWY{k-j3m&pz;uS8qH?l{W?X9@$Eg#z83zsL?I0$_n_f!!=vCiQLoWx&)$o9d(VQY z3_DMtSfXZn+^PMZu=6qPba=E>(7J|YOcF?^k=(7J+Co|CT4Yo z@Dx>sFh#VGVG0J%a50)S_}v8Lw6Fk#eRPauA-@O(C9PlB!fSA+tBV)V`=#PLsW4tL zs^3-z7<@fNUJKh}*v>XIW(&S0I(j1{!~ZK5$SX8C1U93&4D1^y{8*pWyZVoHk7CD8 zuc9aPG;d^k_*}low#FLV$2e3b{2z21_$eGzs-Wj_$2flGef9mR^Z(?1;HN%NVFMSn zj=t4bUXN%9PNL}yP8eY+z8!3Y0_|s|fnDJ4>Z6`%5J;!mQeO7c`Kfj;>dOu3)J-dG z3uge%vw3hUt^aFA)!c}#JRuiYDu6c-Vxsx~X1MU(dPjc?E6Z;Mid%v^Nh3T@#i0bv zX9z+NTE0I)==1-RAV?$SWf{C=!qGgG(j6R?tV=Ee#5$4@w@oGMqMbB{xA9m>XA=*) zQ}7*jk1=?u$I^RvttLcU{~+Af8FC_s(KHJR-)p8g%yPD}YROwtL;FY&DAzl>Ddv=Q#& OUm*HdjDDEPI`ltD$$Jj~ literal 0 HcmV?d00001 diff --git a/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/HadoopGraphFeatureIntegrateTest.java b/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/HadoopGraphFeatureIntegrateTest.java index 91320b3a205..21bfd5bbc44 100644 --- a/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/HadoopGraphFeatureIntegrateTest.java +++ b/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/HadoopGraphFeatureIntegrateTest.java @@ -33,12 +33,10 @@ import org.apache.tinkerpop.gremlin.features.TestFiles; import org.apache.tinkerpop.gremlin.features.World; import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph; -import org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONInputFormat; import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoInputFormat; import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoOutputFormat; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; import org.javatuples.Pair; import org.junit.AssumptionViolatedException; import org.junit.runner.RunWith; @@ -91,7 +89,7 @@ public static class HadoopGraphWorld implements World { private static final HadoopGraph crew = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.CREW))); private static final HadoopGraph sink = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.SINK))); private static final HadoopGraph grateful = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.GRATEFUL))); - private static final HadoopGraph zoo = HadoopGraph.open(new MapConfiguration(getZooConfiguration())); + private static final HadoopGraph zoo = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.ZOO))); static { readIntoGraph(modern, GraphData.MODERN); @@ -148,22 +146,6 @@ private static Map getBaseConfiguration(final GraphData graphDat }}; } - /** - * The zoo graph is multi-label and only GraphSON V4 preserves the full label set for a vertex - Gryo (and - * GraphSON V1/V2/V3) can only carry a single label - so, unlike the other reference graphs, it is read via - * {@link GraphSONInputFormat} configured for {@link GraphSONVersion#V4_0} rather than Gryo. - */ - private static Map getZooConfiguration() { - return new HashMap() {{ - put(Graph.GRAPH, HadoopGraph.class.getName()); - put(Constants.GREMLIN_HADOOP_GRAPH_READER, GraphSONInputFormat.class.getCanonicalName()); - put(Constants.GREMLIN_HADOOP_GRAPHSON_VERSION, GraphSONVersion.V4_0.name()); - put(Constants.GREMLIN_HADOOP_GRAPH_WRITER, GryoOutputFormat.class.getCanonicalName()); - put(Constants.GREMLIN_HADOOP_OUTPUT_LOCATION, getWorkingDirectory()); - put(Constants.GREMLIN_HADOOP_JARS_IN_DISTRIBUTED_CACHE, false); - }}; - } - @Override public boolean useParametersLiterally() { return false; diff --git a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/SparkGraphFeatureIntegrateTest.java b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/SparkGraphFeatureIntegrateTest.java index 884b27ac8d8..3b5903d6f1c 100644 --- a/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/SparkGraphFeatureIntegrateTest.java +++ b/spark-gremlin/src/test/java/org/apache/tinkerpop/gremlin/spark/SparkGraphFeatureIntegrateTest.java @@ -115,6 +115,7 @@ public static class SparkGraphWorld implements World { private static final HadoopGraph crew = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.CREW))); private static final HadoopGraph sink = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.SINK))); private static final HadoopGraph grateful = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.GRATEFUL))); + private static final HadoopGraph zoo = HadoopGraph.open(new MapConfiguration(getBaseConfiguration(GraphData.ZOO))); static { readIntoGraph(modern, GraphData.MODERN); @@ -122,6 +123,7 @@ public static class SparkGraphWorld implements World { readIntoGraph(crew, GraphData.CREW); readIntoGraph(sink, GraphData.SINK); readIntoGraph(grateful, GraphData.GRATEFUL); + readIntoGraph(zoo, GraphData.ZOO); } @Override @@ -138,6 +140,8 @@ else if (graphData == GraphData.SINK) return sink.traversal().withComputer(Computer.compute(SparkGraphComputer.class).workers(AVAILABLE_PROCESSORS)); else if (graphData == GraphData.GRATEFUL) return grateful.traversal().withComputer(Computer.compute(SparkGraphComputer.class).workers(AVAILABLE_PROCESSORS)); + else if (graphData == GraphData.ZOO) + return zoo.traversal().withComputer(Computer.compute(SparkGraphComputer.class).workers(AVAILABLE_PROCESSORS)); else throw new UnsupportedOperationException("GraphData not supported: " + graphData.name()); } diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java index dad35c9ca6e..a30caa07172 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java @@ -143,6 +143,16 @@ public void shouldWriteSinkGraphAsGryoV3() throws IOException { os.close(); } + /** + * No assertions. Just write out the graph for convenience. + */ + @Test + public void shouldWriteTheZooGraphAsGryoV3() throws IOException { + final OutputStream os = new FileOutputStream(new File(tempPath, "tinkerpop-zoo-v3.kryo")); + GryoWriter.build().mapper(GryoMapper.build().version(GryoVersion.V3_0).create()).create().writeGraph(os, TinkerFactory.createTheZoo()); + os.close(); + } + /** * No assertions. Just write out the graph for convenience. */ From c14a9ddd18722e631de95ab0b410cc43ab832b68 Mon Sep 17 00:00:00 2001 From: Cole Greer Date: Tue, 14 Jul 2026 14:48:20 -0700 Subject: [PATCH 09/10] add additional feature tests for multi-label adjacent steps --- .../Gherkin/Gremlin.cs | 38 +++ gremlin-go/driver/cucumber/gremlin.go | 19 ++ .../test/cucumber/gremlin.js | 19 ++ .../src/main/python/tests/feature/gremlin.py | 19 ++ .../language/translator/translations.json | 323 ++++++++++++++++++ .../test/features/branch/Branch.feature | 25 ++ .../test/features/branch/Choose.feature | 39 +++ .../gremlin/test/features/filter/And.feature | 15 + .../test/features/filter/Dedup.feature | 15 +- .../gremlin/test/features/filter/Has.feature | 57 ++++ .../test/features/filter/HasLabel.feature | 44 ++- .../gremlin/test/features/filter/Not.feature | 14 + .../gremlin/test/features/filter/Or.feature | 16 +- .../gremlin/test/features/map/Order.feature | 15 +- .../test/features/sideEffect/Group.feature | 42 +++ .../features/sideEffect/GroupCount.feature | 29 ++ 16 files changed, 725 insertions(+), 4 deletions(-) diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 0d3049a462f..fd92274c4eb 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -84,6 +84,7 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Branch(__.Label().Is("person").Count()).Option(p["xx1"], __.Values("age")).Option(p["xx2"], __.Values("lang")).Option(p["xx2"], __.Values("name")).Option(Pick.Any, __.Label())}}, {"g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Branch(__.Values("age")).Option(P.Lt(30), __.Constant("young")).Option(P.Gt(30), __.Constant("old")).Option(Pick.None, __.Constant("on the edge"))}}, {"g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX", new List, ITraversal>> {(g,p) =>g.V().Branch(__.Identity()).Option(__.HasLabel("software"), __.In("created").Values("name").Order().Fold()).Option(__.Has("name", "vadas"), __.Values("age")).Option(P.Neq(123), __.BothE().Count())}}, + {"g_V_hasLabelXhabitat_reptileX_branchXidentityX_optionXhasLabelXaquaticXX_optionXhasLabelXreptileXX_optionXhasLabelXhabitatXX_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat", "reptile").Branch(__.Identity()).Option(__.HasLabel("aquatic"), __.Constant("wet")).Option(__.HasLabel("reptile"), __.Constant("scaly")).Option(__.HasLabel("habitat"), __.Constant("place"))}}, {"g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX", new List, ITraversal>> {(g,p) =>g.V().Choose(__.Out().Count()).Option(p["xx1"], __.Values("name")).Option(p["xx2"], __.Values("age"))}}, {"g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX_optionXnone_discardX", new List, ITraversal>> {(g,p) =>g.V().Choose(__.Out().Count()).Option(p["xx1"], __.Values("name")).Option(p["xx2"], __.Values("age")).Option(Pick.None, __.Discard())}}, {"g_V_chooseXhasLabelXpersonX_and_outXcreatedX__outXknowsX_identityX_name", new List, ITraversal>> {(g,p) =>g.V().Choose(__.HasLabel("person").And().Out("created"), __.Out("knows"), __.Identity()).Values("name")}}, @@ -123,6 +124,8 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values("age").Choose(P.Eq(29), __.Constant("matched"))}}, {"g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_isXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Choose(__.Values("name")).Option(__.Is("marko"), __.Values("age")).Option(Pick.None, __.Values("name"))}}, {"g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_PeqXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Choose(__.Values("name")).Option(P.Eq("marko"), __.Values("age")).Option(Pick.None, __.Values("name"))}}, + {"g_V_hasLabelXhabitat_reptileX_chooseXhasLabelXaquaticX_constantXwetX_constantXdryXX_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat", "reptile").Choose(__.HasLabel("aquatic"), __.Constant("wet"), __.Constant("dry"))}}, + {"g_V_hasXname_tuxX_chooseXlabelX_optionXanimal_bird_aquatic_endangeredX_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Choose(T.Label).Option("animal", __.Constant("is-animal")).Option("bird", __.Constant("is-bird")).Option("aquatic", __.Constant("is-aquatic")).Option("endangered", __.Constant("is-endangered"))}}, {"g_V_localXpropertiesXlocationX_order_byXvalueX_limitX2XX_value", new List, ITraversal>> {(g,p) =>g.V().Local(__.Properties("location").Order().By(T.Value, Order.Asc).Range(0, 2)).Value()}}, {"g_V_hasXlabel_personX_asXaX_localXoutXcreatedX_asXbXX_selectXa_bX_byXnameX_byXidX", new List, ITraversal>> {(g,p) =>g.V().Has(T.Label, "person").As("a").Local(__.Out("created").As("b")).Select("a", "b").By("name").By(T.Id)}}, {"g_V_localXoutE_countX", new List, ITraversal>> {(g,p) =>g.V().Local(__.OutE().Count())}}, @@ -378,6 +381,7 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().As("a").Out("knows").And().Out("created").In("created").As("a").Values("name")}}, {"g_V_asXaX_andXselectXaX_selectXaXX", new List, ITraversal>> {(g,p) =>g.V().As("a").And(__.Select("a"), __.Select("a"))}}, {"g_V_hasXname_markoX_and_hasXname_markoX_and_hasXname_markoX", new List, ITraversal>> {(g,p) =>g.V().Has("name", "marko").And().Has("name", "marko").And().Has("name", "marko")}}, + {"g_V_andXhasLabelXmammalX_hasLabelXendangeredXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().And(__.HasLabel("mammal"), __.HasLabel("endangered")).Values("name")}}, {"g_V_valuesXageX_anyXgtX32XX", new List, ITraversal>> {(g,p) =>g.V().Values("age").Any(P.Gt(32))}}, {"g_V_valuesXageX_order_byXdescX_fold_anyXeqX29XX", new List, ITraversal>> {(g,p) =>g.V().Values("age").Order().By(Order.Desc).Fold().Any(P.Eq(29))}}, {"g_V_valuesXageX_order_byXdescX_fold_anyXgtX10XX", new List, ITraversal>> {(g,p) =>g.V().Values("age").Order().By(Order.Desc).Fold().Any(P.Gt(10))}}, @@ -427,6 +431,7 @@ private static IDictionary, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "josh").AddV((string) "person").Property("name", "josh").AddV((string) "person").Property("name", "josh"), (g,p) =>g.V().Properties("name").Dedup().Count()}}, {"g_V_properties_dedup_byXvalueX_count", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "josh").AddV((string) "person").Property("name", "josh").AddV((string) "person").Property("name", "josh"), (g,p) =>g.V().Properties("name").Dedup().By(T.Value).Count()}}, {"g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name", new List, ITraversal>> {(g,p) =>g.V().Both().Has(T.Label, "software").Dedup().By("lang").By("name").Values("name")}}, + {"g_V_hasLabelXhabitatX_dedup_byXlabels_order_foldX_localXlabels_order_foldX_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat").Dedup().By(__.Labels().Order().Fold()).Local(__.Labels().Order().Fold())}}, {"g_V_count_discard", new List, ITraversal>> {(g,p) =>g.V().Count().Discard()}}, {"g_V_hasLabelXpersonX_discard", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Discard()}}, {"g_VX1X_outXcreatedX_discard", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out("created").Discard()}}, @@ -532,6 +537,9 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").V().Has("age", P.Gt(__.Select("a").Values("age"))).Values("name")}}, {"g_withSackX29X_V_hasXage_gtXsackXX_valuesXnameX", new List, ITraversal>> {(g,p) =>g.WithSack(29).V().Has("age", P.Gt(__.Sack())).Values("name")}}, {"g_V_hasXname_markoX_outEXknowsX_hasXweight_gteXconstantX05XXX", new List, ITraversal>> {(g,p) =>g.V().Has("name", "marko").OutE("knows").Has("weight", P.Gte(__.Constant(0.5)))}}, + {"g_V_hasXlabel_aquaticX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has(T.Label, "aquatic").Values("name")}}, + {"g_V_hasXlabel_withinXreptile_birdXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has(T.Label, P.Within("reptile", "bird")).Values("name")}}, + {"g_V_hasXlabel_withoutXanimalXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has(T.Label, P.Without("animal")).Values("name")}}, {"g_V_hasIdXemptyX_count", new List, ITraversal>> {(g,p) =>g.V().HasId(p["xx1"]).Count()}}, {"g_V_hasIdXwithinXemptyXX_count", new List, ITraversal>> {(g,p) =>g.V().HasId(P.Within(new List { })).Count()}}, {"g_V_hasIdXwithoutXemptyXX_count", new List, ITraversal>> {(g,p) =>g.V().HasId(P.Without(new List { })).Count()}}, @@ -579,6 +587,9 @@ private static IDictionary, ITraversal>> {(g,p) =>g.E().HasLabel((string) null)}}, {"g_E_hasXlabel_nullX", new List, ITraversal>> {(g,p) =>g.E().Has(T.Label, (object) null)}}, {"g_V_properties_hasLabelXnullX", new List, ITraversal>> {(g,p) =>g.V().Properties().HasLabel((string) null)}}, + {"g_V_hasLabelXbirdX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("bird").Values("name")}}, + {"g_V_hasLabelXaquaticX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("aquatic").Values("name")}}, + {"g_V_hasLabelXreptile_birdX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("reptile", "bird").Values("name")}}, {"g_V_hasNotXageX_name", new List, ITraversal>> {(g,p) =>g.V().HasNot("age").Values("name")}}, {"g_V_properties_hasValueXnullX", new List, ITraversal>> {(g,p) =>g.V().Properties().HasValue((object) null)}}, {"g_V_properties_hasValueXnull_nullX", new List, ITraversal>> {(g,p) =>g.V().Properties().HasValue(null, null)}}, @@ -629,9 +640,11 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Not(__.Has("age", P.Gt(27))).Values("name")}}, {"g_V_notXnotXhasXage_gt_27XXX_name", new List, ITraversal>> {(g,p) =>g.V().Not(__.Not(__.Has("age", P.Gt(27)))).Values("name")}}, {"g_V_notXhasXname_gt_27XX_name", new List, ITraversal>> {(g,p) =>g.V().Not(__.Has("name", P.Gt(27))).Values("name")}}, + {"g_V_notXhasLabelXanimalXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Not(__.HasLabel("animal")).Values("name")}}, {"g_V_orXhasXage_gt_27X__outE_count_gte_2X_name", new List, ITraversal>> {(g,p) =>g.V().Or(__.Has("age", P.Gt(27)), __.OutE().Count().Is(P.Gte(2))).Values("name")}}, {"g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name", new List, ITraversal>> {(g,p) =>g.V().Or(__.OutE("knows"), __.Has(T.Label, "software").Or().Has("age", P.Gte(35))).Values("name")}}, {"g_V_asXaX_orXselectXaX_selectXaXX", new List, ITraversal>> {(g,p) =>g.V().As("a").Or(__.Select("a"), __.Select("a"))}}, + {"g_V_orXhasLabelXbirdX_hasLabelXreptileXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Or(__.HasLabel("bird"), __.HasLabel("reptile")).Values("name")}}, {"g_VX1X_out_limitX2X", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Limit(2)}}, {"g_VX1X_out_limitX2varX", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Limit((long) p["xx1"])}}, {"g_V_localXoutE_limitX1X_inVX_limitX3X", new List, ITraversal>> {(g,p) =>g.V().Local(__.OutE().Limit(1)).InV().Limit(3)}}, @@ -1708,6 +1721,7 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V(p["vid1"]).ElementMap().Order(Scope.Local).By(Column.Keys, Order.Desc).Unfold()}}, {"g_VX1X_elementMap_orderXlocalX_byXkeys_ascXunfold", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).ElementMap().Order(Scope.Local).By(Column.Keys, Order.Asc).Unfold()}}, {"g_VX1X_valuesXageX_orderXlocalX", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values("age").Order(Scope.Local)}}, + {"g_V_hasLabelXhabitatX_order_byXlabels_order_foldX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat").Order().By(__.Labels().Order().Fold()).Values("name")}}, {"g_V_pageRank_hasXpageRankX", new List, ITraversal>> {(g,p) =>g.V().PageRank().Has("gremlin.pageRankVertexProgram.pageRank")}}, {"g_V_outXcreatedX_pageRank_withXedges_bothEX_withXpropertyName_projectRankX_withXtimes_0X_valueMapXname_projectRankX", new List, ITraversal>> {(g,p) =>g.V().Out("created").PageRank().With("~tinkerpop.pageRank.edges", __.BothE()).With("~tinkerpop.pageRank.propertyName", "projectRank").With("~tinkerpop.pageRank.times", 0).ValueMap("name", "projectRank")}}, {"g_V_pageRank_order_byXpageRank_descX_byXnameX_name", new List, ITraversal>> {(g,p) =>g.V().PageRank().Order().By("gremlin.pageRankVertexProgram.pageRank", Order.Desc).By("name").Values("name")}}, @@ -2277,6 +2291,9 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Group("a").By("name").By().Select("a").Count(Scope.Local)}}, {"g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX", new List, ITraversal>> {(g,p) =>g.V().Local(__.Group("a").By("name").By().Select("a").Count(Scope.Local))}}, {"g_V_group_byXvaluesXnameXX_byXboth_countX", new List, ITraversal>> {(g,p) =>g.V().Group().By(__.Values("name")).By(__.Both().Count())}}, + {"g_V_hasLabelXhabitatX_group_byXlabelX_byXcountX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat").Group().By(T.Label).By(__.Count())}}, + {"g_V_hasXname_tuxX_group_byXlabelX_byXcountX", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Group().By(__.Label()).By(__.Count())}}, + {"g_V_hasLabelXreptileX_group_byXlabels_order_foldX_byXname_foldX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("reptile").Group().By(__.Labels().Order().Fold()).By(__.Values("name").Fold())}}, {"g_V_outXcreatedX_groupCount_byXnameX", new List, ITraversal>> {(g,p) =>g.V().Out("created").GroupCount().By("name")}}, {"g_V_groupCount_byXageX", new List, ITraversal>> {(g,p) =>g.V().GroupCount().By("age")}}, {"g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX", new List, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy()).V().GroupCount().By("age")}}, @@ -2297,6 +2314,8 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Out("created").GroupCount("x").By("name").By("age")}}, {"g_V_groupCountXaX_selectXaX_countXlocalX", new List, ITraversal>> {(g,p) =>g.V().GroupCount("a").Select("a").Count(Scope.Local)}}, {"g_V_localXgroupCountXaX_selectXaX_countXlocalXX", new List, ITraversal>> {(g,p) =>g.V().Local(__.GroupCount("a").Select("a").Count(Scope.Local))}}, + {"g_V_hasXname_tuxX_groupCount_byXlabelX", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").GroupCount().By(T.Label)}}, + {"g_V_labels_groupCount", new List, ITraversal>> {(g,p) =>g.V().Labels().GroupCount()}}, {"g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Values("name").Inject("daniel").As("a").Map(__.Length()).Path()}}, {"g_injectXnull_1_3_nullX", new List, ITraversal>> {(g,p) =>g.Inject(null, 1, 3, null)}}, {"g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX", new List, ITraversal>> {(g,p) =>g.Inject(10, 20, null, 20, 10, 10).GroupCount("x").Dedup().As("y").Project("a", "b").By().By(__.Select("x").Select(__.Select("y")))}}, @@ -2414,6 +2433,7 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Branch(__.Label().Is("person").Count()).Option(p["xx1"], __.Values("age")).Option(p["xx2"], __.Values("lang")).Option(p["xx2"], __.Values("name")).Option(Pick.Any, __.Label())}}, {"g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Branch(__.Values("age")).Option(P.Lt(30), __.Constant("young")).Option(P.Gt(30), __.Constant("old")).Option(Pick.None, __.Constant("on the edge"))}}, {"g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX", new List, ITraversal>> {(g,p) =>g.V().Branch(__.Identity()).Option(__.HasLabel("software"), __.In("created").Values("name").Order().Fold()).Option(__.Has("name", "vadas"), __.Values("age")).Option(P.Neq(123), __.BothE().Count())}}, + {"g_V_hasLabelXhabitat_reptileX_branchXidentityX_optionXhasLabelXaquaticXX_optionXhasLabelXreptileXX_optionXhasLabelXhabitatXX_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat", "reptile").Branch(__.Identity()).Option(__.HasLabel("aquatic"), __.Constant("wet")).Option(__.HasLabel("reptile"), __.Constant("scaly")).Option(__.HasLabel("habitat"), __.Constant("place"))}}, {"g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX", new List, ITraversal>> {(g,p) =>g.V().Choose(__.Out().Count()).Option(p["xx1"], __.Values("name")).Option(p["xx2"], __.Values("age"))}}, {"g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX_optionXnone_discardX", new List, ITraversal>> {(g,p) =>g.V().Choose(__.Out().Count()).Option(p["xx1"], __.Values("name")).Option(p["xx2"], __.Values("age")).Option(Pick.None, __.Discard())}}, {"g_V_chooseXhasLabelXpersonX_and_outXcreatedX__outXknowsX_identityX_name", new List, ITraversal>> {(g,p) =>g.V().Choose(__.HasLabel("person").And().Out("created"), __.Out("knows"), __.Identity()).Values("name")}}, @@ -2453,6 +2473,8 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values("age").Choose(P.Eq(29), __.Constant("matched"))}}, {"g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_isXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Choose(__.Values("name")).Option(__.Is("marko"), __.Values("age")).Option(Pick.None, __.Values("name"))}}, {"g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_PeqXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Choose(__.Values("name")).Option(P.Eq("marko"), __.Values("age")).Option(Pick.None, __.Values("name"))}}, + {"g_V_hasLabelXhabitat_reptileX_chooseXhasLabelXaquaticX_constantXwetX_constantXdryXX_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat", "reptile").Choose(__.HasLabel("aquatic"), __.Constant("wet"), __.Constant("dry"))}}, + {"g_V_hasXname_tuxX_chooseXlabelX_optionXanimal_bird_aquatic_endangeredX_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Choose(T.Label).Option("animal", __.Constant("is-animal")).Option("bird", __.Constant("is-bird")).Option("aquatic", __.Constant("is-aquatic")).Option("endangered", __.Constant("is-endangered"))}}, {"g_V_localXpropertiesXlocationX_order_byXvalueX_limitX2XX_value", new List, ITraversal>> {(g,p) =>g.V().Local(__.Properties("location").Order().By(T.Value, Order.Asc).Range(0, 2)).Value()}}, {"g_V_hasXlabel_personX_asXaX_localXoutXcreatedX_asXbXX_selectXa_bX_byXnameX_byXidX", new List, ITraversal>> {(g,p) =>g.V().Has(T.Label, "person").As("a").Local(__.Out("created").As("b")).Select("a", "b").By("name").By(T.Id)}}, {"g_V_localXoutE_countX", new List, ITraversal>> {(g,p) =>g.V().Local(__.OutE().Count())}}, @@ -2708,6 +2730,7 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().As("a").Out("knows").And().Out("created").In("created").As("a").Values("name")}}, {"g_V_asXaX_andXselectXaX_selectXaXX", new List, ITraversal>> {(g,p) =>g.V().As("a").And(__.Select("a"), __.Select("a"))}}, {"g_V_hasXname_markoX_and_hasXname_markoX_and_hasXname_markoX", new List, ITraversal>> {(g,p) =>g.V().Has("name", "marko").And().Has("name", "marko").And().Has("name", "marko")}}, + {"g_V_andXhasLabelXmammalX_hasLabelXendangeredXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().And(__.HasLabel("mammal"), __.HasLabel("endangered")).Values("name")}}, {"g_V_valuesXageX_anyXgtX32XX", new List, ITraversal>> {(g,p) =>g.V().Values("age").Any(P.Gt(32))}}, {"g_V_valuesXageX_order_byXdescX_fold_anyXeqX29XX", new List, ITraversal>> {(g,p) =>g.V().Values("age").Order().By(Order.Desc).Fold().Any(P.Eq(29))}}, {"g_V_valuesXageX_order_byXdescX_fold_anyXgtX10XX", new List, ITraversal>> {(g,p) =>g.V().Values("age").Order().By(Order.Desc).Fold().Any(P.Gt(10))}}, @@ -2757,6 +2780,7 @@ private static IDictionary, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "josh").AddV((string) "person").Property("name", "josh").AddV((string) "person").Property("name", "josh"), (g,p) =>g.V().Properties("name").Dedup().Count()}}, {"g_V_properties_dedup_byXvalueX_count", new List, ITraversal>> {(g,p) =>g.AddV((string) "person").Property("name", "josh").AddV((string) "person").Property("name", "josh").AddV((string) "person").Property("name", "josh"), (g,p) =>g.V().Properties("name").Dedup().By(T.Value).Count()}}, {"g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name", new List, ITraversal>> {(g,p) =>g.V().Both().Has(T.Label, "software").Dedup().By("lang").By("name").Values("name")}}, + {"g_V_hasLabelXhabitatX_dedup_byXlabels_order_foldX_localXlabels_order_foldX_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat").Dedup().By(__.Labels().Order().Fold()).Local(__.Labels().Order().Fold())}}, {"g_V_count_discard", new List, ITraversal>> {(g,p) =>g.V().Count().Discard()}}, {"g_V_hasLabelXpersonX_discard", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Discard()}}, {"g_VX1X_outXcreatedX_discard", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out("created").Discard()}}, @@ -2862,6 +2886,9 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V(p["vid1"]).As("a").V().Has("age", P.Gt(__.Select("a").Values("age"))).Values("name")}}, {"g_withSackX29X_V_hasXage_gtXsackXX_valuesXnameX", new List, ITraversal>> {(g,p) =>g.WithSack(29).V().Has("age", P.Gt(__.Sack())).Values("name")}}, {"g_V_hasXname_markoX_outEXknowsX_hasXweight_gteXconstantX05XXX", new List, ITraversal>> {(g,p) =>g.V().Has("name", "marko").OutE("knows").Has("weight", P.Gte(__.Constant(0.5)))}}, + {"g_V_hasXlabel_aquaticX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has(T.Label, "aquatic").Values("name")}}, + {"g_V_hasXlabel_withinXreptile_birdXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has(T.Label, P.Within("reptile", "bird")).Values("name")}}, + {"g_V_hasXlabel_withoutXanimalXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Has(T.Label, P.Without("animal")).Values("name")}}, {"g_V_hasIdXemptyX_count", new List, ITraversal>> {(g,p) =>g.V().HasId(p["xx1"]).Count()}}, {"g_V_hasIdXwithinXemptyXX_count", new List, ITraversal>> {(g,p) =>g.V().HasId(P.Within(new List { })).Count()}}, {"g_V_hasIdXwithoutXemptyXX_count", new List, ITraversal>> {(g,p) =>g.V().HasId(P.Without(new List { })).Count()}}, @@ -2909,6 +2936,9 @@ private static IDictionary, ITraversal>> {(g,p) =>g.E().HasLabel((string) null)}}, {"g_E_hasXlabel_nullX", new List, ITraversal>> {(g,p) =>g.E().Has(T.Label, (object) null)}}, {"g_V_properties_hasLabelXnullX", new List, ITraversal>> {(g,p) =>g.V().Properties().HasLabel((string) null)}}, + {"g_V_hasLabelXbirdX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("bird").Values("name")}}, + {"g_V_hasLabelXaquaticX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("aquatic").Values("name")}}, + {"g_V_hasLabelXreptile_birdX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("reptile", "bird").Values("name")}}, {"g_V_hasNotXageX_name", new List, ITraversal>> {(g,p) =>g.V().HasNot("age").Values("name")}}, {"g_V_properties_hasValueXnullX", new List, ITraversal>> {(g,p) =>g.V().Properties().HasValue((object) null)}}, {"g_V_properties_hasValueXnull_nullX", new List, ITraversal>> {(g,p) =>g.V().Properties().HasValue(null, null)}}, @@ -2959,9 +2989,11 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Not(__.Has("age", P.Gt(27))).Values("name")}}, {"g_V_notXnotXhasXage_gt_27XXX_name", new List, ITraversal>> {(g,p) =>g.V().Not(__.Not(__.Has("age", P.Gt(27)))).Values("name")}}, {"g_V_notXhasXname_gt_27XX_name", new List, ITraversal>> {(g,p) =>g.V().Not(__.Has("name", P.Gt(27))).Values("name")}}, + {"g_V_notXhasLabelXanimalXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Not(__.HasLabel("animal")).Values("name")}}, {"g_V_orXhasXage_gt_27X__outE_count_gte_2X_name", new List, ITraversal>> {(g,p) =>g.V().Or(__.Has("age", P.Gt(27)), __.OutE().Count().Is(P.Gte(2))).Values("name")}}, {"g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name", new List, ITraversal>> {(g,p) =>g.V().Or(__.OutE("knows"), __.Has(T.Label, "software").Or().Has("age", P.Gte(35))).Values("name")}}, {"g_V_asXaX_orXselectXaX_selectXaXX", new List, ITraversal>> {(g,p) =>g.V().As("a").Or(__.Select("a"), __.Select("a"))}}, + {"g_V_orXhasLabelXbirdX_hasLabelXreptileXX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().Or(__.HasLabel("bird"), __.HasLabel("reptile")).Values("name")}}, {"g_VX1X_out_limitX2X", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Limit(2)}}, {"g_VX1X_out_limitX2varX", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Limit(new GValue("xx1", (long) p["xx1"]))}}, {"g_V_localXoutE_limitX1X_inVX_limitX3X", new List, ITraversal>> {(g,p) =>g.V().Local(__.OutE().Limit(1)).InV().Limit(3)}}, @@ -4038,6 +4070,7 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V(p["vid1"]).ElementMap().Order(Scope.Local).By(Column.Keys, Order.Desc).Unfold()}}, {"g_VX1X_elementMap_orderXlocalX_byXkeys_ascXunfold", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).ElementMap().Order(Scope.Local).By(Column.Keys, Order.Asc).Unfold()}}, {"g_VX1X_valuesXageX_orderXlocalX", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values("age").Order(Scope.Local)}}, + {"g_V_hasLabelXhabitatX_order_byXlabels_order_foldX_name_multilabel", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat").Order().By(__.Labels().Order().Fold()).Values("name")}}, {"g_V_pageRank_hasXpageRankX", new List, ITraversal>> {(g,p) =>g.V().PageRank().Has("gremlin.pageRankVertexProgram.pageRank")}}, {"g_V_outXcreatedX_pageRank_withXedges_bothEX_withXpropertyName_projectRankX_withXtimes_0X_valueMapXname_projectRankX", new List, ITraversal>> {(g,p) =>g.V().Out("created").PageRank().With("~tinkerpop.pageRank.edges", __.BothE()).With("~tinkerpop.pageRank.propertyName", "projectRank").With("~tinkerpop.pageRank.times", 0).ValueMap("name", "projectRank")}}, {"g_V_pageRank_order_byXpageRank_descX_byXnameX_name", new List, ITraversal>> {(g,p) =>g.V().PageRank().Order().By("gremlin.pageRankVertexProgram.pageRank", Order.Desc).By("name").Values("name")}}, @@ -4607,6 +4640,9 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Group("a").By("name").By().Select("a").Count(Scope.Local)}}, {"g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX", new List, ITraversal>> {(g,p) =>g.V().Local(__.Group("a").By("name").By().Select("a").Count(Scope.Local))}}, {"g_V_group_byXvaluesXnameXX_byXboth_countX", new List, ITraversal>> {(g,p) =>g.V().Group().By(__.Values("name")).By(__.Both().Count())}}, + {"g_V_hasLabelXhabitatX_group_byXlabelX_byXcountX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("habitat").Group().By(T.Label).By(__.Count())}}, + {"g_V_hasXname_tuxX_group_byXlabelX_byXcountX", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").Group().By(__.Label()).By(__.Count())}}, + {"g_V_hasLabelXreptileX_group_byXlabels_order_foldX_byXname_foldX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("reptile").Group().By(__.Labels().Order().Fold()).By(__.Values("name").Fold())}}, {"g_V_outXcreatedX_groupCount_byXnameX", new List, ITraversal>> {(g,p) =>g.V().Out("created").GroupCount().By("name")}}, {"g_V_groupCount_byXageX", new List, ITraversal>> {(g,p) =>g.V().GroupCount().By("age")}}, {"g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX", new List, ITraversal>> {(g,p) =>g.WithStrategies(new ProductiveByStrategy()).V().GroupCount().By("age")}}, @@ -4627,6 +4663,8 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Out("created").GroupCount("x").By("name").By("age")}}, {"g_V_groupCountXaX_selectXaX_countXlocalX", new List, ITraversal>> {(g,p) =>g.V().GroupCount("a").Select("a").Count(Scope.Local)}}, {"g_V_localXgroupCountXaX_selectXaX_countXlocalXX", new List, ITraversal>> {(g,p) =>g.V().Local(__.GroupCount("a").Select("a").Count(Scope.Local))}}, + {"g_V_hasXname_tuxX_groupCount_byXlabelX", new List, ITraversal>> {(g,p) =>g.V().Has("name", "tux").GroupCount().By(T.Label)}}, + {"g_V_labels_groupCount", new List, ITraversal>> {(g,p) =>g.V().Labels().GroupCount()}}, {"g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path", new List, ITraversal>> {(g,p) =>g.V(p["vid1"]).Out().Values("name").Inject("daniel").As("a").Map(__.Length()).Path()}}, {"g_injectXnull_1_3_nullX", new List, ITraversal>> {(g,p) =>g.Inject(null, 1, 3, null)}}, {"g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX", new List, ITraversal>> {(g,p) =>g.Inject(10, 20, null, 20, 10, 10).GroupCount("x").Dedup().As("y").Project("a", "b").By().By(__.Select("x").Select(__.Select("y")))}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index d519063a7fb..4bd1884d5b5 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -37,6 +37,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_branchXlabel_isXpersonX_countX_optionX1__ageX_optionX0__langX_optionX0__nameX_optionXany__labelX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Branch(gremlingo.T__.Label().Is("person").Count()).Option(p["xx1"], gremlingo.T__.Values("age")).Option(p["xx2"], gremlingo.T__.Values("lang")).Option(p["xx2"], gremlingo.T__.Values("name")).Option(gremlingo.Pick.Any, gremlingo.T__.Label())}}, "g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Branch(gremlingo.T__.Values("age")).Option(gremlingo.P.Lt(30), gremlingo.T__.Constant("young")).Option(gremlingo.P.Gt(30), gremlingo.T__.Constant("old")).Option(gremlingo.Pick.None, gremlingo.T__.Constant("on the edge"))}}, "g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Branch(gremlingo.T__.Identity()).Option(gremlingo.T__.HasLabel("software"), gremlingo.T__.In("created").Values("name").Order().Fold()).Option(gremlingo.T__.Has("name", "vadas"), gremlingo.T__.Values("age")).Option(gremlingo.P.Neq(123), gremlingo.T__.BothE().Count())}}, + "g_V_hasLabelXhabitat_reptileX_branchXidentityX_optionXhasLabelXaquaticXX_optionXhasLabelXreptileXX_optionXhasLabelXhabitatXX_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("habitat", "reptile").Branch(gremlingo.T__.Identity()).Option(gremlingo.T__.HasLabel("aquatic"), gremlingo.T__.Constant("wet")).Option(gremlingo.T__.HasLabel("reptile"), gremlingo.T__.Constant("scaly")).Option(gremlingo.T__.HasLabel("habitat"), gremlingo.T__.Constant("place"))}}, "g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Choose(gremlingo.T__.Out().Count()).Option(p["xx1"], gremlingo.T__.Values("name")).Option(p["xx2"], gremlingo.T__.Values("age"))}}, "g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX_optionXnone_discardX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Choose(gremlingo.T__.Out().Count()).Option(p["xx1"], gremlingo.T__.Values("name")).Option(p["xx2"], gremlingo.T__.Values("age")).Option(gremlingo.Pick.None, gremlingo.T__.Discard())}}, "g_V_chooseXhasLabelXpersonX_and_outXcreatedX__outXknowsX_identityX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Choose(gremlingo.T__.HasLabel("person").And().Out("created"), gremlingo.T__.Out("knows"), gremlingo.T__.Identity()).Values("name")}}, @@ -76,6 +77,8 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_hasLabelXpersonX_age_chooseXP_eqX29X_constantXmatchedX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("age").Choose(gremlingo.P.Eq(29), gremlingo.T__.Constant("matched"))}}, "g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_isXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Choose(gremlingo.T__.Values("name")).Option(gremlingo.T__.Is("marko"), gremlingo.T__.Values("age")).Option(gremlingo.Pick.None, gremlingo.T__.Values("name"))}}, "g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_PeqXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Choose(gremlingo.T__.Values("name")).Option(gremlingo.P.Eq("marko"), gremlingo.T__.Values("age")).Option(gremlingo.Pick.None, gremlingo.T__.Values("name"))}}, + "g_V_hasLabelXhabitat_reptileX_chooseXhasLabelXaquaticX_constantXwetX_constantXdryXX_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("habitat", "reptile").Choose(gremlingo.T__.HasLabel("aquatic"), gremlingo.T__.Constant("wet"), gremlingo.T__.Constant("dry"))}}, + "g_V_hasXname_tuxX_chooseXlabelX_optionXanimal_bird_aquatic_endangeredX_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "tux").Choose(gremlingo.T.Label).Option("animal", gremlingo.T__.Constant("is-animal")).Option("bird", gremlingo.T__.Constant("is-bird")).Option("aquatic", gremlingo.T__.Constant("is-aquatic")).Option("endangered", gremlingo.T__.Constant("is-endangered"))}}, "g_V_localXpropertiesXlocationX_order_byXvalueX_limitX2XX_value": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.Properties("location").Order().By(gremlingo.T.Value, gremlingo.Order.Asc).Range(0, 2)).Value()}}, "g_V_hasXlabel_personX_asXaX_localXoutXcreatedX_asXbXX_selectXa_bX_byXnameX_byXidX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has(gremlingo.T.Label, "person").As("a").Local(gremlingo.T__.Out("created").As("b")).Select("a", "b").By("name").By(gremlingo.T.Id)}}, "g_V_localXoutE_countX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.OutE().Count())}}, @@ -331,6 +334,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_asXaX_outXknowsX_and_outXcreatedX_inXcreatedX_asXaX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().As("a").Out("knows").And().Out("created").In("created").As("a").Values("name")}}, "g_V_asXaX_andXselectXaX_selectXaXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().As("a").And(gremlingo.T__.Select("a"), gremlingo.T__.Select("a"))}}, "g_V_hasXname_markoX_and_hasXname_markoX_and_hasXname_markoX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "marko").And().Has("name", "marko").And().Has("name", "marko")}}, + "g_V_andXhasLabelXmammalX_hasLabelXendangeredXX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().And(gremlingo.T__.HasLabel("mammal"), gremlingo.T__.HasLabel("endangered")).Values("name")}}, "g_V_valuesXageX_anyXgtX32XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Any(gremlingo.P.Gt(32))}}, "g_V_valuesXageX_order_byXdescX_fold_anyXeqX29XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Order().By(gremlingo.Order.Desc).Fold().Any(gremlingo.P.Eq(29))}}, "g_V_valuesXageX_order_byXdescX_fold_anyXgtX10XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Order().By(gremlingo.Order.Desc).Fold().Any(gremlingo.P.Gt(10))}}, @@ -380,6 +384,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_properties_dedup_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "josh").AddV("person").Property("name", "josh").AddV("person").Property("name", "josh")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties("name").Dedup().Count()}}, "g_V_properties_dedup_byXvalueX_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "josh").AddV("person").Property("name", "josh").AddV("person").Property("name", "josh")}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties("name").Dedup().By(gremlingo.T.Value).Count()}}, "g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Both().Has(gremlingo.T.Label, "software").Dedup().By("lang").By("name").Values("name")}}, + "g_V_hasLabelXhabitatX_dedup_byXlabels_order_foldX_localXlabels_order_foldX_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("habitat").Dedup().By(gremlingo.T__.Labels().Order().Fold()).Local(gremlingo.T__.Labels().Order().Fold())}}, "g_V_count_discard": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Count().Discard()}}, "g_V_hasLabelXpersonX_discard": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Discard()}}, "g_VX1X_outXcreatedX_discard": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Out("created").Discard()}}, @@ -485,6 +490,9 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_VXvid1X_asXaX_V_hasXage_gtXselectXaX_valuesXageXXX_valuesXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).As("a").V().Has("age", gremlingo.P.Gt(gremlingo.T__.Select("a").Values("age"))).Values("name")}}, "g_withSackX29X_V_hasXage_gtXsackXX_valuesXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithSack(29).V().Has("age", gremlingo.P.Gt(gremlingo.T__.Sack())).Values("name")}}, "g_V_hasXname_markoX_outEXknowsX_hasXweight_gteXconstantX05XXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "marko").OutE("knows").Has("weight", gremlingo.P.Gte(gremlingo.T__.Constant(0.5)))}}, + "g_V_hasXlabel_aquaticX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has(gremlingo.T.Label, "aquatic").Values("name")}}, + "g_V_hasXlabel_withinXreptile_birdXX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has(gremlingo.T.Label, gremlingo.P.Within("reptile", "bird")).Values("name")}}, + "g_V_hasXlabel_withoutXanimalXX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has(gremlingo.T.Label, gremlingo.P.Without("animal")).Values("name")}}, "g_V_hasIdXemptyX_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasId(p["xx1"]).Count()}}, "g_V_hasIdXwithinXemptyXX_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasId(gremlingo.P.Within([]interface{}{})).Count()}}, "g_V_hasIdXwithoutXemptyXX_count": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasId(gremlingo.P.Without([]interface{}{})).Count()}}, @@ -532,6 +540,9 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_E_hasLabelXnullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().HasLabel(nil)}}, "g_E_hasXlabel_nullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E().Has(gremlingo.T.Label, nil)}}, "g_V_properties_hasLabelXnullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties().HasLabel(nil)}}, + "g_V_hasLabelXbirdX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("bird").Values("name")}}, + "g_V_hasLabelXaquaticX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("aquatic").Values("name")}}, + "g_V_hasLabelXreptile_birdX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("reptile", "bird").Values("name")}}, "g_V_hasNotXageX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasNot("age").Values("name")}}, "g_V_properties_hasValueXnullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties().HasValue(nil)}}, "g_V_properties_hasValueXnull_nullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Properties().HasValue(nil, nil)}}, @@ -582,9 +593,11 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_notXhasXage_gt_27XX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Not(gremlingo.T__.Has("age", gremlingo.P.Gt(27))).Values("name")}}, "g_V_notXnotXhasXage_gt_27XXX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Not(gremlingo.T__.Not(gremlingo.T__.Has("age", gremlingo.P.Gt(27)))).Values("name")}}, "g_V_notXhasXname_gt_27XX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Not(gremlingo.T__.Has("name", gremlingo.P.Gt(27))).Values("name")}}, + "g_V_notXhasLabelXanimalXX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Not(gremlingo.T__.HasLabel("animal")).Values("name")}}, "g_V_orXhasXage_gt_27X__outE_count_gte_2X_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Or(gremlingo.T__.Has("age", gremlingo.P.Gt(27)), gremlingo.T__.OutE().Count().Is(gremlingo.P.Gte(2))).Values("name")}}, "g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Or(gremlingo.T__.OutE("knows"), gremlingo.T__.Has(gremlingo.T.Label, "software").Or().Has("age", gremlingo.P.Gte(35))).Values("name")}}, "g_V_asXaX_orXselectXaX_selectXaXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().As("a").Or(gremlingo.T__.Select("a"), gremlingo.T__.Select("a"))}}, + "g_V_orXhasLabelXbirdX_hasLabelXreptileXX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Or(gremlingo.T__.HasLabel("bird"), gremlingo.T__.HasLabel("reptile")).Values("name")}}, "g_VX1X_out_limitX2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Out().Limit(2)}}, "g_VX1X_out_limitX2varX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Out().Limit(p["xx1"])}}, "g_V_localXoutE_limitX1X_inVX_limitX3X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.OutE().Limit(1)).InV().Limit(3)}}, @@ -1661,6 +1674,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_VX1X_elementMap_orderXlocalX_byXkeys_descXunfold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).ElementMap().Order(gremlingo.Scope.Local).By(gremlingo.Column.Keys, gremlingo.Order.Desc).Unfold()}}, "g_VX1X_elementMap_orderXlocalX_byXkeys_ascXunfold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).ElementMap().Order(gremlingo.Scope.Local).By(gremlingo.Column.Keys, gremlingo.Order.Asc).Unfold()}}, "g_VX1X_valuesXageX_orderXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Order(gremlingo.Scope.Local)}}, + "g_V_hasLabelXhabitatX_order_byXlabels_order_foldX_name_multilabel": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("habitat").Order().By(gremlingo.T__.Labels().Order().Fold()).Values("name")}}, "g_V_pageRank_hasXpageRankX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().PageRank().Has("gremlin.pageRankVertexProgram.pageRank")}}, "g_V_outXcreatedX_pageRank_withXedges_bothEX_withXpropertyName_projectRankX_withXtimes_0X_valueMapXname_projectRankX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Out("created").PageRank().With("~tinkerpop.pageRank.edges", gremlingo.T__.BothE()).With("~tinkerpop.pageRank.propertyName", "projectRank").With("~tinkerpop.pageRank.times", 0).ValueMap("name", "projectRank")}}, "g_V_pageRank_order_byXpageRank_descX_byXnameX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().PageRank().Order().By("gremlin.pageRankVertexProgram.pageRank", gremlingo.Order.Desc).By("name").Values("name")}}, @@ -2230,6 +2244,9 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_groupXaX_byXnameX_by_selectXaX_countXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Group("a").By("name").By().Select("a").Count(gremlingo.Scope.Local)}}, "g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.Group("a").By("name").By().Select("a").Count(gremlingo.Scope.Local))}}, "g_V_group_byXvaluesXnameXX_byXboth_countX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Group().By(gremlingo.T__.Values("name")).By(gremlingo.T__.Both().Count())}}, + "g_V_hasLabelXhabitatX_group_byXlabelX_byXcountX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("habitat").Group().By(gremlingo.T.Label).By(gremlingo.T__.Count())}}, + "g_V_hasXname_tuxX_group_byXlabelX_byXcountX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "tux").Group().By(gremlingo.T__.Label()).By(gremlingo.T__.Count())}}, + "g_V_hasLabelXreptileX_group_byXlabels_order_foldX_byXname_foldX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("reptile").Group().By(gremlingo.T__.Labels().Order().Fold()).By(gremlingo.T__.Values("name").Fold())}}, "g_V_outXcreatedX_groupCount_byXnameX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Out("created").GroupCount().By("name")}}, "g_V_groupCount_byXageX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().GroupCount().By("age")}}, "g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.WithStrategies(gremlingo.ProductiveByStrategy()).V().GroupCount().By("age")}}, @@ -2250,6 +2267,8 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_outXcreatedX_groupCountXxX_byXnameX_byXageX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Out("created").GroupCount("x").By("name").By("age")}}, "g_V_groupCountXaX_selectXaX_countXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().GroupCount("a").Select("a").Count(gremlingo.Scope.Local)}}, "g_V_localXgroupCountXaX_selectXaX_countXlocalXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.GroupCount("a").Select("a").Count(gremlingo.Scope.Local))}}, + "g_V_hasXname_tuxX_groupCount_byXlabelX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "tux").GroupCount().By(gremlingo.T.Label)}}, + "g_V_labels_groupCount": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Labels().GroupCount()}}, "g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Out().Values("name").Inject("daniel").As("a").Map(gremlingo.T__.Length()).Path()}}, "g_injectXnull_1_3_nullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil, 1, 3, nil)}}, "g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(10, 20, nil, 20, 10, 10).GroupCount("x").Dedup().As("y").Project("a", "b").By().By(gremlingo.T__.Select("x").Select(gremlingo.T__.Select("y")))}}, diff --git a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js index a615f91d136..dbc7985908f 100644 --- a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js @@ -68,6 +68,7 @@ const gremlins = { g_V_branchXlabel_isXpersonX_countX_optionX1__ageX_optionX0__langX_optionX0__nameX_optionXany__labelX: [function({g, xx1, xx2}) { return g.V().branch(__.label().is("person").count()).option(xx1, __.values("age")).option(xx2, __.values("lang")).option(xx2, __.values("name")).option(Pick.any, __.label()) }], g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX: [function({g}) { return g.V().hasLabel("person").branch(__.values("age")).option(P.lt(30), __.constant("young")).option(P.gt(30), __.constant("old")).option(Pick.none, __.constant("on the edge")) }], g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX: [function({g}) { return g.V().branch(__.identity()).option(__.hasLabel("software"), __.in_("created").values("name").order().fold()).option(__.has("name", "vadas"), __.values("age")).option(P.neq(123), __.bothE().count()) }], + g_V_hasLabelXhabitat_reptileX_branchXidentityX_optionXhasLabelXaquaticXX_optionXhasLabelXreptileXX_optionXhasLabelXhabitatXX_multilabel: [function({g}) { return g.V().hasLabel("habitat", "reptile").branch(__.identity()).option(__.hasLabel("aquatic"), __.constant("wet")).option(__.hasLabel("reptile"), __.constant("scaly")).option(__.hasLabel("habitat"), __.constant("place")) }], g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX: [function({g, xx1, xx2}) { return g.V().choose(__.out().count()).option(xx1, __.values("name")).option(xx2, __.values("age")) }], g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX_optionXnone_discardX: [function({g, xx1, xx2}) { return g.V().choose(__.out().count()).option(xx1, __.values("name")).option(xx2, __.values("age")).option(Pick.none, __.discard()) }], g_V_chooseXhasLabelXpersonX_and_outXcreatedX__outXknowsX_identityX_name: [function({g}) { return g.V().choose(__.hasLabel("person").and().out("created"), __.out("knows"), __.identity()).values("name") }], @@ -107,6 +108,8 @@ const gremlins = { g_V_hasLabelXpersonX_age_chooseXP_eqX29X_constantXmatchedX: [function({g}) { return g.V().hasLabel("person").values("age").choose(P.eq(29), __.constant("matched")) }], g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_isXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX: [function({g}) { return g.V().hasLabel("person").choose(__.values("name")).option(__.is("marko"), __.values("age")).option(Pick.none, __.values("name")) }], g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_PeqXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX: [function({g}) { return g.V().hasLabel("person").choose(__.values("name")).option(P.eq("marko"), __.values("age")).option(Pick.none, __.values("name")) }], + g_V_hasLabelXhabitat_reptileX_chooseXhasLabelXaquaticX_constantXwetX_constantXdryXX_multilabel: [function({g}) { return g.V().hasLabel("habitat", "reptile").choose(__.hasLabel("aquatic"), __.constant("wet"), __.constant("dry")) }], + g_V_hasXname_tuxX_chooseXlabelX_optionXanimal_bird_aquatic_endangeredX_multilabel: [function({g}) { return g.V().has("name", "tux").choose(T.label).option("animal", __.constant("is-animal")).option("bird", __.constant("is-bird")).option("aquatic", __.constant("is-aquatic")).option("endangered", __.constant("is-endangered")) }], g_V_localXpropertiesXlocationX_order_byXvalueX_limitX2XX_value: [function({g}) { return g.V().local(__.properties("location").order().by(T.value, Order.asc).range(0, 2)).value() }], g_V_hasXlabel_personX_asXaX_localXoutXcreatedX_asXbXX_selectXa_bX_byXnameX_byXidX: [function({g}) { return g.V().has(T.label, "person").as("a").local(__.out("created").as("b")).select("a", "b").by("name").by(T.id) }], g_V_localXoutE_countX: [function({g}) { return g.V().local(__.outE().count()) }], @@ -362,6 +365,7 @@ const gremlins = { g_V_asXaX_outXknowsX_and_outXcreatedX_inXcreatedX_asXaX_name: [function({g}) { return g.V().as("a").out("knows").and().out("created").in_("created").as("a").values("name") }], g_V_asXaX_andXselectXaX_selectXaXX: [function({g}) { return g.V().as("a").and(__.select("a"), __.select("a")) }], g_V_hasXname_markoX_and_hasXname_markoX_and_hasXname_markoX: [function({g}) { return g.V().has("name", "marko").and().has("name", "marko").and().has("name", "marko") }], + g_V_andXhasLabelXmammalX_hasLabelXendangeredXX_name_multilabel: [function({g}) { return g.V().and(__.hasLabel("mammal"), __.hasLabel("endangered")).values("name") }], g_V_valuesXageX_anyXgtX32XX: [function({g}) { return g.V().values("age").any(P.gt(32)) }], g_V_valuesXageX_order_byXdescX_fold_anyXeqX29XX: [function({g}) { return g.V().values("age").order().by(Order.desc).fold().any(P.eq(29)) }], g_V_valuesXageX_order_byXdescX_fold_anyXgtX10XX: [function({g}) { return g.V().values("age").order().by(Order.desc).fold().any(P.gt(10)) }], @@ -411,6 +415,7 @@ const gremlins = { g_V_properties_dedup_count: [function({g}) { return g.addV("person").property("name", "josh").addV("person").property("name", "josh").addV("person").property("name", "josh") }, function({g}) { return g.V().properties("name").dedup().count() }], g_V_properties_dedup_byXvalueX_count: [function({g}) { return g.addV("person").property("name", "josh").addV("person").property("name", "josh").addV("person").property("name", "josh") }, function({g}) { return g.V().properties("name").dedup().by(T.value).count() }], g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name: [function({g}) { return g.V().both().has(T.label, "software").dedup().by("lang").by("name").values("name") }], + g_V_hasLabelXhabitatX_dedup_byXlabels_order_foldX_localXlabels_order_foldX_multilabel: [function({g}) { return g.V().hasLabel("habitat").dedup().by(__.labels().order().fold()).local(__.labels().order().fold()) }], g_V_count_discard: [function({g}) { return g.V().count().discard() }], g_V_hasLabelXpersonX_discard: [function({g}) { return g.V().hasLabel("person").discard() }], g_VX1X_outXcreatedX_discard: [function({g, vid1}) { return g.V(vid1).out("created").discard() }], @@ -516,6 +521,9 @@ const gremlins = { g_VXvid1X_asXaX_V_hasXage_gtXselectXaX_valuesXageXXX_valuesXnameX: [function({g, vid1}) { return g.V(vid1).as("a").V().has("age", P.gt(__.select("a").values("age"))).values("name") }], g_withSackX29X_V_hasXage_gtXsackXX_valuesXnameX: [function({g}) { return g.withSack(29).V().has("age", P.gt(__.sack())).values("name") }], g_V_hasXname_markoX_outEXknowsX_hasXweight_gteXconstantX05XXX: [function({g}) { return g.V().has("name", "marko").outE("knows").has("weight", P.gte(__.constant(0.5))) }], + g_V_hasXlabel_aquaticX_name_multilabel: [function({g}) { return g.V().has(T.label, "aquatic").values("name") }], + g_V_hasXlabel_withinXreptile_birdXX_name_multilabel: [function({g}) { return g.V().has(T.label, P.within("reptile", "bird")).values("name") }], + g_V_hasXlabel_withoutXanimalXX_name_multilabel: [function({g}) { return g.V().has(T.label, P.without("animal")).values("name") }], g_V_hasIdXemptyX_count: [function({g, xx1}) { return g.V().hasId(xx1).count() }], g_V_hasIdXwithinXemptyXX_count: [function({g}) { return g.V().hasId(P.within([])).count() }], g_V_hasIdXwithoutXemptyXX_count: [function({g}) { return g.V().hasId(P.without([])).count() }], @@ -563,6 +571,9 @@ const gremlins = { g_E_hasLabelXnullX: [function({g}) { return g.E().hasLabel(null) }], g_E_hasXlabel_nullX: [function({g}) { return g.E().has(T.label, null) }], g_V_properties_hasLabelXnullX: [function({g}) { return g.V().properties().hasLabel(null) }], + g_V_hasLabelXbirdX_name_multilabel: [function({g}) { return g.V().hasLabel("bird").values("name") }], + g_V_hasLabelXaquaticX_name_multilabel: [function({g}) { return g.V().hasLabel("aquatic").values("name") }], + g_V_hasLabelXreptile_birdX_name_multilabel: [function({g}) { return g.V().hasLabel("reptile", "bird").values("name") }], g_V_hasNotXageX_name: [function({g}) { return g.V().hasNot("age").values("name") }], g_V_properties_hasValueXnullX: [function({g}) { return g.V().properties().hasValue(null) }], g_V_properties_hasValueXnull_nullX: [function({g}) { return g.V().properties().hasValue(null, null) }], @@ -613,9 +624,11 @@ const gremlins = { g_V_notXhasXage_gt_27XX_name: [function({g}) { return g.V().not(__.has("age", P.gt(27))).values("name") }], g_V_notXnotXhasXage_gt_27XXX_name: [function({g}) { return g.V().not(__.not(__.has("age", P.gt(27)))).values("name") }], g_V_notXhasXname_gt_27XX_name: [function({g}) { return g.V().not(__.has("name", P.gt(27))).values("name") }], + g_V_notXhasLabelXanimalXX_name_multilabel: [function({g}) { return g.V().not(__.hasLabel("animal")).values("name") }], g_V_orXhasXage_gt_27X__outE_count_gte_2X_name: [function({g}) { return g.V().or(__.has("age", P.gt(27)), __.outE().count().is(P.gte(2))).values("name") }], g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name: [function({g}) { return g.V().or(__.outE("knows"), __.has(T.label, "software").or().has("age", P.gte(35))).values("name") }], g_V_asXaX_orXselectXaX_selectXaXX: [function({g}) { return g.V().as("a").or(__.select("a"), __.select("a")) }], + g_V_orXhasLabelXbirdX_hasLabelXreptileXX_name_multilabel: [function({g}) { return g.V().or(__.hasLabel("bird"), __.hasLabel("reptile")).values("name") }], g_VX1X_out_limitX2X: [function({g, vid1}) { return g.V(vid1).out().limit(2) }], g_VX1X_out_limitX2varX: [function({g, xx1, vid1}) { return g.V(vid1).out().limit(xx1) }], g_V_localXoutE_limitX1X_inVX_limitX3X: [function({g}) { return g.V().local(__.outE().limit(1)).inV().limit(3) }], @@ -1692,6 +1705,7 @@ const gremlins = { g_VX1X_elementMap_orderXlocalX_byXkeys_descXunfold: [function({g, vid1}) { return g.V(vid1).elementMap().order(Scope.local).by(Column.keys, Order.desc).unfold() }], g_VX1X_elementMap_orderXlocalX_byXkeys_ascXunfold: [function({g, vid1}) { return g.V(vid1).elementMap().order(Scope.local).by(Column.keys, Order.asc).unfold() }], g_VX1X_valuesXageX_orderXlocalX: [function({g, vid1}) { return g.V(vid1).values("age").order(Scope.local) }], + g_V_hasLabelXhabitatX_order_byXlabels_order_foldX_name_multilabel: [function({g}) { return g.V().hasLabel("habitat").order().by(__.labels().order().fold()).values("name") }], g_V_pageRank_hasXpageRankX: [function({g}) { return g.V().pageRank().has("gremlin.pageRankVertexProgram.pageRank") }], g_V_outXcreatedX_pageRank_withXedges_bothEX_withXpropertyName_projectRankX_withXtimes_0X_valueMapXname_projectRankX: [function({g}) { return g.V().out("created").pageRank().with_("~tinkerpop.pageRank.edges", __.bothE()).with_("~tinkerpop.pageRank.propertyName", "projectRank").with_("~tinkerpop.pageRank.times", 0).valueMap("name", "projectRank") }], g_V_pageRank_order_byXpageRank_descX_byXnameX_name: [function({g}) { return g.V().pageRank().order().by("gremlin.pageRankVertexProgram.pageRank", Order.desc).by("name").values("name") }], @@ -2261,6 +2275,9 @@ const gremlins = { g_V_groupXaX_byXnameX_by_selectXaX_countXlocalX: [function({g}) { return g.V().group("a").by("name").by().select("a").count(Scope.local) }], g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX: [function({g}) { return g.V().local(__.group("a").by("name").by().select("a").count(Scope.local)) }], g_V_group_byXvaluesXnameXX_byXboth_countX: [function({g}) { return g.V().group().by(__.values("name")).by(__.both().count()) }], + g_V_hasLabelXhabitatX_group_byXlabelX_byXcountX: [function({g}) { return g.V().hasLabel("habitat").group().by(T.label).by(__.count()) }], + g_V_hasXname_tuxX_group_byXlabelX_byXcountX: [function({g}) { return g.V().has("name", "tux").group().by(__.label()).by(__.count()) }], + g_V_hasLabelXreptileX_group_byXlabels_order_foldX_byXname_foldX: [function({g}) { return g.V().hasLabel("reptile").group().by(__.labels().order().fold()).by(__.values("name").fold()) }], g_V_outXcreatedX_groupCount_byXnameX: [function({g}) { return g.V().out("created").groupCount().by("name") }], g_V_groupCount_byXageX: [function({g}) { return g.V().groupCount().by("age") }], g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX: [function({g}) { return g.withStrategies(new ProductiveByStrategy()).V().groupCount().by("age") }], @@ -2281,6 +2298,8 @@ const gremlins = { g_V_outXcreatedX_groupCountXxX_byXnameX_byXageX: [function({g}) { return g.V().out("created").groupCount("x").by("name").by("age") }], g_V_groupCountXaX_selectXaX_countXlocalX: [function({g}) { return g.V().groupCount("a").select("a").count(Scope.local) }], g_V_localXgroupCountXaX_selectXaX_countXlocalXX: [function({g}) { return g.V().local(__.groupCount("a").select("a").count(Scope.local)) }], + g_V_hasXname_tuxX_groupCount_byXlabelX: [function({g}) { return g.V().has("name", "tux").groupCount().by(T.label) }], + g_V_labels_groupCount: [function({g}) { return g.V().labels().groupCount() }], g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path: [function({g, vid1}) { return g.V(vid1).out().values("name").inject("daniel").as("a").map(__.length()).path() }], g_injectXnull_1_3_nullX: [function({g}) { return g.inject(null, 1, 3, null) }], g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX: [function({g}) { return g.inject(10, 20, null, 20, 10, 10).groupCount("x").dedup().as("y").project("a", "b").by().by(__.select("x").select(__.select("y"))) }], diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py b/gremlin-python/src/main/python/tests/feature/gremlin.py index b6145bc1813..d100edf0ccf 100644 --- a/gremlin-python/src/main/python/tests/feature/gremlin.py +++ b/gremlin-python/src/main/python/tests/feature/gremlin.py @@ -42,6 +42,7 @@ 'g_V_branchXlabel_isXpersonX_countX_optionX1__ageX_optionX0__langX_optionX0__nameX_optionXany__labelX': [(lambda g, xx1=None,xx2=None:g.V().branch(__.label().is_('person').count()).option(xx1, __.values('age')).option(xx2, __.values('lang')).option(xx2, __.values('name')).option(Pick.any_, __.label()))], 'g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX': [(lambda g:g.V().has_label('person').branch(__.values('age')).option(P.lt(30), __.constant('young')).option(P.gt(30), __.constant('old')).option(Pick.none, __.constant('on the edge')))], 'g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX': [(lambda g:g.V().branch(__.identity()).option(__.has_label('software'), __.in_('created').values('name').order().fold()).option(__.has('name', 'vadas'), __.values('age')).option(P.neq(123), __.both_e().count()))], + 'g_V_hasLabelXhabitat_reptileX_branchXidentityX_optionXhasLabelXaquaticXX_optionXhasLabelXreptileXX_optionXhasLabelXhabitatXX_multilabel': [(lambda g:g.V().has_label('habitat', 'reptile').branch(__.identity()).option(__.has_label('aquatic'), __.constant('wet')).option(__.has_label('reptile'), __.constant('scaly')).option(__.has_label('habitat'), __.constant('place')))], 'g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX': [(lambda g, xx1=None,xx2=None:g.V().choose(__.out().count()).option(xx1, __.values('name')).option(xx2, __.values('age')))], 'g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX_optionXnone_discardX': [(lambda g, xx1=None,xx2=None:g.V().choose(__.out().count()).option(xx1, __.values('name')).option(xx2, __.values('age')).option(Pick.none, __.discard()))], 'g_V_chooseXhasLabelXpersonX_and_outXcreatedX__outXknowsX_identityX_name': [(lambda g:g.V().choose(__.has_label('person').and_().out('created'), __.out('knows'), __.identity()).values('name'))], @@ -81,6 +82,8 @@ 'g_V_hasLabelXpersonX_age_chooseXP_eqX29X_constantXmatchedX': [(lambda g:g.V().has_label('person').values('age').choose(P.eq(29), __.constant('matched')))], 'g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_isXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX': [(lambda g:g.V().has_label('person').choose(__.values('name')).option(__.is_('marko'), __.values('age')).option(Pick.none, __.values('name')))], 'g_V_hasLabelXpersonX_chooseX_valuesXnameX_option1X_PeqXmarkoX_valuesXageXX_option2Xnone_valuesXnameXX': [(lambda g:g.V().has_label('person').choose(__.values('name')).option(P.eq('marko'), __.values('age')).option(Pick.none, __.values('name')))], + 'g_V_hasLabelXhabitat_reptileX_chooseXhasLabelXaquaticX_constantXwetX_constantXdryXX_multilabel': [(lambda g:g.V().has_label('habitat', 'reptile').choose(__.has_label('aquatic'), __.constant('wet'), __.constant('dry')))], + 'g_V_hasXname_tuxX_chooseXlabelX_optionXanimal_bird_aquatic_endangeredX_multilabel': [(lambda g:g.V().has('name', 'tux').choose(T.label).option('animal', __.constant('is-animal')).option('bird', __.constant('is-bird')).option('aquatic', __.constant('is-aquatic')).option('endangered', __.constant('is-endangered')))], 'g_V_localXpropertiesXlocationX_order_byXvalueX_limitX2XX_value': [(lambda g:g.V().local(__.properties('location').order().by(T.value, Order.asc).range_(0, 2)).value())], 'g_V_hasXlabel_personX_asXaX_localXoutXcreatedX_asXbXX_selectXa_bX_byXnameX_byXidX': [(lambda g:g.V().has(T.label, 'person').as_('a').local(__.out('created').as_('b')).select('a', 'b').by('name').by(T.id_))], 'g_V_localXoutE_countX': [(lambda g:g.V().local(__.out_e().count()))], @@ -336,6 +339,7 @@ 'g_V_asXaX_outXknowsX_and_outXcreatedX_inXcreatedX_asXaX_name': [(lambda g:g.V().as_('a').out('knows').and_().out('created').in_('created').as_('a').values('name'))], 'g_V_asXaX_andXselectXaX_selectXaXX': [(lambda g:g.V().as_('a').and_(__.select('a'), __.select('a')))], 'g_V_hasXname_markoX_and_hasXname_markoX_and_hasXname_markoX': [(lambda g:g.V().has('name', 'marko').and_().has('name', 'marko').and_().has('name', 'marko'))], + 'g_V_andXhasLabelXmammalX_hasLabelXendangeredXX_name_multilabel': [(lambda g:g.V().and_(__.has_label('mammal'), __.has_label('endangered')).values('name'))], 'g_V_valuesXageX_anyXgtX32XX': [(lambda g:g.V().values('age').any_(P.gt(32)))], 'g_V_valuesXageX_order_byXdescX_fold_anyXeqX29XX': [(lambda g:g.V().values('age').order().by(Order.desc).fold().any_(P.eq(29)))], 'g_V_valuesXageX_order_byXdescX_fold_anyXgtX10XX': [(lambda g:g.V().values('age').order().by(Order.desc).fold().any_(P.gt(10)))], @@ -385,6 +389,7 @@ 'g_V_properties_dedup_count': [(lambda g:g.add_v('person').property('name', 'josh').add_v('person').property('name', 'josh').add_v('person').property('name', 'josh')), (lambda g:g.V().properties('name').dedup().count())], 'g_V_properties_dedup_byXvalueX_count': [(lambda g:g.add_v('person').property('name', 'josh').add_v('person').property('name', 'josh').add_v('person').property('name', 'josh')), (lambda g:g.V().properties('name').dedup().by(T.value).count())], 'g_V_both_hasXlabel_softwareX_dedup_byXlangX_byXnameX_name': [(lambda g:g.V().both().has(T.label, 'software').dedup().by('lang').by('name').values('name'))], + 'g_V_hasLabelXhabitatX_dedup_byXlabels_order_foldX_localXlabels_order_foldX_multilabel': [(lambda g:g.V().has_label('habitat').dedup().by(__.labels().order().fold()).local(__.labels().order().fold()))], 'g_V_count_discard': [(lambda g:g.V().count().discard())], 'g_V_hasLabelXpersonX_discard': [(lambda g:g.V().has_label('person').discard())], 'g_VX1X_outXcreatedX_discard': [(lambda g, vid1=None:g.V(vid1).out('created').discard())], @@ -490,6 +495,9 @@ 'g_VXvid1X_asXaX_V_hasXage_gtXselectXaX_valuesXageXXX_valuesXnameX': [(lambda g, vid1=None:g.V(vid1).as_('a').V().has('age', P.gt(__.select('a').values('age'))).values('name'))], 'g_withSackX29X_V_hasXage_gtXsackXX_valuesXnameX': [(lambda g:g.with_sack(29).V().has('age', P.gt(__.sack())).values('name'))], 'g_V_hasXname_markoX_outEXknowsX_hasXweight_gteXconstantX05XXX': [(lambda g:g.V().has('name', 'marko').out_e('knows').has('weight', P.gte(__.constant(0.5))))], + 'g_V_hasXlabel_aquaticX_name_multilabel': [(lambda g:g.V().has(T.label, 'aquatic').values('name'))], + 'g_V_hasXlabel_withinXreptile_birdXX_name_multilabel': [(lambda g:g.V().has(T.label, P.within('reptile', 'bird')).values('name'))], + 'g_V_hasXlabel_withoutXanimalXX_name_multilabel': [(lambda g:g.V().has(T.label, P.without('animal')).values('name'))], 'g_V_hasIdXemptyX_count': [(lambda g, xx1=None:g.V().has_id(xx1).count())], 'g_V_hasIdXwithinXemptyXX_count': [(lambda g:g.V().has_id(P.within([])).count())], 'g_V_hasIdXwithoutXemptyXX_count': [(lambda g:g.V().has_id(P.without([])).count())], @@ -537,6 +545,9 @@ 'g_E_hasLabelXnullX': [(lambda g:g.E().has_label(None))], 'g_E_hasXlabel_nullX': [(lambda g:g.E().has(T.label, None))], 'g_V_properties_hasLabelXnullX': [(lambda g:g.V().properties().has_label(None))], + 'g_V_hasLabelXbirdX_name_multilabel': [(lambda g:g.V().has_label('bird').values('name'))], + 'g_V_hasLabelXaquaticX_name_multilabel': [(lambda g:g.V().has_label('aquatic').values('name'))], + 'g_V_hasLabelXreptile_birdX_name_multilabel': [(lambda g:g.V().has_label('reptile', 'bird').values('name'))], 'g_V_hasNotXageX_name': [(lambda g:g.V().has_not('age').values('name'))], 'g_V_properties_hasValueXnullX': [(lambda g:g.V().properties().has_value(None))], 'g_V_properties_hasValueXnull_nullX': [(lambda g:g.V().properties().has_value(None, None))], @@ -587,9 +598,11 @@ 'g_V_notXhasXage_gt_27XX_name': [(lambda g:g.V().not_(__.has('age', P.gt(27))).values('name'))], 'g_V_notXnotXhasXage_gt_27XXX_name': [(lambda g:g.V().not_(__.not_(__.has('age', P.gt(27)))).values('name'))], 'g_V_notXhasXname_gt_27XX_name': [(lambda g:g.V().not_(__.has('name', P.gt(27))).values('name'))], + 'g_V_notXhasLabelXanimalXX_name_multilabel': [(lambda g:g.V().not_(__.has_label('animal')).values('name'))], 'g_V_orXhasXage_gt_27X__outE_count_gte_2X_name': [(lambda g:g.V().or_(__.has('age', P.gt(27)), __.out_e().count().is_(P.gte(2))).values('name'))], 'g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name': [(lambda g:g.V().or_(__.out_e('knows'), __.has(T.label, 'software').or_().has('age', P.gte(35))).values('name'))], 'g_V_asXaX_orXselectXaX_selectXaXX': [(lambda g:g.V().as_('a').or_(__.select('a'), __.select('a')))], + 'g_V_orXhasLabelXbirdX_hasLabelXreptileXX_name_multilabel': [(lambda g:g.V().or_(__.has_label('bird'), __.has_label('reptile')).values('name'))], 'g_VX1X_out_limitX2X': [(lambda g, vid1=None:g.V(vid1).out().limit(2))], 'g_VX1X_out_limitX2varX': [(lambda g, xx1=None,vid1=None:g.V(vid1).out().limit(xx1))], 'g_V_localXoutE_limitX1X_inVX_limitX3X': [(lambda g:g.V().local(__.out_e().limit(1)).in_v().limit(3))], @@ -1666,6 +1679,7 @@ 'g_VX1X_elementMap_orderXlocalX_byXkeys_descXunfold': [(lambda g, vid1=None:g.V(vid1).element_map().order(Scope.local).by(Column.keys, Order.desc).unfold())], 'g_VX1X_elementMap_orderXlocalX_byXkeys_ascXunfold': [(lambda g, vid1=None:g.V(vid1).element_map().order(Scope.local).by(Column.keys, Order.asc).unfold())], 'g_VX1X_valuesXageX_orderXlocalX': [(lambda g, vid1=None:g.V(vid1).values('age').order(Scope.local))], + 'g_V_hasLabelXhabitatX_order_byXlabels_order_foldX_name_multilabel': [(lambda g:g.V().has_label('habitat').order().by(__.labels().order().fold()).values('name'))], 'g_V_pageRank_hasXpageRankX': [(lambda g:g.V().page_rank().has('gremlin.pageRankVertexProgram.pageRank'))], 'g_V_outXcreatedX_pageRank_withXedges_bothEX_withXpropertyName_projectRankX_withXtimes_0X_valueMapXname_projectRankX': [(lambda g:g.V().out('created').page_rank().with_('~tinkerpop.pageRank.edges', __.both_e()).with_('~tinkerpop.pageRank.propertyName', 'projectRank').with_('~tinkerpop.pageRank.times', 0).value_map('name', 'projectRank'))], 'g_V_pageRank_order_byXpageRank_descX_byXnameX_name': [(lambda g:g.V().page_rank().order().by('gremlin.pageRankVertexProgram.pageRank', Order.desc).by('name').values('name'))], @@ -2235,6 +2249,9 @@ 'g_V_groupXaX_byXnameX_by_selectXaX_countXlocalX': [(lambda g:g.V().group('a').by('name').by().select('a').count(Scope.local))], 'g_V_localXgroupXaX_byXnameX_by_selectXaX_countXlocalXX': [(lambda g:g.V().local(__.group('a').by('name').by().select('a').count(Scope.local)))], 'g_V_group_byXvaluesXnameXX_byXboth_countX': [(lambda g:g.V().group().by(__.values('name')).by(__.both().count()))], + 'g_V_hasLabelXhabitatX_group_byXlabelX_byXcountX': [(lambda g:g.V().has_label('habitat').group().by(T.label).by(__.count()))], + 'g_V_hasXname_tuxX_group_byXlabelX_byXcountX': [(lambda g:g.V().has('name', 'tux').group().by(__.label()).by(__.count()))], + 'g_V_hasLabelXreptileX_group_byXlabels_order_foldX_byXname_foldX': [(lambda g:g.V().has_label('reptile').group().by(__.labels().order().fold()).by(__.values('name').fold()))], 'g_V_outXcreatedX_groupCount_byXnameX': [(lambda g:g.V().out('created').group_count().by('name'))], 'g_V_groupCount_byXageX': [(lambda g:g.V().group_count().by('age'))], 'g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX': [(lambda g:g.with_strategies(ProductiveByStrategy()).V().group_count().by('age'))], @@ -2255,6 +2272,8 @@ 'g_V_outXcreatedX_groupCountXxX_byXnameX_byXageX': [(lambda g:g.V().out('created').group_count('x').by('name').by('age'))], 'g_V_groupCountXaX_selectXaX_countXlocalX': [(lambda g:g.V().group_count('a').select('a').count(Scope.local))], 'g_V_localXgroupCountXaX_selectXaX_countXlocalXX': [(lambda g:g.V().local(__.group_count('a').select('a').count(Scope.local)))], + 'g_V_hasXname_tuxX_groupCount_byXlabelX': [(lambda g:g.V().has('name', 'tux').group_count().by(T.label))], + 'g_V_labels_groupCount': [(lambda g:g.V().labels().group_count())], 'g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path': [(lambda g, vid1=None:g.V(vid1).out().values('name').inject('daniel').as_('a').map(__.length()).path())], 'g_injectXnull_1_3_nullX': [(lambda g:g.inject(None, 1, 3, None))], 'g_injectX10_20_null_20_10_10X_groupCountXxX_dedup_asXyX_projectXa_bX_by_byXselectXxX_selectXselectXyXXX': [(lambda g:g.inject(10, 20, None, 20, 10, 10).group_count('x').dedup().as_('y').project('a', 'b').by().by(__.select('x').select(__.select('y'))))], diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json index 830e2e4fd92..bd98bd35570 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json @@ -67,6 +67,23 @@ } ] }, + { + "scenario": "g_V_hasLabelXhabitat_reptileX_branchXidentityX_optionXhasLabelXaquaticXX_optionXhasLabelXreptileXX_optionXhasLabelXhabitatXX_multilabel", + "traversals": [ + { + "original": "g.V().hasLabel(\"habitat\", \"reptile\").branch(__.identity()).option(__.hasLabel(\"aquatic\"), __.constant(\"wet\")).option(__.hasLabel(\"reptile\"), __.constant(\"scaly\")).option(__.hasLabel(\"habitat\"), __.constant(\"place\"))", + "language": "g.V().hasLabel(\"habitat\", \"reptile\").branch(__.identity()).option(__.hasLabel(\"aquatic\"), __.constant(\"wet\")).option(__.hasLabel(\"reptile\"), __.constant(\"scaly\")).option(__.hasLabel(\"habitat\"), __.constant(\"place\"))", + "canonical": "g.V().hasLabel(\"habitat\", \"reptile\").branch(__.identity()).option(__.hasLabel(\"aquatic\"), __.constant(\"wet\")).option(__.hasLabel(\"reptile\"), __.constant(\"scaly\")).option(__.hasLabel(\"habitat\"), __.constant(\"place\"))", + "anonymized": "g.V().hasLabel(string0, string1).branch(__.identity()).option(__.hasLabel(string2), __.constant(string3)).option(__.hasLabel(string1), __.constant(string4)).option(__.hasLabel(string0), __.constant(string5))", + "dotnet": "g.V().HasLabel(\"habitat\", \"reptile\").Branch(__.Identity()).Option(__.HasLabel(\"aquatic\"), __.Constant(\"wet\")).Option(__.HasLabel(\"reptile\"), __.Constant(\"scaly\")).Option(__.HasLabel(\"habitat\"), __.Constant(\"place\"))", + "go": "g.V().HasLabel(\"habitat\", \"reptile\").Branch(gremlingo.T__.Identity()).Option(gremlingo.T__.HasLabel(\"aquatic\"), gremlingo.T__.Constant(\"wet\")).Option(gremlingo.T__.HasLabel(\"reptile\"), gremlingo.T__.Constant(\"scaly\")).Option(gremlingo.T__.HasLabel(\"habitat\"), gremlingo.T__.Constant(\"place\"))", + "groovy": "g.V().hasLabel(\"habitat\", \"reptile\").branch(__.identity()).option(__.hasLabel(\"aquatic\"), __.constant(\"wet\")).option(__.hasLabel(\"reptile\"), __.constant(\"scaly\")).option(__.hasLabel(\"habitat\"), __.constant(\"place\"))", + "java": "g.V().hasLabel(\"habitat\", \"reptile\").branch(__.identity()).option(__.hasLabel(\"aquatic\"), __.constant(\"wet\")).option(__.hasLabel(\"reptile\"), __.constant(\"scaly\")).option(__.hasLabel(\"habitat\"), __.constant(\"place\"))", + "javascript": "g.V().hasLabel(\"habitat\", \"reptile\").branch(__.identity()).option(__.hasLabel(\"aquatic\"), __.constant(\"wet\")).option(__.hasLabel(\"reptile\"), __.constant(\"scaly\")).option(__.hasLabel(\"habitat\"), __.constant(\"place\"))", + "python": "g.V().has_label('habitat', 'reptile').branch(__.identity()).option(__.has_label('aquatic'), __.constant('wet')).option(__.has_label('reptile'), __.constant('scaly')).option(__.has_label('habitat'), __.constant('place'))" + } + ] + }, { "scenario": "g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX", "traversals": [ @@ -730,6 +747,40 @@ } ] }, + { + "scenario": "g_V_hasLabelXhabitat_reptileX_chooseXhasLabelXaquaticX_constantXwetX_constantXdryXX_multilabel", + "traversals": [ + { + "original": "g.V().hasLabel(\"habitat\", \"reptile\").choose(__.hasLabel(\"aquatic\"), __.constant(\"wet\"), __.constant(\"dry\"))", + "language": "g.V().hasLabel(\"habitat\", \"reptile\").choose(__.hasLabel(\"aquatic\"), __.constant(\"wet\"), __.constant(\"dry\"))", + "canonical": "g.V().hasLabel(\"habitat\", \"reptile\").choose(__.hasLabel(\"aquatic\"), __.constant(\"wet\"), __.constant(\"dry\"))", + "anonymized": "g.V().hasLabel(string0, string1).choose(__.hasLabel(string2), __.constant(string3), __.constant(string4))", + "dotnet": "g.V().HasLabel(\"habitat\", \"reptile\").Choose(__.HasLabel(\"aquatic\"), __.Constant(\"wet\"), __.Constant(\"dry\"))", + "go": "g.V().HasLabel(\"habitat\", \"reptile\").Choose(gremlingo.T__.HasLabel(\"aquatic\"), gremlingo.T__.Constant(\"wet\"), gremlingo.T__.Constant(\"dry\"))", + "groovy": "g.V().hasLabel(\"habitat\", \"reptile\").choose(__.hasLabel(\"aquatic\"), __.constant(\"wet\"), __.constant(\"dry\"))", + "java": "g.V().hasLabel(\"habitat\", \"reptile\").choose(__.hasLabel(\"aquatic\"), __.constant(\"wet\"), __.constant(\"dry\"))", + "javascript": "g.V().hasLabel(\"habitat\", \"reptile\").choose(__.hasLabel(\"aquatic\"), __.constant(\"wet\"), __.constant(\"dry\"))", + "python": "g.V().has_label('habitat', 'reptile').choose(__.has_label('aquatic'), __.constant('wet'), __.constant('dry'))" + } + ] + }, + { + "scenario": "g_V_hasXname_tuxX_chooseXlabelX_optionXanimal_bird_aquatic_endangeredX_multilabel", + "traversals": [ + { + "original": "g.V().has(\"name\", \"tux\").choose(T.label).option(\"animal\", __.constant(\"is-animal\")).option(\"bird\", __.constant(\"is-bird\")).option(\"aquatic\", __.constant(\"is-aquatic\")).option(\"endangered\", __.constant(\"is-endangered\"))", + "language": "g.V().has(\"name\", \"tux\").choose(T.label).option(\"animal\", __.constant(\"is-animal\")).option(\"bird\", __.constant(\"is-bird\")).option(\"aquatic\", __.constant(\"is-aquatic\")).option(\"endangered\", __.constant(\"is-endangered\"))", + "canonical": "g.V().has(\"name\", \"tux\").choose(T.label).option(\"animal\", __.constant(\"is-animal\")).option(\"bird\", __.constant(\"is-bird\")).option(\"aquatic\", __.constant(\"is-aquatic\")).option(\"endangered\", __.constant(\"is-endangered\"))", + "anonymized": "g.V().has(string0, string1).choose(T.label).option(string2, __.constant(string3)).option(string4, __.constant(string5)).option(string6, __.constant(string7)).option(string8, __.constant(string9))", + "dotnet": "g.V().Has(\"name\", \"tux\").Choose(T.Label).Option(\"animal\", __.Constant(\"is-animal\")).Option(\"bird\", __.Constant(\"is-bird\")).Option(\"aquatic\", __.Constant(\"is-aquatic\")).Option(\"endangered\", __.Constant(\"is-endangered\"))", + "go": "g.V().Has(\"name\", \"tux\").Choose(gremlingo.T.Label).Option(\"animal\", gremlingo.T__.Constant(\"is-animal\")).Option(\"bird\", gremlingo.T__.Constant(\"is-bird\")).Option(\"aquatic\", gremlingo.T__.Constant(\"is-aquatic\")).Option(\"endangered\", gremlingo.T__.Constant(\"is-endangered\"))", + "groovy": "g.V().has(\"name\", \"tux\").choose(T.label).option(\"animal\", __.constant(\"is-animal\")).option(\"bird\", __.constant(\"is-bird\")).option(\"aquatic\", __.constant(\"is-aquatic\")).option(\"endangered\", __.constant(\"is-endangered\"))", + "java": "g.V().has(\"name\", \"tux\").choose(T.label).option(\"animal\", __.constant(\"is-animal\")).option(\"bird\", __.constant(\"is-bird\")).option(\"aquatic\", __.constant(\"is-aquatic\")).option(\"endangered\", __.constant(\"is-endangered\"))", + "javascript": "g.V().has(\"name\", \"tux\").choose(T.label).option(\"animal\", __.constant(\"is-animal\")).option(\"bird\", __.constant(\"is-bird\")).option(\"aquatic\", __.constant(\"is-aquatic\")).option(\"endangered\", __.constant(\"is-endangered\"))", + "python": "g.V().has('name', 'tux').choose(T.label).option('animal', __.constant('is-animal')).option('bird', __.constant('is-bird')).option('aquatic', __.constant('is-aquatic')).option('endangered', __.constant('is-endangered'))" + } + ] + }, { "scenario": "g_V_localXpropertiesXlocationX_order_byXvalueX_limitX2XX_value", "traversals": [ @@ -6091,6 +6142,23 @@ } ] }, + { + "scenario": "g_V_andXhasLabelXmammalX_hasLabelXendangeredXX_name_multilabel", + "traversals": [ + { + "original": "g.V().and(__.hasLabel(\"mammal\"), __.hasLabel(\"endangered\")).values(\"name\")", + "language": "g.V().and(__.hasLabel(\"mammal\"), __.hasLabel(\"endangered\")).values(\"name\")", + "canonical": "g.V().and(__.hasLabel(\"mammal\"), __.hasLabel(\"endangered\")).values(\"name\")", + "anonymized": "g.V().and(__.hasLabel(string0), __.hasLabel(string1)).values(string2)", + "dotnet": "g.V().And(__.HasLabel(\"mammal\"), __.HasLabel(\"endangered\")).Values(\"name\")", + "go": "g.V().And(gremlingo.T__.HasLabel(\"mammal\"), gremlingo.T__.HasLabel(\"endangered\")).Values(\"name\")", + "groovy": "g.V().and(__.hasLabel(\"mammal\"), __.hasLabel(\"endangered\")).values(\"name\")", + "java": "g.V().and(__.hasLabel(\"mammal\"), __.hasLabel(\"endangered\")).values(\"name\")", + "javascript": "g.V().and(__.hasLabel(\"mammal\"), __.hasLabel(\"endangered\")).values(\"name\")", + "python": "g.V().and_(__.has_label('mammal'), __.has_label('endangered')).values('name')" + } + ] + }, { "scenario": "g_V_valuesXageX_anyXgtX32XX", "traversals": [ @@ -6960,6 +7028,23 @@ } ] }, + { + "scenario": "g_V_hasLabelXhabitatX_dedup_byXlabels_order_foldX_localXlabels_order_foldX_multilabel", + "traversals": [ + { + "original": "g.V().hasLabel(\"habitat\").dedup().by(__.labels().order().fold()).local(__.labels().order().fold())", + "language": "g.V().hasLabel(\"habitat\").dedup().by(__.labels().order().fold()).local(__.labels().order().fold())", + "canonical": "g.V().hasLabel(\"habitat\").dedup().by(__.labels().order().fold()).local(__.labels().order().fold())", + "anonymized": "g.V().hasLabel(string0).dedup().by(__.labels().order().fold()).local(__.labels().order().fold())", + "dotnet": "g.V().HasLabel(\"habitat\").Dedup().By(__.Labels().Order().Fold()).Local(__.Labels().Order().Fold())", + "go": "g.V().HasLabel(\"habitat\").Dedup().By(gremlingo.T__.Labels().Order().Fold()).Local(gremlingo.T__.Labels().Order().Fold())", + "groovy": "g.V().hasLabel(\"habitat\").dedup().by(__.labels().order().fold()).local(__.labels().order().fold())", + "java": "g.V().hasLabel(\"habitat\").dedup().by(__.labels().order().fold()).local(__.labels().order().fold())", + "javascript": "g.V().hasLabel(\"habitat\").dedup().by(__.labels().order().fold()).local(__.labels().order().fold())", + "python": "g.V().has_label('habitat').dedup().by(__.labels().order().fold()).local(__.labels().order().fold())" + } + ] + }, { "scenario": "g_V_count_discard", "traversals": [ @@ -8949,6 +9034,57 @@ } ] }, + { + "scenario": "g_V_hasXlabel_aquaticX_name_multilabel", + "traversals": [ + { + "original": "g.V().has(T.label, \"aquatic\").values(\"name\")", + "language": "g.V().has(T.label, \"aquatic\").values(\"name\")", + "canonical": "g.V().has(T.label, \"aquatic\").values(\"name\")", + "anonymized": "g.V().has(T.label, string0).values(string1)", + "dotnet": "g.V().Has(T.Label, \"aquatic\").Values(\"name\")", + "go": "g.V().Has(gremlingo.T.Label, \"aquatic\").Values(\"name\")", + "groovy": "g.V().has(T.label, \"aquatic\").values(\"name\")", + "java": "g.V().has(T.label, \"aquatic\").values(\"name\")", + "javascript": "g.V().has(T.label, \"aquatic\").values(\"name\")", + "python": "g.V().has(T.label, 'aquatic').values('name')" + } + ] + }, + { + "scenario": "g_V_hasXlabel_withinXreptile_birdXX_name_multilabel", + "traversals": [ + { + "original": "g.V().has(T.label, P.within(\"reptile\", \"bird\")).values(\"name\")", + "language": "g.V().has(T.label, P.within(\"reptile\", \"bird\")).values(\"name\")", + "canonical": "g.V().has(T.label, P.within(\"reptile\", \"bird\")).values(\"name\")", + "anonymized": "g.V().has(T.label, P.within(string0, string1)).values(string2)", + "dotnet": "g.V().Has(T.Label, P.Within(\"reptile\", \"bird\")).Values(\"name\")", + "go": "g.V().Has(gremlingo.T.Label, gremlingo.P.Within(\"reptile\", \"bird\")).Values(\"name\")", + "groovy": "g.V().has(T.label, P.within(\"reptile\", \"bird\")).values(\"name\")", + "java": "g.V().has(T.label, P.within(\"reptile\", \"bird\")).values(\"name\")", + "javascript": "g.V().has(T.label, P.within(\"reptile\", \"bird\")).values(\"name\")", + "python": "g.V().has(T.label, P.within('reptile', 'bird')).values('name')" + } + ] + }, + { + "scenario": "g_V_hasXlabel_withoutXanimalXX_name_multilabel", + "traversals": [ + { + "original": "g.V().has(T.label, P.without(\"animal\")).values(\"name\")", + "language": "g.V().has(T.label, P.without(\"animal\")).values(\"name\")", + "canonical": "g.V().has(T.label, P.without(\"animal\")).values(\"name\")", + "anonymized": "g.V().has(T.label, P.without(string0)).values(string1)", + "dotnet": "g.V().Has(T.Label, P.Without(\"animal\")).Values(\"name\")", + "go": "g.V().Has(gremlingo.T.Label, gremlingo.P.Without(\"animal\")).Values(\"name\")", + "groovy": "g.V().has(T.label, P.without(\"animal\")).values(\"name\")", + "java": "g.V().has(T.label, P.without(\"animal\")).values(\"name\")", + "javascript": "g.V().has(T.label, P.without(\"animal\")).values(\"name\")", + "python": "g.V().has(T.label, P.without('animal')).values('name')" + } + ] + }, { "scenario": "g_V_hasIdXemptyX_count", "traversals": [ @@ -9772,6 +9908,57 @@ } ] }, + { + "scenario": "g_V_hasLabelXbirdX_name_multilabel", + "traversals": [ + { + "original": "g.V().hasLabel(\"bird\").values(\"name\")", + "language": "g.V().hasLabel(\"bird\").values(\"name\")", + "canonical": "g.V().hasLabel(\"bird\").values(\"name\")", + "anonymized": "g.V().hasLabel(string0).values(string1)", + "dotnet": "g.V().HasLabel(\"bird\").Values(\"name\")", + "go": "g.V().HasLabel(\"bird\").Values(\"name\")", + "groovy": "g.V().hasLabel(\"bird\").values(\"name\")", + "java": "g.V().hasLabel(\"bird\").values(\"name\")", + "javascript": "g.V().hasLabel(\"bird\").values(\"name\")", + "python": "g.V().has_label('bird').values('name')" + } + ] + }, + { + "scenario": "g_V_hasLabelXaquaticX_name_multilabel", + "traversals": [ + { + "original": "g.V().hasLabel(\"aquatic\").values(\"name\")", + "language": "g.V().hasLabel(\"aquatic\").values(\"name\")", + "canonical": "g.V().hasLabel(\"aquatic\").values(\"name\")", + "anonymized": "g.V().hasLabel(string0).values(string1)", + "dotnet": "g.V().HasLabel(\"aquatic\").Values(\"name\")", + "go": "g.V().HasLabel(\"aquatic\").Values(\"name\")", + "groovy": "g.V().hasLabel(\"aquatic\").values(\"name\")", + "java": "g.V().hasLabel(\"aquatic\").values(\"name\")", + "javascript": "g.V().hasLabel(\"aquatic\").values(\"name\")", + "python": "g.V().has_label('aquatic').values('name')" + } + ] + }, + { + "scenario": "g_V_hasLabelXreptile_birdX_name_multilabel", + "traversals": [ + { + "original": "g.V().hasLabel(\"reptile\", \"bird\").values(\"name\")", + "language": "g.V().hasLabel(\"reptile\", \"bird\").values(\"name\")", + "canonical": "g.V().hasLabel(\"reptile\", \"bird\").values(\"name\")", + "anonymized": "g.V().hasLabel(string0, string1).values(string2)", + "dotnet": "g.V().HasLabel(\"reptile\", \"bird\").Values(\"name\")", + "go": "g.V().HasLabel(\"reptile\", \"bird\").Values(\"name\")", + "groovy": "g.V().hasLabel(\"reptile\", \"bird\").values(\"name\")", + "java": "g.V().hasLabel(\"reptile\", \"bird\").values(\"name\")", + "javascript": "g.V().hasLabel(\"reptile\", \"bird\").values(\"name\")", + "python": "g.V().has_label('reptile', 'bird').values('name')" + } + ] + }, { "scenario": "g_V_hasNotXageX_name", "traversals": [ @@ -10622,6 +10809,23 @@ } ] }, + { + "scenario": "g_V_notXhasLabelXanimalXX_name_multilabel", + "traversals": [ + { + "original": "g.V().not(__.hasLabel(\"animal\")).values(\"name\")", + "language": "g.V().not(__.hasLabel(\"animal\")).values(\"name\")", + "canonical": "g.V().not(__.hasLabel(\"animal\")).values(\"name\")", + "anonymized": "g.V().not(__.hasLabel(string0)).values(string1)", + "dotnet": "g.V().Not(__.HasLabel(\"animal\")).Values(\"name\")", + "go": "g.V().Not(gremlingo.T__.HasLabel(\"animal\")).Values(\"name\")", + "groovy": "g.V().not(__.hasLabel(\"animal\")).values(\"name\")", + "java": "g.V().not(__.hasLabel(\"animal\")).values(\"name\")", + "javascript": "g.V().not(__.hasLabel(\"animal\")).values(\"name\")", + "python": "g.V().not_(__.has_label('animal')).values('name')" + } + ] + }, { "scenario": "g_V_orXhasXage_gt_27X__outE_count_gte_2X_name", "traversals": [ @@ -10673,6 +10877,23 @@ } ] }, + { + "scenario": "g_V_orXhasLabelXbirdX_hasLabelXreptileXX_name_multilabel", + "traversals": [ + { + "original": "g.V().or(__.hasLabel(\"bird\"), __.hasLabel(\"reptile\")).values(\"name\")", + "language": "g.V().or(__.hasLabel(\"bird\"), __.hasLabel(\"reptile\")).values(\"name\")", + "canonical": "g.V().or(__.hasLabel(\"bird\"), __.hasLabel(\"reptile\")).values(\"name\")", + "anonymized": "g.V().or(__.hasLabel(string0), __.hasLabel(string1)).values(string2)", + "dotnet": "g.V().Or(__.HasLabel(\"bird\"), __.HasLabel(\"reptile\")).Values(\"name\")", + "go": "g.V().Or(gremlingo.T__.HasLabel(\"bird\"), gremlingo.T__.HasLabel(\"reptile\")).Values(\"name\")", + "groovy": "g.V().or(__.hasLabel(\"bird\"), __.hasLabel(\"reptile\")).values(\"name\")", + "java": "g.V().or(__.hasLabel(\"bird\"), __.hasLabel(\"reptile\")).values(\"name\")", + "javascript": "g.V().or(__.hasLabel(\"bird\"), __.hasLabel(\"reptile\")).values(\"name\")", + "python": "g.V().or_(__.has_label('bird'), __.has_label('reptile')).values('name')" + } + ] + }, { "scenario": "g_VX1X_out_limitX2X", "traversals": [ @@ -36189,6 +36410,23 @@ } ] }, + { + "scenario": "g_V_hasLabelXhabitatX_order_byXlabels_order_foldX_name_multilabel", + "traversals": [ + { + "original": "g.V().hasLabel(\"habitat\").order().by(__.labels().order().fold()).values(\"name\")", + "language": "g.V().hasLabel(\"habitat\").order().by(__.labels().order().fold()).values(\"name\")", + "canonical": "g.V().hasLabel(\"habitat\").order().by(__.labels().order().fold()).values(\"name\")", + "anonymized": "g.V().hasLabel(string0).order().by(__.labels().order().fold()).values(string1)", + "dotnet": "g.V().HasLabel(\"habitat\").Order().By(__.Labels().Order().Fold()).Values(\"name\")", + "go": "g.V().HasLabel(\"habitat\").Order().By(gremlingo.T__.Labels().Order().Fold()).Values(\"name\")", + "groovy": "g.V().hasLabel(\"habitat\").order().by(__.labels().order().fold()).values(\"name\")", + "java": "g.V().hasLabel(\"habitat\").order().by(__.labels().order().fold()).values(\"name\")", + "javascript": "g.V().hasLabel(\"habitat\").order().by(__.labels().order().fold()).values(\"name\")", + "python": "g.V().has_label('habitat').order().by(__.labels().order().fold()).values('name')" + } + ] + }, { "scenario": "g_V_pageRank_hasXpageRankX", "traversals": [ @@ -46582,6 +46820,57 @@ } ] }, + { + "scenario": "g_V_hasLabelXhabitatX_group_byXlabelX_byXcountX", + "traversals": [ + { + "original": "g.V().hasLabel(\"habitat\").group().by(T.label).by(__.count())", + "language": "g.V().hasLabel(\"habitat\").group().by(T.label).by(__.count())", + "canonical": "g.V().hasLabel(\"habitat\").group().by(T.label).by(__.count())", + "anonymized": "g.V().hasLabel(string0).group().by(T.label).by(__.count())", + "dotnet": "g.V().HasLabel(\"habitat\").Group().By(T.Label).By(__.Count())", + "go": "g.V().HasLabel(\"habitat\").Group().By(gremlingo.T.Label).By(gremlingo.T__.Count())", + "groovy": "g.V().hasLabel(\"habitat\").group().by(T.label).by(__.count())", + "java": "g.V().hasLabel(\"habitat\").group().by(T.label).by(__.count())", + "javascript": "g.V().hasLabel(\"habitat\").group().by(T.label).by(__.count())", + "python": "g.V().has_label('habitat').group().by(T.label).by(__.count())" + } + ] + }, + { + "scenario": "g_V_hasXname_tuxX_group_byXlabelX_byXcountX", + "traversals": [ + { + "original": "g.V().has(\"name\", \"tux\").group().by(__.label()).by(__.count())", + "language": "g.V().has(\"name\", \"tux\").group().by(__.label()).by(__.count())", + "canonical": "g.V().has(\"name\", \"tux\").group().by(__.label()).by(__.count())", + "anonymized": "g.V().has(string0, string1).group().by(__.label()).by(__.count())", + "dotnet": "g.V().Has(\"name\", \"tux\").Group().By(__.Label()).By(__.Count())", + "go": "g.V().Has(\"name\", \"tux\").Group().By(gremlingo.T__.Label()).By(gremlingo.T__.Count())", + "groovy": "g.V().has(\"name\", \"tux\").group().by(__.label()).by(__.count())", + "java": "g.V().has(\"name\", \"tux\").group().by(__.label()).by(__.count())", + "javascript": "g.V().has(\"name\", \"tux\").group().by(__.label()).by(__.count())", + "python": "g.V().has('name', 'tux').group().by(__.label()).by(__.count())" + } + ] + }, + { + "scenario": "g_V_hasLabelXreptileX_group_byXlabels_order_foldX_byXname_foldX", + "traversals": [ + { + "original": "g.V().hasLabel(\"reptile\").group().by(__.labels().order().fold()).by(__.values(\"name\").fold())", + "language": "g.V().hasLabel(\"reptile\").group().by(__.labels().order().fold()).by(__.values(\"name\").fold())", + "canonical": "g.V().hasLabel(\"reptile\").group().by(__.labels().order().fold()).by(__.values(\"name\").fold())", + "anonymized": "g.V().hasLabel(string0).group().by(__.labels().order().fold()).by(__.values(string1).fold())", + "dotnet": "g.V().HasLabel(\"reptile\").Group().By(__.Labels().Order().Fold()).By(__.Values(\"name\").Fold())", + "go": "g.V().HasLabel(\"reptile\").Group().By(gremlingo.T__.Labels().Order().Fold()).By(gremlingo.T__.Values(\"name\").Fold())", + "groovy": "g.V().hasLabel(\"reptile\").group().by(__.labels().order().fold()).by(__.values(\"name\").fold())", + "java": "g.V().hasLabel(\"reptile\").group().by(__.labels().order().fold()).by(__.values(\"name\").fold())", + "javascript": "g.V().hasLabel(\"reptile\").group().by(__.labels().order().fold()).by(__.values(\"name\").fold())", + "python": "g.V().has_label('reptile').group().by(__.labels().order().fold()).by(__.values('name').fold())" + } + ] + }, { "scenario": "g_V_outXcreatedX_groupCount_byXnameX", "traversals": [ @@ -46922,6 +47211,40 @@ } ] }, + { + "scenario": "g_V_hasXname_tuxX_groupCount_byXlabelX", + "traversals": [ + { + "original": "g.V().has(\"name\", \"tux\").groupCount().by(T.label)", + "language": "g.V().has(\"name\", \"tux\").groupCount().by(T.label)", + "canonical": "g.V().has(\"name\", \"tux\").groupCount().by(T.label)", + "anonymized": "g.V().has(string0, string1).groupCount().by(T.label)", + "dotnet": "g.V().Has(\"name\", \"tux\").GroupCount().By(T.Label)", + "go": "g.V().Has(\"name\", \"tux\").GroupCount().By(gremlingo.T.Label)", + "groovy": "g.V().has(\"name\", \"tux\").groupCount().by(T.label)", + "java": "g.V().has(\"name\", \"tux\").groupCount().by(T.label)", + "javascript": "g.V().has(\"name\", \"tux\").groupCount().by(T.label)", + "python": "g.V().has('name', 'tux').group_count().by(T.label)" + } + ] + }, + { + "scenario": "g_V_labels_groupCount", + "traversals": [ + { + "original": "g.V().labels().groupCount()", + "language": "g.V().labels().groupCount()", + "canonical": "g.V().labels().groupCount()", + "anonymized": "g.V().labels().groupCount()", + "dotnet": "g.V().Labels().GroupCount()", + "go": "g.V().Labels().GroupCount()", + "groovy": "g.V().labels().groupCount()", + "java": "g.V().labels().groupCount()", + "javascript": "g.V().labels().groupCount()", + "python": "g.V().labels().group_count()" + } + ] + }, { "scenario": "g_VX1X_out_name_injectXdanielX_asXaX_mapXlengthX_path", "traversals": [ diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Branch.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Branch.feature index 50d058377be..4e668ccf3b3 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Branch.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Branch.feature @@ -107,3 +107,28 @@ Feature: Step - branch() | l[josh,josh,marko,peter] | | d[27].i | | d[12].l | + + # On a multi-label graph a vertex can satisfy several branch option predicates at once, so branch() + # routes it down every matching branch. canopy [habitat] -> place; lagoon [habitat, aquatic] -> + # place + wet; atlas [animal, reptile, aquatic, endangered] -> scaly + wet; monty [animal, reptile, + # nocturnal] -> scaly. + @MultiLabel + Scenario: g_V_hasLabelXhabitat_reptileX_branchXidentityX_optionXhasLabelXaquaticXX_optionXhasLabelXreptileXX_optionXhasLabelXhabitatXX_multilabel + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("habitat", "reptile"). + branch(__.identity()). + option(__.hasLabel("aquatic"), __.constant("wet")). + option(__.hasLabel("reptile"), __.constant("scaly")). + option(__.hasLabel("habitat"), __.constant("place")) + """ + When iterated to list + Then the result should be unordered + | result | + | wet | + | wet | + | scaly | + | scaly | + | place | + | place | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Choose.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Choose.feature index 318845dd346..43881a4fa72 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Choose.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Choose.feature @@ -698,3 +698,42 @@ Feature: Step - choose() | vadas | | josh | | peter | + + # choose() with a boolean predicate branches on label set membership (hasLabel matches if ANY label + # matches), so the aquatic habitats/reptiles take the true branch and the rest take the false branch. + @MultiLabel + Scenario: g_V_hasLabelXhabitat_reptileX_chooseXhasLabelXaquaticX_constantXwetX_constantXdryXX_multilabel + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("habitat", "reptile").choose(__.hasLabel("aquatic"), __.constant("wet"), __.constant("dry")) + """ + When iterated to list + Then the result should be unordered + | result | + | wet | + | wet | + | dry | + | dry | + + # choose(T.label) selects the option matching a single, arbitrary label from the vertex's label set, so + # for the multi-label vertex tux any one of its four label branches may be taken. + @MultiLabel + Scenario: g_V_hasXname_tuxX_chooseXlabelX_optionXanimal_bird_aquatic_endangeredX_multilabel + Given the zoo graph + And the traversal of + """ + g.V().has("name", "tux").choose(T.label). + option("animal", __.constant("is-animal")). + option("bird", __.constant("is-bird")). + option("aquatic", __.constant("is-aquatic")). + option("endangered", __.constant("is-endangered")) + """ + When iterated to list + Then the result should be of + | result | + | is-animal | + | is-bird | + | is-aquatic | + | is-endangered | + And the result should have a count of 1 diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/And.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/And.feature index 3523073134a..76893c35b5d 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/And.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/And.feature @@ -80,3 +80,18 @@ Feature: Step - and() Then the result should be unordered | result | | v[marko] | + + @MultiLabel + Scenario: g_V_andXhasLabelXmammalX_hasLabelXendangeredXX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().and(__.hasLabel("mammal"), __.hasLabel("endangered")).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | blaze | + | titan | + | splash | + | tinker | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Dedup.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Dedup.feature index 9e74d755398..2da6bac418f 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Dedup.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Dedup.feature @@ -430,4 +430,17 @@ Feature: Step - dedup() g.V().both().has(T.label, "software").dedup().by("lang").by("name").values("name") """ When iterated to list - Then the traversal will raise an error with message containing text of "Dedup step can only have one by modulator" \ No newline at end of file + Then the traversal will raise an error with message containing text of "Dedup step can only have one by modulator" + + @MultiLabel + Scenario: g_V_hasLabelXhabitatX_dedup_byXlabels_order_foldX_localXlabels_order_foldX_multilabel + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("habitat").dedup().by(__.labels().order().fold()).local(__.labels().order().fold()) + """ + When iterated to list + Then the result should be unordered + | result | + | l[aquatic,habitat] | + | l[habitat] | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Has.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Has.feature index e559148f4b5..f9fee1e71a9 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Has.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Has.feature @@ -1026,3 +1026,60 @@ Feature: Step - has() | result | | e[marko-knows->vadas] | | e[marko-knows->josh] | + + @MultiLabel + Scenario: g_V_hasXlabel_aquaticX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().has(T.label, "aquatic").values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | tux | + | atlas | + | ripple | + | splash | + | lagoon | + + @MultiLabel + Scenario: g_V_hasXlabel_withinXreptile_birdXX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().has(T.label, P.within("reptile", "bird")).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | tux | + | atlas | + | monty | + + # Because label predicates match if ANY label satisfies them, has(T.label, without("animal")) matches + # every vertex that carries some label other than "animal" - which is all of them. To exclude vertices + # labeled "animal", use not(hasLabel("animal")) instead (see the Not step). + @MultiLabel + Scenario: g_V_hasXlabel_withoutXanimalXX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().has(T.label, P.without("animal")).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | tux | + | atlas | + | ripple | + | monty | + | echo | + | blaze | + | titan | + | bitsy | + | splash | + | tinker | + | lagoon | + | canopy | + | dr_gremlin | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasLabel.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasLabel.feature index 54e29938adb..59a0ae9cc18 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasLabel.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/HasLabel.feature @@ -210,4 +210,46 @@ Feature: Step - hasLabel() g.V().properties().hasLabel(null) """ When iterated to list - Then the result should be empty \ No newline at end of file + Then the result should be empty + + @MultiLabel + Scenario: g_V_hasLabelXbirdX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("bird").values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | tux | + + @MultiLabel + Scenario: g_V_hasLabelXaquaticX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("aquatic").values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | tux | + | atlas | + | ripple | + | splash | + | lagoon | + + @MultiLabel + Scenario: g_V_hasLabelXreptile_birdX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("reptile", "bird").values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | tux | + | atlas | + | monty | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Not.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Not.feature index 22430a4551d..8e604f59c14 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Not.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Not.feature @@ -59,3 +59,17 @@ Feature: Step - not() | josh | | ripple | | peter | + + @MultiLabel + Scenario: g_V_notXhasLabelXanimalXX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().not(__.hasLabel("animal")).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | lagoon | + | canopy | + | dr_gremlin | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Or.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Or.feature index cc927bb51fc..11d50aafa98 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Or.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Or.feature @@ -59,4 +59,18 @@ Feature: Step - or() | v[lop] | | v[josh] | | v[ripple] | - | v[peter] | \ No newline at end of file + | v[peter] | + + @MultiLabel + Scenario: g_V_orXhasLabelXbirdX_hasLabelXreptileXX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().or(__.hasLabel("bird"), __.hasLabel("reptile")).values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | tux | + | atlas | + | monty | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Order.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Order.feature index c49c6fca14b..d6bdfc867bb 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Order.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Order.feature @@ -548,4 +548,17 @@ Feature: Step - order() When iterated to list Then the result should be unordered | result | - | d[29].i | \ No newline at end of file + | d[29].i | + + @MultiLabel + Scenario: g_V_hasLabelXhabitatX_order_byXlabels_order_foldX_name_multilabel + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("habitat").order().by(__.labels().order().fold()).values("name") + """ + When iterated to list + Then the result should be ordered + | result | + | lagoon | + | canopy | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Group.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Group.feature index f36e2a513b6..49f969c9d23 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Group.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Group.feature @@ -407,3 +407,45 @@ Feature: Step - group() Then the result should be unordered | result | | m[{"marko":"d[3].l", "vadas":"d[1].l", "lop":"d[3].l", "josh":"d[3].l", "ripple":"d[1].l", "peter":"d[1].l"}] | + + @MultiLabel + Scenario: g_V_hasLabelXhabitatX_group_byXlabelX_byXcountX + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("habitat").group().by(T.label).by(__.count()) + """ + When iterated to list + Then the result should be of + | result | + | m[{"habitat":"d[2].l"}] | + | m[{"aquatic":"d[1].l","habitat":"d[1].l"}] | + And the result should have a count of 1 + + @MultiLabel + Scenario: g_V_hasXname_tuxX_group_byXlabelX_byXcountX + Given the zoo graph + And the traversal of + """ + g.V().has("name", "tux").group().by(__.label()).by(__.count()) + """ + When iterated to list + Then the result should be of + | result | + | m[{"animal":"d[1].l"}] | + | m[{"bird":"d[1].l"}] | + | m[{"aquatic":"d[1].l"}] | + | m[{"endangered":"d[1].l"}] | + And the result should have a count of 1 + + @MultiLabel + Scenario: g_V_hasLabelXreptileX_group_byXlabels_order_foldX_byXname_foldX + Given the zoo graph + And the traversal of + """ + g.V().hasLabel("reptile").group().by(__.labels().order().fold()).by(__.values("name").fold()) + """ + When iterated to list + Then the result should be unordered + | result | + | m[{"l[animal,aquatic,endangered,reptile]":["atlas"],"l[animal,nocturnal,reptile]":["monty"]}] | diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/GroupCount.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/GroupCount.feature index 876f282d05e..209f555e704 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/GroupCount.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/GroupCount.feature @@ -276,3 +276,32 @@ Feature: Step - groupCount() | d[4].l | | d[5].l | | d[6].l | + + + @MultiLabel + Scenario: g_V_hasXname_tuxX_groupCount_byXlabelX + Given the zoo graph + And the traversal of + """ + g.V().has("name", "tux").groupCount().by(T.label) + """ + When iterated to list + Then the result should be of + | result | + | m[{"animal":"d[1].l"}] | + | m[{"bird":"d[1].l"}] | + | m[{"aquatic":"d[1].l"}] | + | m[{"endangered":"d[1].l"}] | + And the result should have a count of 1 + + @MultiLabel + Scenario: g_V_labels_groupCount + Given the zoo graph + And the traversal of + """ + g.V().labels().groupCount() + """ + When iterated to list + Then the result should be unordered + | result | + | m[{"animal":"d[10].l","mammal":"d[7].l","nocturnal":"d[6].l","endangered":"d[6].l","aquatic":"d[5].l","reptile":"d[2].l","habitat":"d[2].l","bird":"d[1].l","flying":"d[1].l","person":"d[1].l","veterinarian":"d[1].l","keeper":"d[1].l"}] | From e72294eb1b90e3c9d0c4b43e6914b0c9203f3527 Mon Sep 17 00:00:00 2001 From: Cole Greer Date: Tue, 14 Jul 2026 15:35:57 -0700 Subject: [PATCH 10/10] docs tweak --- docs/src/reference/the-graph.asciidoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/src/reference/the-graph.asciidoc b/docs/src/reference/the-graph.asciidoc index 1c04da4b97d..48fff3f219b 100644 --- a/docs/src/reference/the-graph.asciidoc +++ b/docs/src/reference/the-graph.asciidoc @@ -214,9 +214,8 @@ supported, what its default is or whether the choice is left to the user to conf [[the-zoo-toy-graph]] TIP: A toy graph demonstrating multi-label vertices alongside a variety of property types is available at -`TinkerFactory.createTheZoo()` and `data/tinkerpop-zoo*`. Unlike "The Crew" above, which requires a graph configured -for vertex multi-properties and meta-properties, "The Zoo" requires a `LabelCardinality` of `ONE_OR_MORE` or -`ZERO_OR_MORE`. +`TinkerFactory.createTheZoo()` and `data/tinkerpop-zoo*`. "The Zoo" requires a graph supporting a `LabelCardinality` of +`ONE_OR_MORE` or `ZERO_OR_MORE`. .The Zoo image::the-zoo-graph.png[width=685]