This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch steps-taking-traversal-poc in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 7456bf6389189f3664c4d05ed453f52dd3945166 Author: Yang Xia <[email protected]> AuthorDate: Tue Jun 2 09:21:58 2026 -0700 add hasLabel(Traversal) --- .../language/grammar/DefaultGremlinBaseVisitor.java | 4 ++++ .../language/grammar/TraversalMethodVisitor.java | 9 +++++++++ .../language/translator/DotNetTranslateVisitor.java | 10 ++++++++++ .../process/traversal/dsl/graph/GraphTraversal.java | 17 +++++++++++++++++ .../gremlin/process/traversal/dsl/graph/__.java | 7 +++++++ .../test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 1 + gremlin-go/driver/cucumber/gremlin.go | 1 + gremlin-js/gremlin-javascript/test/cucumber/gremlin.js | 1 + gremlin-language/src/main/antlr4/Gremlin.g4 | 1 + gremlin-python/src/main/python/tests/feature/gremlin.py | 1 + 10 files changed, 52 insertions(+) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java index 96cd5ef97a..4fdf7c0866 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java @@ -487,6 +487,10 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im * {@inheritDoc} */ @Override public T visitTraversalMethod_hasLabel_P(final GremlinParser.TraversalMethod_hasLabel_PContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_hasLabel_Traversal(final GremlinParser.TraversalMethod_hasLabel_TraversalContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java index 1c99745f60..518196daef 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java @@ -771,6 +771,15 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> return graphTraversal.hasLabel(antlr.traversalPredicateVisitor.visitTraversalPredicate(ctx.traversalPredicate())); } + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_hasLabel_Traversal(final GremlinParser.TraversalMethod_hasLabel_TraversalContext ctx) { + final Traversal.Admin<?, ?> traversal = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()); + return graphTraversal.hasLabel((Traversal<?, ?>) traversal); + } + /** * {@inheritDoc} */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java index 7ce385c925..540f650039 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java @@ -745,6 +745,16 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor { return null; } + @Override + public Void visitTraversalMethod_hasLabel_Traversal(final GremlinParser.TraversalMethod_hasLabel_TraversalContext ctx) { + final String step = ctx.getChild(0).getText(); + sb.append(convertToPascalCase(step)); + sb.append("("); + visit(ctx.nestedTraversal()); + sb.append(")"); + return null; + } + @Override public Void visitTraversalMethod_hasLabel_String_String(final GremlinParser.TraversalMethod_hasLabel_String_StringContext ctx) { // if there is only one argument then cast to string otherwise it's ambiguous with hasLabel(P) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java index 8364cb8332..552dec3be8 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java @@ -2894,6 +2894,23 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { } } + /** + * Filters vertices, edges and vertex properties based on their label using a child traversal + * whose results are resolved at runtime against the current traverser. + * + * @param traversal the child traversal whose results are used as the label filter value + * @return the traversal with an appended {@link HasStep} + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#has-step" target="_blank">Reference Documentation - Has Step</a> + * @since 4.0.0 + */ + public default GraphTraversal<S, E> hasLabel(final Traversal<?, ?> traversal) { + if (null == traversal) return hasLabel((String) null); + ChildTraversalValidator.validateFilterContext(traversal.asAdmin()); + this.asAdmin().getGremlinLang().addStep(Symbols.hasLabel, traversal); + final HasContainer hasContainer = new HasContainer(T.label.getAccessor(), traversal.asAdmin()); + return this.asAdmin().addStep(new HasStep(this.asAdmin(), hasContainer)); + } + /** * Filters vertices, edges and vertex properties based on their label. * diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java index 3dc2ed0651..46eddc4e2f 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java @@ -1212,6 +1212,13 @@ public class __ { return __.<A>start().hasLabel(predicate); } + /** + * @see GraphTraversal#hasLabel(Traversal) + */ + public static <A> GraphTraversal<A, A> hasLabel(final Traversal<?, ?> traversal) { + return __.<A>start().hasLabel(traversal); + } + /** * @see GraphTraversal#hasId(Object, Object...) */ diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 57c02cd13a..fe1b5a8e16 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -386,6 +386,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_VXaddVXxX_idX_rejected", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(__.AddV((string) "x").Id())}}, {"g_EXaddVXxX_idX_rejected", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.E(__.AddV((string) "x").Id())}}, {"g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Property((ITraversal) __.V().Map<object>(__.Drop()).Project<object>("x").By("name"))}}, + {"g_V_propertyXaddVXtempX_projectXkX_byXnameXX_rejected", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Property((ITraversal) __.AddV((string) "temp").Project<object>("k").By("name"))}}, {"g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("name", __.V(p["vid1"]).Values<object>("name"))}}, {"g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("age", P.Gt(__.V(p["vid1"]).Values<object>("age")))}}, {"g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).V(__.Out("knows").Id()).Values<object>("name")}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index ee2808eca5..708a0efab3 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -356,6 +356,7 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_VXaddVXxX_idX_rejected": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(gremlingo.T__.AddV("x").Id())}}, "g_EXaddVXxX_idX_rejected": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E(gremlingo.T__.AddV("x").Id())}}, "g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Property(gremlingo.T__.V().Map(gremlingo.T__.Drop()).Project("x").By("name"))}}, + "g_V_propertyXaddVXtempX_projectXkX_byXnameXX_rejected": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Property(gremlingo.T__.AddV("temp").Project("k").By("name"))}}, "g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", gremlingo.T__.V(p["vid1"]).Values("name"))}}, "g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("age", gremlingo.P.Gt(gremlingo.T__.V(p["vid1"]).Values("age")))}}, "g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).V(gremlingo.T__.Out("knows").Id()).Values("name")}}, diff --git a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js index f34f594bf1..216589eff4 100644 --- a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js @@ -387,6 +387,7 @@ const gremlins = { g_VXaddVXxX_idX_rejected: [function({g}) { return g.V(__.addV("x").id()) }], g_EXaddVXxX_idX_rejected: [function({g}) { return g.E(__.addV("x").id()) }], g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected: [function({g}) { return g.V().property(__.V().map(__.drop()).project("x").by("name")) }], + g_V_propertyXaddVXtempX_projectXkX_byXnameXX_rejected: [function({g}) { return g.V().property(__.addV("temp").project("k").by("name")) }], g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification: [function({g, vid1}) { return g.V().has("name", __.V(vid1).values("name")) }], g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification: [function({g, vid1}) { return g.V().has("age", P.gt(__.V(vid1).values("age"))) }], g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification: [function({g, vid1}) { return g.V(vid1).V(__.out("knows").id()).values("name") }], diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4 index 822cb59481..17dfbaec11 100644 --- a/gremlin-language/src/main/antlr4/Gremlin.g4 +++ b/gremlin-language/src/main/antlr4/Gremlin.g4 @@ -573,6 +573,7 @@ traversalMethod_hasKey traversalMethod_hasLabel : K_HASLABEL LPAREN traversalPredicate RPAREN #traversalMethod_hasLabel_P | K_HASLABEL LPAREN stringNullableArgument (COMMA stringNullableArgumentVarargs)? RPAREN #traversalMethod_hasLabel_String_String + | K_HASLABEL LPAREN nestedTraversal RPAREN #traversalMethod_hasLabel_Traversal ; traversalMethod_hasNot diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py b/gremlin-python/src/main/python/tests/feature/gremlin.py index 00c93cd082..7e43e4c884 100644 --- a/gremlin-python/src/main/python/tests/feature/gremlin.py +++ b/gremlin-python/src/main/python/tests/feature/gremlin.py @@ -361,6 +361,7 @@ world.gremlins = { 'g_VXaddVXxX_idX_rejected': [(lambda g:g.V(__.add_v('x').id_()))], 'g_EXaddVXxX_idX_rejected': [(lambda g:g.E(__.add_v('x').id_()))], 'g_V_propertyXV_mapXdropX_projectXxX_byXnameXX_rejected': [(lambda g:g.V().property(__.V().map(__.drop()).project('x').by('name')))], + 'g_V_propertyXaddVXtempX_projectXkX_byXnameXX_rejected': [(lambda g:g.V().property(__.add_v('temp').project('k').by('name')))], 'g_V_hasXname_VXvid1X_valuesXnameXX_passes_verification': [(lambda g, vid1=None:g.V().has('name', __.V(vid1).values('name')))], 'g_V_hasXage_gtXVXvid1X_valuesXageXXX_passes_verification': [(lambda g, vid1=None:g.V().has('age', P.gt(__.V(vid1).values('age'))))], 'g_V_VXoutXknowsX_idX_valuesXnameX_passes_verification': [(lambda g, vid1=None:g.V(vid1).V(__.out('knows').id_()).values('name'))],
