added support for g.addE().to().from() as well as to(vertex) and from(vertex). This makes things a little easier on the eyes.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/0c926872 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/0c926872 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/0c926872 Branch: refs/heads/TINKERPOP-1682 Commit: 0c92687229955fdb13dc4e3e9a51da1f3b95ae97 Parents: 86edf56 Author: Marko A. Rodriguez <[email protected]> Authored: Wed Aug 2 15:59:19 2017 -0600 Committer: Marko A. Rodriguez <[email protected]> Committed: Wed Aug 2 15:59:19 2017 -0600 ---------------------------------------------------------------------- CHANGELOG.asciidoc | 2 + docs/src/reference/the-traversal.asciidoc | 6 + .../traversal/dsl/graph/GraphTraversal.java | 30 ++++ .../dsl/graph/GraphTraversalSource.java | 8 + .../traversal/step/map/AddEdgeStartStep.java | 161 +++++++++++++++++++ .../process/traversal/step/map/AddEdgeTest.java | 54 ++++++- 6 files changed, 260 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0c926872/CHANGELOG.asciidoc ---------------------------------------------------------------------- diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index cbf0621..8117637 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -26,6 +26,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima TinkerPop 3.3.0 (Release Date: NOT OFFICIALLY RELEASED YET) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Added `GraphTraversalSource.addE(String)` in support of `g.addE().from().to()`. +* Added support for `to(Vertex)` and `from(Vertex)` as a shorthand for `to(V(a))` and `from(V(b))`. * Bumped to support Giraph 1.2.0. * Bumped to support Spark 2.2.0. * Detected if type checking was required in `GremlinGroovyScriptEngine` and disabled related infrastructure if not. http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0c926872/docs/src/reference/the-traversal.asciidoc ---------------------------------------------------------------------- diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index 7db09d2..6829df4 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -208,6 +208,10 @@ g.V().match( addE('friendlyCollaborator').from('a').to('b'). property(id,23).property('project',select('c').values('name')) <5> g.E(23).valueMap() +marko = g.V().has('name','marko').next() +peter = g.V().has('name','peter').next() +g.V(marko).addE('knows').to(peter) <6> +g.addE('knows').from(marko).to(peter) <7> ---- <1> Add a co-developer edge with a year-property between marko and his collaborators. @@ -216,6 +220,8 @@ g.E(23).valueMap() <4> The newly created edge is a traversable object. <5> Two arbitrary bindings in a traversal can be joined `from()`->`to()`, where `id` can be provided for graphs that supports user provided ids. +<6> Add an edge between marko and peter given the directed (detached) vertex references. +<7> Add an edge between marko and peter given the directed (detached) vertex references. [[addvertex-step]] AddVertex Step http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0c926872/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java ---------------------------------------------------------------------- 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 dcee467..99be547 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 @@ -1042,6 +1042,36 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { return this; } + /** + * When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the + * incoming vertex of the newly added {@link Edge}. + * + * @param toVertex the vertex for selecting the incoming vertex + * @return the traversal with the modified {@link AddEdgeStep} + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a> + * @since 3.3.0 + */ + public default GraphTraversal<S, E> to(final Vertex toVertex) { + this.asAdmin().getBytecode().addStep(Symbols.to, toVertex); + ((FromToModulating) this.asAdmin().getEndStep()).addTo(__.constant(toVertex).asAdmin()); + return this; + } + + /** + * When used as a modifier to {@link #addE(String)} this method specifies the traversal to use for selecting the + * outgoing vertex of the newly added {@link Edge}. + * + * @param fromVertex the vertex for selecting the outgoing vertex + * @return the traversal with the modified {@link AddEdgeStep} + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - From Step</a> + * @since 3.3.0 + */ + public default GraphTraversal<S, E> from(final Vertex fromVertex) { + this.asAdmin().getBytecode().addStep(Symbols.from, fromVertex); + ((FromToModulating) this.asAdmin().getEndStep()).addFrom(__.constant(fromVertex).asAdmin()); + return this; + } + ///////////////////// FILTER STEPS ///////////////////// /** http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0c926872/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java index f820c59..2d4b94f 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java @@ -31,6 +31,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.engine.ComputerTraversalEngine; import org.apache.tinkerpop.gremlin.process.traversal.engine.StandardTraversalEngine; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStartStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep; @@ -267,6 +268,13 @@ public class GraphTraversalSource implements TraversalSource { return traversal.addStep(new AddVertexStartStep(traversal, null)); } + public GraphTraversal<Edge, Edge> addE(final String label) { + final GraphTraversalSource clone = this.clone(); + clone.bytecode.addStep(GraphTraversal.Symbols.addE, label); + final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone); + return traversal.addStep(new AddEdgeStartStep(traversal, label)); + } + public <S> GraphTraversal<S, S> inject(S... starts) { final GraphTraversalSource clone = this.clone(); clone.bytecode.addStep(GraphTraversal.Symbols.inject, starts); http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0c926872/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java new file mode 100644 index 0000000..9d2eec5 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tinkerpop.gremlin.process.traversal.step.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Parameterizing; +import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator; +import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating; +import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating; +import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; +import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory; +import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public final class AddEdgeStartStep extends AbstractStep<Edge, Edge> + implements Mutating<Event.EdgeAddedEvent>, TraversalParent, Parameterizing, Scoping, FromToModulating { + + private static final String FROM = Graph.Hidden.hide("from"); + private static final String TO = Graph.Hidden.hide("to"); + + private boolean first = true; + private Parameters parameters = new Parameters(); + private CallbackRegistry<Event.EdgeAddedEvent> callbackRegistry; + + public AddEdgeStartStep(final Traversal.Admin traversal, final String edgeLabel) { + super(traversal); + this.parameters.set(this, T.label, edgeLabel); + } + + @Override + public <S, E> List<Traversal.Admin<S, E>> getLocalChildren() { + return this.parameters.getTraversals(); + } + + @Override + public Parameters getParameters() { + return this.parameters; + } + + @Override + public Set<String> getScopeKeys() { + return this.parameters.getReferencedLabels(); + } + + @Override + public void addPropertyMutations(final Object... keyValues) { + this.parameters.set(this, keyValues); + } + + @Override + public void addTo(final Traversal.Admin<?, ?> toObject) { + this.parameters.set(this, TO, toObject); + } + + @Override + public void addFrom(final Traversal.Admin<?, ?> fromObject) { + this.parameters.set(this, FROM, fromObject); + } + + @Override + protected Traverser.Admin<Edge> processNextStart() { + if (this.first) { + this.first = false; + final TraverserGenerator generator = this.getTraversal().getTraverserGenerator(); + final Traverser.Admin traverser = generator.generate(1, (Step) this, 1); // a dead traverser to trigger the traversal + Vertex toVertex = (Vertex) this.parameters.get(traverser, TO, Collections::emptyList).get(0); + Vertex fromVertex = (Vertex) this.parameters.get(traverser, FROM, Collections::emptyList).get(0); + if (toVertex instanceof Attachable) + toVertex = ((Attachable<Vertex>) toVertex) + .attach(Attachable.Method.get(this.getTraversal().getGraph().orElse(EmptyGraph.instance()))); + if (fromVertex instanceof Attachable) + fromVertex = ((Attachable<Vertex>) fromVertex) + .attach(Attachable.Method.get(this.getTraversal().getGraph().orElse(EmptyGraph.instance()))); + final String edgeLabel = this.parameters.get(T.label, () -> Edge.DEFAULT_LABEL).get(0); + + final Edge edge = fromVertex.addEdge(edgeLabel, toVertex, this.parameters.getKeyValues(traverser, TO, FROM, T.label)); + if (callbackRegistry != null) { + final Event.EdgeAddedEvent vae = new Event.EdgeAddedEvent(DetachedFactory.detach(edge, true)); + callbackRegistry.getCallbacks().forEach(c -> c.accept(vae)); + } + return generator.generate(edge, this, 1L); + } else + throw FastNoSuchElementException.instance(); + + + } + + @Override + public Set<TraverserRequirement> getRequirements() { + return this.getSelfAndChildRequirements(TraverserRequirement.OBJECT); + } + + @Override + public CallbackRegistry<Event.EdgeAddedEvent> getMutatingCallbackRegistry() { + if (null == this.callbackRegistry) this.callbackRegistry = new ListCallbackRegistry<>(); + return this.callbackRegistry; + } + + @Override + public int hashCode() { + return super.hashCode() ^ this.parameters.hashCode(); + } + + @Override + public String toString() { + return StringFactory.stepString(this, this.parameters.toString()); + } + + @Override + public void setTraversal(final Traversal.Admin<?, ?> parentTraversal) { + super.setTraversal(parentTraversal); + this.parameters.getTraversals().forEach(this::integrateChild); + } + + @Override + public AddEdgeStartStep clone() { + final AddEdgeStartStep clone = (AddEdgeStartStep) super.clone(); + clone.parameters = this.parameters.clone(); + return clone; + } + +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0c926872/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java ---------------------------------------------------------------------- diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java index 46d1766..337c09f 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java @@ -30,7 +30,6 @@ import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -65,6 +64,10 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, Edge> get_g_withSideEffectXb_bX_VXaX_addEXknowsX_toXbX_propertyXweight_0_5X(); + public abstract Traversal<Vertex, Edge> get_g_VXaX_addEXknowsX_toXbX_propertyXweight_0_1X(); + + public abstract Traversal<Edge, Edge> get_g_addEXknowsX_fromXaX_toXbX_propertyXweight_0_1X(); + /////// @Test @@ -211,6 +214,41 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest { assertEquals(Arrays.asList(1L, 1L, 1L, 1L, 1L, 1L), g.V().map(outE().count()).toList()); } + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + public void g_VXaX_addEXknowsX_toXbX_propertyXweight_0_1X() { + final Traversal<Vertex, Edge> traversal = get_g_VXaX_addEXknowsX_toXbX_propertyXweight_0_1X(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals(edge.outVertex(), convertToVertex(graph, "marko")); + assertEquals(edge.inVertex(), convertToVertex(graph, "peter")); + assertEquals("knows", edge.label()); + assertEquals(1, IteratorUtils.count(edge.properties())); + assertEquals(0.1d, edge.value("weight"), 0.1d); + assertEquals(6L, g.V().count().next().longValue()); + assertEquals(7L, g.E().count().next().longValue()); + + + } + + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + public void g_addEXknowsX_fromXaX_toXbX_propertyXweight_0_1X() { + final Traversal<Edge, Edge> traversal = get_g_addEXknowsX_fromXaX_toXbX_propertyXweight_0_1X(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals(edge.outVertex(), convertToVertex(graph, "marko")); + assertEquals(edge.inVertex(), convertToVertex(graph, "peter")); + assertEquals("knows", edge.label()); + assertEquals(1, IteratorUtils.count(edge.properties())); + assertEquals(0.1d, edge.value("weight"), 0.1d); + assertEquals(6L, g.V().count().next().longValue()); + assertEquals(7L, g.E().count().next().longValue()); + + + } public static class Traversals extends AddEdgeTest { @@ -250,5 +288,19 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest { public Traversal<Vertex, Edge> get_g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX() { return g.addV().as("first").repeat(__.addE("next").to(__.addV()).inV()).times(5).addE("next").to(select("first")); } + + @Override + public Traversal<Vertex, Edge> get_g_VXaX_addEXknowsX_toXbX_propertyXweight_0_1X() { + final Vertex a = g.V().has("name", "marko").next(); + final Vertex b = g.V().has("name", "peter").next(); + return g.V(a).addE("knows").to(b).property("weight", 0.1d); + } + + @Override + public Traversal<Edge, Edge> get_g_addEXknowsX_fromXaX_toXbX_propertyXweight_0_1X() { + final Vertex a = g.V().has("name", "marko").next(); + final Vertex b = g.V().has("name", "peter").next(); + return g.addE("knows").from(a).to(b).property("weight", 0.1d); + } } }
