This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch gvalue in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 19c5552dc60e911c5f89003852f9e48f5cc34abb Author: Stephen Mallette <[email protected]> AuthorDate: Wed Aug 21 15:35:01 2024 -0400 wip - hasLabel(String, String...) fixes, more GValue utility --- .../language/grammar/GenericLiteralVisitor.java | 14 +++ .../language/grammar/TraversalMethodVisitor.java | 27 +++++- .../translator/DotNetTranslateVisitor.java | 2 +- .../traversal/dsl/graph/GraphTraversal.java | 2 +- .../gremlin/process/traversal/step/GType.java | 40 ++++---- .../gremlin/process/traversal/step/GValue.java | 55 ++++++++++- .../process/traversal/step/map/AddEdgeStep.java | 4 +- .../process/traversal/step/map/GraphStep.java | 4 +- .../strategy/decoration/ElementIdStrategy.java | 7 +- .../language/translator/GremlinTranslatorTest.java | 9 ++ .../gremlin/process/traversal/step/GValueTest.java | 102 +++++++++++++++++++++ .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 11 ++- gremlin-go/driver/cucumber/gremlin.go | 3 +- .../gremlin-javascript/test/cucumber/gremlin.js | 3 +- gremlin-python/src/main/python/radish/gremlin.py | 3 +- .../gremlin/test/features/map/Conjoin.feature | 4 +- .../traversal/step/sideEffect/Neo4jGraphStep.java | 4 +- .../traversal/step/sideEffect/TinkerGraphStep.java | 4 +- 18 files changed, 253 insertions(+), 45 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java index ced6c832d1..fb124ec910 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java @@ -658,4 +658,18 @@ public class GenericLiteralVisitor extends DefaultGremlinBaseVisitor<Object> { else return StringEscapeUtils.unescapeJava(stripQuotes(ctx.getText())); } + + @Override + public Object[] visitStringLiteralVarargs(final GremlinParser.StringLiteralVarargsContext ctx) { + if (ctx == null) { + return new Object[0]; + } + return ctx.children + .stream() + .filter(Objects::nonNull) + .filter(p -> p instanceof GremlinParser.StringNullableArgumentContext) + .map(p -> (GremlinParser.StringNullableArgumentContext) p) + .map(antlr.argumentVisitor::visitStringNullableArgument) + .toArray(Object[]::new); + } } 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 daf72820d4..9878b96a9a 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 @@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.GValue; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality; +import java.util.Arrays; import java.util.Map; import java.util.function.BiFunction; @@ -756,10 +757,30 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> @Override public GraphTraversal visitTraversalMethod_hasLabel_String_String(final GremlinParser.TraversalMethod_hasLabel_String_StringContext ctx) { if (ctx.getChildCount() == 4) { - return graphTraversal.hasLabel(antlr.argumentVisitor.parseString(ctx.stringNullableArgument())); + final Object literalOrVar = antlr.argumentVisitor.visitStringNullableArgument(ctx.stringNullableArgument()); + if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) + return graphTraversal.hasLabel((GValue) literalOrVar); + else + return graphTraversal.hasLabel((String) literalOrVar); } else { - return graphTraversal.hasLabel(antlr.argumentVisitor.parseString(ctx.stringNullableArgument()), - antlr.genericVisitor.parseStringVarargs(ctx.stringLiteralVarargs())); + Object literalOrVar = antlr.argumentVisitor.visitStringNullableArgument(ctx.stringNullableArgument()); + Object[] literalOrVars = antlr.genericVisitor.visitStringLiteralVarargs(ctx.stringLiteralVarargs()); + + // if any are GValue then they all need to be GValue to call hasLabel + if (literalOrVar instanceof GValue || Arrays.stream(literalOrVars).anyMatch(lov -> lov instanceof GValue)) { + literalOrVar = GValue.of(literalOrVar); + literalOrVars = Arrays.stream(literalOrVars).map(GValue::of).toArray(); + } + + // since we normalized above to gvalue or literal we can just test the first arg for gvalue-ness + if (GValue.valueInstanceOf(literalOrVar, GType.STRING)) { + final GValue[] gvalueLiteralOrVars = literalOrVars == null ? null : Arrays.stream(literalOrVars).map(o -> (GValue) o).toArray(GValue[]::new); + return graphTraversal.hasLabel((GValue) literalOrVar, (GValue[]) literalOrVars); + } else { + // convert object array to string array + final String[] stringLiteralOrVars = literalOrVars == null ? null : Arrays.stream(literalOrVars).map(o -> (String) o).toArray(String[]::new); + return graphTraversal.hasLabel((String) literalOrVar, stringLiteralOrVars); + } } } 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 717fc536cb..7ab9819c2c 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 @@ -879,7 +879,7 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor { @Override public Void visitTraversalMethod_option_Object_Traversal(final GremlinParser.TraversalMethod_option_Object_TraversalContext ctx) { - if (ctx.genericLiteralArgument().genericLiteral().traversalMerge() != null) { + if (ctx.genericLiteralArgument().genericLiteral() != null && ctx.genericLiteralArgument().genericLiteral().traversalMerge() != null) { visit(ctx.getChild(0)); sb.append("("); visit(ctx.genericLiteralArgument()); 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 fed7899485..375a9993b8 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 @@ -447,7 +447,7 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#has-step" target="_blank">Reference Documentation - Has Step</a> * @since 3.2.2 */ - public default GraphTraversal<S, E> hasLabel(final Object label, final Object... otherLabels) { + public default GraphTraversal<S, E> hasLabel(final GValue<String> label, final GValue<String>... otherLabels) { this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.hasLabel, label, otherLabels); // groovy evaluation seems to do strange things with varargs given hasLabel(null, null). odd someone would diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java index 58c1fdfe4d..4c8777d290 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java @@ -33,23 +33,31 @@ import java.util.Set; * An enum that describes types that are used in the Gremlin language. */ public enum GType { - BIG_DECIMAL, - BIG_INTEGER, - BOOLEAN, - DOUBLE, - EDGE, - INTEGER, - LIST, - LONG, - MAP, - PATH, - PROPERTY, - SET, - STRING, - UNKNOWN, - VERTEX; + BIG_DECIMAL(BigDecimal.class), + BIG_INTEGER(BigInteger.class), + BOOLEAN(Boolean.class), + DOUBLE(Double.class), + EDGE(Edge.class), + INTEGER(Integer.class), + LIST(List.class), + LONG(Long.class), + MAP(Map.class), + PATH(Path.class), + PROPERTY(Property.class), + SET(Set.class), + STRING(String.class), + UNKNOWN(null), + VERTEX(Vertex.class); - GType() {} + private Class<?> javaType; + + GType(final Class<?> javaType) { + this.javaType = javaType; + } + + public Class<?> getJavaType() { + return this.javaType; + } /** * Returns {@code true} if the type is a number. diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java index da0694acdc..6f5c6223f6 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java @@ -21,16 +21,17 @@ package org.apache.tinkerpop.gremlin.process.traversal.step; import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; /** @@ -113,21 +114,25 @@ public class GValue<V> implements Cloneable, Serializable { } /** - * Create a new {@code Var} from a particular value but without the specified name. + * Create a new {@code Var} from a particular value but without the specified name. If the argument provide is + * already a {@code GValue} then it is returned as-is. * * @param value the value of the variable */ public static <V> GValue<V> of(final V value) { + if (value instanceof GValue) return (GValue) value; return new GValue<>(GType.getType(value), value); } /** - * Create a new {@code Var} with the specified name and value. + * Create a new {@code Var} with the specified name and value.. If the argument provide is already a + * {@code GValue} then it is returned as-is. * * @param name the name of the variable * @param value the value of the variable */ public static <V> GValue<V> of(final String name, final V value) { + if (value instanceof GValue) throw new IllegalArgumentException("value cannot be a GValue"); return new GValue<>(name, GType.getType(value), value); } @@ -326,4 +331,48 @@ public class GValue<V> implements Cloneable, Serializable { public static GValue<Property> ofProperty(final String name, final Property value) { return new GValue<>(name, GType.PROPERTY, value); } + + /** + * Tests if the object is a {@link GValue} and if so, checks the type of the value against the provided + * {@link GType}. + */ + public static boolean valueInstanceOf(final Object o, final GType type) { + return o instanceof GValue && ((GValue) o).getType() == type; + } + + /** + * Checks the type of the object against the provided {@link GType}. If the object is a {@link GValue} then it + * can directly check the type, otherwise it will test the given object's class itself using the mappign on the + * {@link GType}. + */ + public static boolean instanceOf(final Object o, final GType type) { + // todo: is this right for null? + if (null == o) + return false; + else if (o instanceof GValue) + return ((GValue) o).getType() == type; + else + return o.getClass().isAssignableFrom(type.getJavaType()); + } + + /** + * Returns {@code true} if the object is a collection or a {@link GValue} that contains a {@link Collection}. + */ + public static boolean instanceOfCollection(final Object o) { + return o instanceof Collection || (o instanceof GValue && ((GValue) o).getType().isCollection()); + } + + /** + * Returns {@code true} if the object is an element or a {@link GValue} that contains an {@link Element}. + */ + public static boolean instanceOfElement(final Object o) { + return o instanceof Element || (o instanceof GValue && ((GValue) o).getType().isElement()); + } + + /** + * Returns {@code true} if the object is a number or a {@link GValue} that contains a number. + */ + public static boolean instanceOfNumber(final Object o) { + return o instanceof Number || (o instanceof GValue && ((GValue) o).getType().isNumeric()); + } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java index 82c7f3893b..39c37bf9f5 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java @@ -114,7 +114,7 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge> edgeLabel, e.getMessage())); } - if (!(theTo instanceof Vertex) && (theTo instanceof GValue && ((GValue) theTo).getType() != GType.VERTEX)) + if (!(theTo instanceof Vertex) || (theTo instanceof GValue && ((GValue) theTo).getType() != GType.VERTEX)) throw new IllegalStateException(String.format( "The value given to addE(%s).to() must resolve to a Vertex but %s was specified instead", edgeLabel, null == theTo ? "null" : theTo.getClass().getSimpleName())); @@ -128,7 +128,7 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge> edgeLabel, e.getMessage())); } - if (!(theFrom instanceof Vertex) && (theFrom instanceof GValue && ((GValue) theTo).getType() != GType.VERTEX)) + if (!(theFrom instanceof Vertex) || (theFrom instanceof GValue && ((GValue) theTo).getType() != GType.VERTEX)) throw new IllegalStateException(String.format( "The value given to addE(%s).to() must resolve to a Vertex but %s was specified instead", edgeLabel, null == theFrom ? "null" : theFrom.getClass().getSimpleName())); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java index 784ef087f3..0a4635240a 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java @@ -144,7 +144,7 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen /** * Get the ids associated with this step. If there are {@link GValue} objects present they will be returned - * alongside literal ids. Prefer {@link #getResolvedIds()} if you prefer to work with literal ids only. + * alongside literal ids. Prefer {@link #getIdsAsValues()} if you prefer to work with literal ids only. */ public GValue[] getIds() { return this.ids; @@ -153,7 +153,7 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen /** * Gets the ids associated with this step as literal values rather than {@link GValue} objects. */ - public Object[] getResolvedIds() { + public Object[] getIdsAsValues() { if (legacyLogicForPassingNoIds) return null; return resolveToValues(this.ids); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java index 67c08254a8..e47b782185 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ElementIdStrategy.java @@ -94,11 +94,12 @@ public final class ElementIdStrategy extends AbstractTraversalStrategy<Traversal // note that it is then only necessary to replace the step if the id is a non-element. other tests // in the suite validate that items in getIds() is uniform so it is ok to just test the first item // in the list. - if (graphStep.getIds().length > 0 && !(graphStep.getIds()[0] instanceof Element)) { + final Object[] ids = graphStep.getIdsAsValues(); + if (ids.length > 0 && !(ids[0] instanceof Element)) { if (graphStep instanceof HasContainerHolder) - ((HasContainerHolder) graphStep).addHasContainer(new HasContainer(this.idPropertyKey, P.within(Arrays.asList(graphStep.getIds())))); + ((HasContainerHolder) graphStep).addHasContainer(new HasContainer(this.idPropertyKey, P.within(Arrays.asList(ids)))); else - TraversalHelper.insertAfterStep(new HasStep(traversal, new HasContainer(this.idPropertyKey, P.within(Arrays.asList(graphStep.getIds())))), graphStep, traversal); + TraversalHelper.insertAfterStep(new HasStep(traversal, new HasContainer(this.idPropertyKey, P.within(Arrays.asList(ids)))), graphStep, traversal); graphStep.clearIds(); } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java index 90022b34d5..7c920f2613 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java @@ -1063,6 +1063,15 @@ public class GremlinTranslatorTest { "g.mergeV(new LinkedHashMap<Object, Object>() {{ put(\"name\", \"alice\"); put(T.label, \"person\"); }}).option(Merge.onCreate, new LinkedHashMap<Object, Object>() {{ put(\"age\", Cardinality.single(81)); }})", "g.mergeV(new Map([[\"name\", \"alice\"], [T.label, \"person\"]])).option(Merge.onCreate, new Map([[\"age\", CardinalityValue.single(81)]]))", "g.merge_v({ 'name': 'alice', T.label: 'person' }).option(Merge.on_create, { 'age': CardinalityValue.single(81) })"}, + {"g.V().branch(__.label().is(\"person\").count()).option(xx1, __.values(\"age\")).option(xx2, __.values(\"lang\")).option(xx2, __.values(\"name\")).option(Pick.any, __.label())", + null, + "g.V().branch(__.label().is(string0).count()).option(xx1, __.values(string1)).option(xx2, __.values(string2)).option(xx2, __.values(string3)).option(Pick.any, __.label())", + "g.V().Branch<object>(__.Label().Is(\"person\").Count()).Option(xx1, __.Values<object>(\"age\")).Option(xx2, __.Values<object>(\"lang\")).Option(xx2, __.Values<object>(\"name\")).Option(Pick.Any, __.Label())", + "g.V().Branch(gremlingo.T__.Label().Is(\"person\").Count()).Option(xx1, gremlingo.T__.Values(\"age\")).Option(xx2, gremlingo.T__.Values(\"lang\")).Option(xx2, gremlingo.T__.Values(\"name\")).Option(gremlingo.Pick.Any, gremlingo.T__.Label())", + null, + null, + null, + "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().toList()", null, null, diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java index f4ae56dff8..07f3fe7f7e 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java @@ -37,10 +37,25 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertSame; import static org.mockito.Mockito.mock; public class GValueTest { + @Test + public void shouldReturnAnExistingGValue() { + final GValue<Integer> gValue = GValue.of(123); + final Object returnedGValue = GValue.of(gValue); + assertEquals(gValue, returnedGValue); + assertSame(gValue, returnedGValue); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldReturnAnExistInTypedGValue() { + final Object gValue = GValue.of("x", 123); + GValue.of("x", gValue); + } + @Test public void shouldCreateGValueFromValue() { final GValue<Integer> gValue = GValue.of(123); @@ -313,4 +328,91 @@ public class GValueTest { assertEquals("varName", gValue.getName()); assertThat(gValue.isVariable(), is(true)); } + + @Test + public void shouldBeAnInstanceOf() { + assertThat(GValue.instanceOf(GValue.of("string"), GType.STRING), is(true)); + assertThat(GValue.instanceOf(GValue.ofInteger(1), GType.INTEGER), is(true)); + assertThat(GValue.instanceOf("string", GType.STRING), is(true)); + assertThat(GValue.instanceOf(1, GType.INTEGER), is(true)); + } + + @Test + public void shouldNotBeAnInstanceOf() { + assertThat(GValue.instanceOf(GValue.of("string"), GType.INTEGER), is(false)); + assertThat(GValue.instanceOf(GValue.ofInteger(1), GType.STRING), is(false)); + assertThat(GValue.instanceOf("string", GType.INTEGER), is(false)); + assertThat(GValue.instanceOf(1, GType.STRING), is(false)); + } + + @Test + public void shouldBeAnInstanceOfCollection() { + assertThat(GValue.instanceOfCollection(GValue.of(Arrays.asList("string"))), is(true)); + assertThat(GValue.instanceOfCollection(GValue.ofSet(new HashSet(Arrays.asList("string")))), is(true)); + assertThat(GValue.instanceOfCollection(Arrays.asList("string")), is(true)); + assertThat(GValue.instanceOfCollection(new HashSet(Arrays.asList("string"))), is(true)); + } + + @Test + public void shouldNotBeAnInstanceOfCollection() { + assertThat(GValue.instanceOfCollection(GValue.of(new HashMap())), is(false)); + assertThat(GValue.instanceOfCollection(GValue.ofInteger(1)), is(false)); + assertThat(GValue.instanceOfCollection(new HashMap()), is(false)); + assertThat(GValue.instanceOfCollection(1), is(false)); + } + + @Test + public void shouldBeAnInstanceOfNumber() { + assertThat(GValue.instanceOfNumber(GValue.of(1)), is(true)); + assertThat(GValue.instanceOfNumber(GValue.of(1L)), is(true)); + assertThat(GValue.instanceOfNumber(GValue.of(1D)), is(true)); + assertThat(GValue.instanceOfNumber(GValue.of(BigInteger.valueOf((1L)))), is(true)); + assertThat(GValue.instanceOfNumber(GValue.of(BigDecimal.valueOf((1.0)))), is(true)); + assertThat(GValue.instanceOfNumber(GValue.ofInteger(1)), is(true)); + assertThat(GValue.instanceOfNumber(GValue.ofLong(1L)), is(true)); + assertThat(GValue.instanceOfNumber(GValue.ofDouble(1D)), is(true)); + assertThat(GValue.instanceOfNumber(GValue.ofBigInteger(BigInteger.valueOf((1L)))), is(true)); + assertThat(GValue.instanceOfNumber(GValue.ofBigDecimal(BigDecimal.valueOf((1.0)))), is(true)); + } + + @Test + public void shouldNotBeAnInstanceOfNumber() { + assertThat(GValue.instanceOfNumber(GValue.of("string")), is(false)); + assertThat(GValue.instanceOfNumber(GValue.of(Arrays.asList("string"))), is(false)); + } + + @Test + public void shouldBeAnInstanceOfElement() { + assertThat(GValue.instanceOfElement(GValue.ofVertex(mock(Vertex.class))), is(true)); + assertThat(GValue.instanceOfElement(GValue.ofEdge(mock(Edge.class))), is(true)); + } + + @Test + public void shouldNotBeAnInstanceOfElement() { + assertThat(GValue.instanceOfElement(GValue.of("string")), is(false)); + assertThat(GValue.instanceOfElement(GValue.of(Arrays.asList("string"))), is(false)); + } + + @Test + public void valueInstanceOfShouldReturnTrueForMatchingType() { + GValue<Integer> gValue = GValue.of(123); + assertThat(GValue.valueInstanceOf(gValue, GType.INTEGER), is(true)); + } + + @Test + public void valueInstanceOfShouldReturnFalseForNonMatchingType() { + GValue<Integer> gValue = GValue.of(123); + assertThat(GValue.valueInstanceOf(gValue, GType.STRING), is(false)); + } + + @Test + public void valueInstanceOfShouldReturnFalseForNonGValueObject() { + String nonGValue = "test"; + assertThat(GValue.valueInstanceOf(nonGValue, GType.STRING), is(false)); + } + + @Test + public void valueInstanceOfShouldReturnFalseForNullObject() { + assertThat(GValue.valueInstanceOf(null, GType.STRING), is(false)); + } } \ No newline at end of file diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 81c6b1fe26..f9388d42a0 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -63,17 +63,17 @@ namespace Gremlin.Net.IntegrationTest.Gherkin new Dictionary<string, List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>> { {"g_V_branchXlabel_eq_person__a_bX_optionXa__ageX_optionXb__langX_optionXb__nameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>()}, // skipping as it contains a lambda - {"g_V_branchXlabel_isXpersonX_countX_optionX1__ageX_optionX0__langX_optionX0__nameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Branch<object>(__.Label().Is("person").Count()).Option(p["xx1"], (ITraversal) __.Values<object>("age")).Option(p["xx2"], (ITraversal) __.Values<object>("lang")).Option(p["xx2"], (ITraversal) __.Values<object>("name"))}}, - {"g_V_branchXlabel_isXpersonX_countX_optionX1__ageX_optionX0__langX_optionX0__nameX_optionXany__labelX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Branch<object>(__.Label().Is("person").Count()).Option(p["xx1"], (ITraversal) __.Values<object>("age")).Option(p["xx2"], (ITraversal) __.Values<object>("lang")).Option(p["xx2"], (ITraversal) __.Values<object>("name")).Option(Pick.Any, __.Label())}}, + {"g_V_branchXlabel_isXpersonX_countX_optionX1__ageX_optionX0__langX_optionX0__nameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Branch<object>(__.Label().Is("person").Count()).Option(p["xx1"], __.Values<object>("age")).Option(p["xx2"], __.Values<object>("lang")).Option(p["xx2"], __.Values<object>("name"))}}, + {"g_V_branchXlabel_isXpersonX_countX_optionX1__ageX_optionX0__langX_optionX0__nameX_optionXany__labelX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Branch<object>(__.Label().Is("person").Count()).Option(p["xx1"], __.Values<object>("age")).Option(p["xx2"], __.Values<object>("lang")).Option(p["xx2"], __.Values<object>("name")).Option(Pick.Any, __.Label())}}, {"g_V_branchXageX_optionXltX30X__youngX_optionXgtX30X__oldX_optionXnone__on_the_edgeX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Branch<object>(__.Values<object>("age")).Option(P.Lt(30), __.Constant<object>("young")).Option(P.Gt(30), __.Constant<object>("old")).Option(Pick.None, __.Constant<object>("on the edge"))}}, {"g_V_branchXidentityX_optionXhasLabelXsoftwareX__inXcreatedX_name_order_foldX_optionXhasXname_vadasX__ageX_optionXneqX123X__bothE_countX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Branch<object>(__.Identity()).Option(__.HasLabel("software"), __.In("created").Values<object>("name").Order().Fold()).Option(__.Has("name", "vadas"), __.Values<object>("age")).Option(P.Neq(123), __.BothE().Count())}}, - {"g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Choose<object>(__.Out().Count()).Option(p["xx1"], (ITraversal) __.Values<object>("name")).Option(p["xx2"], (ITraversal) __.Values<object>("age"))}}, + {"g_V_chooseXout_countX_optionX2L_nameX_optionX3L_ageX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Choose<object>(__.Out().Count()).Option(p["xx1"], __.Values<object>("name")).Option(p["xx2"], __.Values<object>("age"))}}, {"g_V_chooseXlabel_eqXpersonX__outXknowsX__inXcreatedXX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>()}, // skipping as it contains a lambda {"g_V_chooseXhasLabelXpersonX_and_outXcreatedX__outXknowsX__identityX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Choose<object>(__.HasLabel("person").And().Out("created"), __.Out("knows"), __.Identity()).Values<object>("name")}}, {"g_V_chooseXlabelX_optionXblah__outXknowsXX_optionXbleep__outXcreatedXX_optionXnone__identityX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Choose<object>(__.Label()).Option("blah", __.Out("knows")).Option("bleep", __.Out("created")).Option(Pick.None, __.Identity()).Values<object>("name")}}, {"g_V_chooseXoutXknowsX_count_isXgtX0XX__outXknowsXX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Choose<object>(__.Out("knows").Count().Is(P.Gt(0)), __.Out("knows")).Values<object>("name")}}, {"g_V_hasLabelXpersonX_asXp1X_chooseXoutEXknowsX__outXknowsXX_asXp2X_selectXp1_p2X_byXnameX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").As("p1").Choose<object>(__.OutE("knows"), __.Out("knows")).As("p2").Select<object>("p1", "p2").By("name")}}, - {"g_V_hasLabelXpersonX_chooseXageX__optionX27L__constantXyoungXX_optionXnone__constantXoldXX_groupCount", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Choose<object>(__.Values<object>("age")).Option(p["xx1"], (ITraversal) __.Constant<object>("young")).Option(Pick.None, __.Constant<object>("old")).GroupCount<object>()}}, + {"g_V_hasLabelXpersonX_chooseXageX__optionX27L__constantXyoungXX_optionXnone__constantXoldXX_groupCount", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Choose<object>(__.Values<object>("age")).Option(p["xx1"], __.Constant<object>("young")).Option(Pick.None, __.Constant<object>("old")).GroupCount<object>()}}, {"g_injectX1X_chooseXisX1X__constantX10Xfold__foldX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx2"]).Choose<object>(__.Is(p["xx2"]), __.Constant<object>(p["xx1"]).Fold(), __.Fold<object>())}}, {"g_injectX2X_chooseXisX1X__constantX10Xfold__foldX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx3"]).Choose<object>(__.Is(p["xx2"]), __.Constant<object>(p["xx1"]).Fold(), __.Fold<object>())}}, {"g_V_localXpropertiesXlocationX_order_byXvalueX_limitX2XX_value", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.Properties<object>("location").Order().By(T.Value, Order.Asc).Range<object>(0, 2)).Value<object>()}}, @@ -707,7 +707,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_valuesXnameX_conjoinX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Conjoin("1")}}, {"g_V_valuesXnonexistantX_fold_conjoinX_X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("nonexistant").Fold().Conjoin(";")}}, {"g_V_valuesXnameX_order_fold_conjoinX_X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Order().Fold().Conjoin("_")}}, - {"g_V_valuesXageX_order_fold_conjoinX_X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Order().Fold().Conjoin(";")}}, + {"g_V_valuesXageX_order_fold_conjoinXsemicolonX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Order().Fold().Conjoin(";")}}, + {"g_V_valuesXageX_order_fold_conjoinXslashX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Order().Fold().Conjoin(p["xx1"])}}, {"g_V_out_path_byXvaluesXnameX_toUpperX_conjoinXMARKOX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Out().Path().By(__.Values<object>("name").ToUpper()).Conjoin("MARKO")}}, {"g_injectXmarkoX_conjoinX_X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(p["xx1"]).Conjoin("-")}}, {"g_V_valueMapXlocationX_selectXvaluesX_unfold_orderXlocalX_conjoinX1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object, object>("location").Select<object>(Column.Values).Unfold<object>().Order(Scope.Local).Conjoin("1")}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index dba9316ffb..55feb35dfe 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -677,7 +677,8 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_valuesXnameX_conjoinX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Conjoin("1")}}, "g_V_valuesXnonexistantX_fold_conjoinX_X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("nonexistant").Fold().Conjoin(";")}}, "g_V_valuesXnameX_order_fold_conjoinX_X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Order().Fold().Conjoin("_")}}, - "g_V_valuesXageX_order_fold_conjoinX_X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Order().Fold().Conjoin(";")}}, + "g_V_valuesXageX_order_fold_conjoinXsemicolonX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Order().Fold().Conjoin(";")}}, + "g_V_valuesXageX_order_fold_conjoinXslashX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Order().Fold().Conjoin(p["xx1"])}}, "g_V_out_path_byXvaluesXnameX_toUpperX_conjoinXMARKOX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Out().Path().By(gremlingo.T__.Values("name").ToUpper()).Conjoin("MARKO")}}, "g_injectXmarkoX_conjoinX_X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Conjoin("-")}}, "g_V_valueMapXlocationX_selectXvaluesX_unfold_orderXlocalX_conjoinX1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().ValueMap("location").Select(gremlingo.Column.Values).Unfold().Order(gremlingo.Scope.Local).Conjoin("1")}}, diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js index e140b5a3b0..b8e81d76da 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js @@ -694,7 +694,8 @@ const gremlins = { g_V_valuesXnameX_conjoinX1X: [function({g}) { return g.V().values("name").conjoin("1") }], g_V_valuesXnonexistantX_fold_conjoinX_X: [function({g}) { return g.V().values("nonexistant").fold().conjoin(";") }], g_V_valuesXnameX_order_fold_conjoinX_X: [function({g}) { return g.V().values("name").order().fold().conjoin("_") }], - g_V_valuesXageX_order_fold_conjoinX_X: [function({g}) { return g.V().values("age").order().fold().conjoin(";") }], + g_V_valuesXageX_order_fold_conjoinXsemicolonX: [function({g}) { return g.V().values("age").order().fold().conjoin(";") }], + g_V_valuesXageX_order_fold_conjoinXslashX: [function({g, xx1}) { return g.V().values("age").order().fold().conjoin(xx1) }], g_V_out_path_byXvaluesXnameX_toUpperX_conjoinXMARKOX: [function({g}) { return g.V().out().path().by(__.values("name").toUpper()).conjoin("MARKO") }], g_injectXmarkoX_conjoinX_X: [function({g, xx1}) { return g.inject(xx1).conjoin("-") }], g_V_valueMapXlocationX_selectXvaluesX_unfold_orderXlocalX_conjoinX1X: [function({g}) { return g.V().valueMap("location").select(Column.values).unfold().order(Scope.local).conjoin("1") }], diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 72aaefa200..5cddea94f5 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -676,7 +676,8 @@ world.gremlins = { 'g_V_valuesXnameX_conjoinX1X': [(lambda g:g.V().values('name').conjoin('1'))], 'g_V_valuesXnonexistantX_fold_conjoinX_X': [(lambda g:g.V().values('nonexistant').fold().conjoin(';'))], 'g_V_valuesXnameX_order_fold_conjoinX_X': [(lambda g:g.V().values('name').order().fold().conjoin('_'))], - 'g_V_valuesXageX_order_fold_conjoinX_X': [(lambda g:g.V().values('age').order().fold().conjoin(';'))], + 'g_V_valuesXageX_order_fold_conjoinXsemicolonX': [(lambda g:g.V().values('age').order().fold().conjoin(';'))], + 'g_V_valuesXageX_order_fold_conjoinXslashX': [(lambda g, xx1=None:g.V().values('age').order().fold().conjoin(xx1))], 'g_V_out_path_byXvaluesXnameX_toUpperX_conjoinXMARKOX': [(lambda g:g.V().out().path().by(__.values('name').to_upper()).conjoin('MARKO'))], 'g_injectXmarkoX_conjoinX_X': [(lambda g, xx1=None:g.inject(xx1).conjoin('-'))], 'g_V_valueMapXlocationX_selectXvaluesX_unfold_orderXlocalX_conjoinX1X': [(lambda g:g.V().value_map('location').select(Column.values).unfold().order(Scope.local).conjoin('1'))], diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Conjoin.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Conjoin.feature index 4c9cdb483b..8f05f14f0d 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Conjoin.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Conjoin.feature @@ -59,7 +59,7 @@ Feature: Step - conjoin() | result | | josh_lop_marko_peter_ripple_vadas | - Scenario: g_V_valuesXageX_order_fold_conjoinX_X + Scenario: g_V_valuesXageX_order_fold_conjoinXsemicolonX Given the modern graph And the traversal of """ @@ -70,7 +70,7 @@ Feature: Step - conjoin() | result | | 27;29;32;35 | - Scenario: g_V_valuesXageX_order_fold_conjoinX_X + Scenario: g_V_valuesXageX_order_fold_conjoinXslashX Given the modern graph And using the parameter xx1 defined as "/" And the traversal of diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java index abd0a6b801..90d7327cc7 100644 --- a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java +++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java @@ -66,7 +66,7 @@ public final class Neo4jGraphStep<S, E extends Element> extends GraphStep<S, E> private Iterator<? extends Edge> edges() { if (null == this.ids) return Collections.emptyIterator(); - return IteratorUtils.filter(this.getTraversal().getGraph().get().edges(this.getResolvedIds()), edge -> HasContainer.testAll(edge, this.hasContainers)); + return IteratorUtils.filter(this.getTraversal().getGraph().get().edges(this.getIdsAsValues()), edge -> HasContainer.testAll(edge, this.hasContainers)); } private Iterator<? extends Vertex> vertices() { @@ -76,7 +76,7 @@ public final class Neo4jGraphStep<S, E extends Element> extends GraphStep<S, E> // ids are present, filter on them first if (ids.length > 0) - return IteratorUtils.filter(graph.vertices(this.getResolvedIds()), vertex -> HasContainer.testAll(vertex, hasContainers)); + return IteratorUtils.filter(graph.vertices(this.getIdsAsValues()), vertex -> HasContainer.testAll(vertex, hasContainers)); ////// do index lookups ////// graph.tx().readWrite(); // get a label being search on diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java index 9715c0e4e4..5a3d59e80b 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java @@ -71,7 +71,7 @@ public final class TinkerGraphStep<S, E extends Element> extends GraphStep<S, E> final AbstractTinkerGraph graph = (AbstractTinkerGraph) this.getTraversal().getGraph().get(); final HasContainer indexedContainer = getIndexKey(Edge.class); Iterator<Edge> iterator; - final Object[] resolvedIds = this.getResolvedIds(); + final Object[] resolvedIds = this.getIdsAsValues(); // ids are present, filter on them first if (null == resolvedIds) iterator = Collections.emptyIterator(); @@ -94,7 +94,7 @@ public final class TinkerGraphStep<S, E extends Element> extends GraphStep<S, E> final AbstractTinkerGraph graph = (AbstractTinkerGraph) this.getTraversal().getGraph().get(); final HasContainer indexedContainer = getIndexKey(Vertex.class); Iterator<? extends Vertex> iterator; - final Object[] resolvedIds = this.getResolvedIds(); + final Object[] resolvedIds = this.getIdsAsValues(); // ids are present, filter on them first if (null == resolvedIds) iterator = Collections.emptyIterator();
