Repository: tinkerpop Updated Branches: refs/heads/master 957ed09f1 -> fcd623ce3
added AddEdgeTest that leverages side-effect data to ensure Attachables are respected. Exposed PropertyTests that were OPT_OUT in RemoteGraph, but didn't need to be. Added a Python test to verify the specific ticket test passes (though tested in ProcessTestSuite). The real problem is that Python does not have Serializers for Graph elements. It only has deserializers. I added serializers of the ReferenceXXX form. This way, elements can be passed in as parameters. Added read/write graphson tests in Python to verify serialization/deserialization behavior. Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/1f823983 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/1f823983 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/1f823983 Branch: refs/heads/master Commit: 1f8239834b0c911f7d53d25a37cbee0ef26a66fc Parents: 3f06a6a Author: Marko A. Rodriguez <[email protected]> Authored: Thu Jul 20 14:44:02 2017 -0600 Committer: Marko A. Rodriguez <[email protected]> Committed: Thu Jul 20 14:44:02 2017 -0600 ---------------------------------------------------------------------- CHANGELOG.asciidoc | 1 + .../gremlin/process/remote/RemoteGraph.java | 8 --- .../traversal/step/map/GroovyAddEdgeTest.groovy | 8 +++ .../gremlin_python/structure/io/graphson.py | 51 +++++++++++++-- .../driver/test_driver_remote_connection.py | 19 +++++- .../jython/tests/structure/io/test_graphson.py | 69 +++++++++++++------- .../process/traversal/step/map/AddEdgeTest.java | 25 +++++++ 7 files changed, 141 insertions(+), 40 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1f823983/CHANGELOG.asciidoc ---------------------------------------------------------------------- diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 3a0627d..ab44755 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -28,6 +28,7 @@ TinkerPop 3.2.6 (Release Date: NOT OFFICIALLY RELEASED YET) This release also includes changes from <<release-3-1-8, 3.1.8>>. +* Added graph element GraphSON serializers in Gremlin-Python. * Initialization scripts for Gremlin Server will not timeout. * Added Gremlin.Net. * `ProfileTest` is now less stringent about assertions which will reduce burdens on providers. http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1f823983/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java index 2bc310a..d69e12e 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/RemoteGraph.java @@ -65,14 +65,6 @@ import java.util.Iterator; method = "*", reason = "hmmmm") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ProjectTest", - method = "g_V_hasLabelXpersonX_projectXa_bX_byXoutE_countX_byXageX", - reason = "Not happy in OLAP.") [email protected]( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ProjectTest", - method = "g_V_outXcreatedX_projectXa_bX_byXnameX_byXinXcreatedX_countX_order_byXselectXbX__decrX_selectXaX", - reason = "Not happy in OLAP.") [email protected]( test = "org.apache.tinkerpop.gremlin.process.traversal.CoreTraversalTest", method = "*", reason = "The test suite does not support profiling or lambdas and for groovy tests: 'Could not locate method: GraphTraversalSource.withStrategies([{traversalCategory=interface org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy$DecorationStrategy}])'") http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1f823983/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddEdgeTest.groovy ---------------------------------------------------------------------- diff --git a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddEdgeTest.groovy b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddEdgeTest.groovy index 13d3e0a..35823a1 100644 --- a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddEdgeTest.groovy +++ b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/map/GroovyAddEdgeTest.groovy @@ -86,5 +86,13 @@ public abstract class GroovyAddEdgeTest { public Traversal<Vertex, Edge> get_g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX() { new ScriptTraversal<>(g, "gremlin-groovy", "g.addV().as('first').repeat(addE('next').to(addV()).inV).times(5).addE('next').to(select('first'))") } + + @Override + public Traversal<Vertex, Edge> get_g_withSideEffectXb_bX_VXaX_addEXknowsX_toXbX_propertyXweight_0_5X() { + final Vertex a = g.V().has("name", "marko").next(); + final Vertex b = g.V().has("name", "peter").next(); + return new ScriptTraversal<>(g, "gremlin-groovy", "g.withSideEffect('b', b).V(a).addE('knows').to('b').property('weight', 0.5d)", "a", a, "b", b) + } + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1f823983/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py ---------------------------------------------------------------------- diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py index 8102ee8..0daeffa 100644 --- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py +++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py @@ -26,7 +26,6 @@ from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, T from gremlin_python.process.traversal import Binding, Bytecode, P, Traversal, Traverser, TraversalStrategy from gremlin_python.structure.graph import Edge, Property, Vertex, VertexProperty, Path - # When we fall back to a superclass's serializer, we iterate over this map. # We want that iteration order to be consistent, so we use an OrderedDict, # not a dict. @@ -60,9 +59,9 @@ class GraphSONUtil(object): def formatType(cls, prefix, type_name): return "%s:%s" % (prefix, type_name) + # Read/Write classes split to follow precedence of the Java API class GraphSONWriter(object): - def __init__(self, serializer_map=None): """ :param serializer_map: map from Python type to serializer instance implementing `dictify` @@ -96,7 +95,6 @@ class GraphSONWriter(object): class GraphSONReader(object): - def __init__(self, deserializer_map=None): """ :param deserializer_map: map from GraphSON type tag to deserializer instance implementing `objectify` @@ -147,7 +145,6 @@ class _GraphSONTypeIO(object): class _BytecodeSerializer(_GraphSONTypeIO): - @classmethod def _dictify_instructions(cls, instructions, writer): out = [] @@ -169,6 +166,49 @@ class _BytecodeSerializer(_GraphSONTypeIO): return GraphSONUtil.typedValue("Bytecode", out) +class VertexSerializer(_GraphSONTypeIO): + python_type = Vertex + + @classmethod + def dictify(cls, vertex, writer): + v = {"id": writer.toDict(vertex.id), + "label": vertex.label} + return GraphSONUtil.typedValue("Vertex", v) + + +class EdgeSerializer(_GraphSONTypeIO): + python_type = Edge + + @classmethod + def dictify(cls, edge, writer): + e = {"id": edge.id, + "label": edge.label, + "inV": writer.toDict(edge.inV.id), + "outV": writer.toDict(edge.outV.id)} + return GraphSONUtil.typedValue("Edge", e) + + +class VertexPropertySerializer(_GraphSONTypeIO): + python_type = VertexProperty + + @classmethod + def dictify(cls, vertex_property, writer): + vp = {"id": vertex_property.id, + "label": vertex_property.label, + "value": writer.toDict(vertex_property.value)} + return GraphSONUtil.typedValue("VertexProperty", vp) + + +class PropertySerializer(_GraphSONTypeIO): + python_type = Property + + @classmethod + def dictify(cls, property, writer): + p = {"key": writer.toDict(property.key), + "value": writer.toDict(property.value)} + return GraphSONUtil.typedValue("Property", p) + + class TraversalSerializer(_BytecodeSerializer): python_type = Traversal @@ -216,7 +256,7 @@ class PSerializer(_GraphSONTypeIO): def dictify(cls, p, writer): out = {"predicate": p.operator, "value": [writer.toDict(p.value), writer.toDict(p.other)] if p.other is not None else - writer.toDict(p.value)} + writer.toDict(p.value)} return GraphSONUtil.typedValue("P", out) @@ -259,7 +299,6 @@ class TypeSerializer(_GraphSONTypeIO): class _NumberIO(_GraphSONTypeIO): - @classmethod def dictify(cls, n, writer): if isinstance(n, bool): # because isinstance(False, int) and isinstance(True, int) http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1f823983/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py ---------------------------------------------------------------------- diff --git a/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py b/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py index 71cebf0..9bbccfb 100644 --- a/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py +++ b/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py @@ -59,7 +59,8 @@ class TestDriverRemoteConnection(object): assert 4 == g.V()[2:].count().next() assert 2 == g.V()[:2].count().next() # # - results = g.withSideEffect('a',['josh','peter']).V(1).out('created').in_('created').values('name').where(within('a')).toList() + results = g.withSideEffect('a', ['josh', 'peter']).V(1).out('created').in_('created').values('name').where( + within('a')).toList() assert 2 == len(results) assert 'josh' in results assert 'peter' in results @@ -153,6 +154,20 @@ class TestDriverRemoteConnection(object): with pytest.raises(Exception): x = t.side_effects["x"] + a = g.V().has("name", "marko").next() + b = g.V().has("name", "peter").next() + edge = g.withSideEffect("b", b).V(a).addE("knows").to("b").next() + assert "knows" == edge.label + assert a == edge.outV + assert b == edge.inV + g.V().has("name","marko").outE("knows").where(__.inV().has("name","peter")).drop().iterate() + ## + edge = g.withSideEffect("a", a).withSideEffect("b", b).V().limit(1).addE("knows").from_("a").to("b").next() + assert "knows" == edge.label + assert a == edge.outV + assert b == edge.inV + g.V().has("name","marko").outE("knows").where(__.inV().has("name","peter")).drop().iterate() + def test_side_effect_close(self, remote_connection): g = Graph().traversal().withRemote(remote_connection) t = g.V().aggregate('a').aggregate('b') @@ -191,7 +206,7 @@ class TestDriverRemoteConnection(object): t = future.result() assert len(t.toList()) == 6 a, = t.side_effects.keys() - assert a == 'a' + assert a == 'a' results = t.side_effects.get('a') assert results results = t.side_effects.close() http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1f823983/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py ---------------------------------------------------------------------- diff --git a/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py b/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py index 07381bf..d80d045 100644 --- a/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py +++ b/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py @@ -25,7 +25,7 @@ from mock import Mock import six from gremlin_python.statics import * -from gremlin_python.structure.graph import Vertex +from gremlin_python.structure.graph import Vertex, Edge, VertexProperty, Property from gremlin_python.structure.graph import Path from gremlin_python.structure.io.graphson import GraphSONWriter, GraphSONReader, GraphSONUtil import gremlin_python.structure.io.graphson @@ -123,13 +123,13 @@ class TestGraphSONReader(object): class TestGraphSONWriter(object): - graphson_writer = GraphSONWriter() + graphson_reader = GraphSONReader() def test_number_output(self): - assert {"@type":"g:Int64","@value":2} == json.loads(self.graphson_writer.writeObject(long(2))) - assert {"@type":"g:Int32","@value":1} == json.loads(self.graphson_writer.writeObject(1)) - assert {"@type":"g:Double","@value":3.2} == json.loads(self.graphson_writer.writeObject(3.2)) + assert {"@type": "g:Int64", "@value": 2} == json.loads(self.graphson_writer.writeObject(long(2))) + assert {"@type": "g:Int32", "@value": 1} == json.loads(self.graphson_writer.writeObject(1)) + assert {"@type": "g:Double", "@value": 3.2} == json.loads(self.graphson_writer.writeObject(3.2)) assert """true""" == self.graphson_writer.writeObject(True) def test_numbers(self): @@ -141,33 +141,33 @@ class TestGraphSONWriter(object): def test_P(self): result = {'@type': 'g:P', '@value': { - 'predicate': 'and', - 'value': [{ - '@type': 'g:P', - '@value': { - 'predicate': 'or', - 'value': [{ - '@type': 'g:P', - '@value': {'predicate': 'lt', 'value': 'b'} - }, - {'@type': 'g:P', '@value': {'predicate': 'gt', 'value': 'c'}} - ] - } - }, - {'@type': 'g:P', '@value': {'predicate': 'neq', 'value': 'd'}}]}} - - assert result == json.loads( + 'predicate': 'and', + 'value': [{ + '@type': 'g:P', + '@value': { + 'predicate': 'or', + 'value': [{ + '@type': 'g:P', + '@value': {'predicate': 'lt', 'value': 'b'} + }, + {'@type': 'g:P', '@value': {'predicate': 'gt', 'value': 'c'}} + ] + } + }, + {'@type': 'g:P', '@value': {'predicate': 'neq', 'value': 'd'}}]}} + + assert result == json.loads( self.graphson_writer.writeObject(P.lt("b").or_(P.gt("c")).and_(P.neq("d")))) def test_strategies(self): # we have a proxy model for now given that we don't want to have to have g:XXX all registered on the Gremlin traversal machine (yet) - assert {"@type": "g:SubgraphStrategy", "@value": {}} == json.loads(self.graphson_writer.writeObject(SubgraphStrategy)) + assert {"@type": "g:SubgraphStrategy", "@value": {}} == json.loads( + self.graphson_writer.writeObject(SubgraphStrategy)) assert {"@type": "g:SubgraphStrategy", "@value": { "vertices": {"@type": "g:Bytecode", "@value": {"step": [["has", "name", "marko"]]}}}} == json.loads( self.graphson_writer.writeObject(SubgraphStrategy(vertices=__.has("name", "marko")))) def test_custom_mapping(self): - # extended mapping class X(object): pass @@ -195,7 +195,6 @@ class TestGraphSONWriter(object): assert d is serdes.dictify() def test_write_long(self): - mapping = self.graphson_writer.toDict(1) assert mapping['@type'] == 'g:Int32' assert mapping['@value'] == 1 @@ -203,3 +202,25 @@ class TestGraphSONWriter(object): mapping = self.graphson_writer.toDict(long(1)) assert mapping['@type'] == 'g:Int64' assert mapping['@value'] == 1 + + def test_graph(self): + vertex = self.graphson_reader.readObject(self.graphson_writer.writeObject(Vertex(1, "person"))) + assert 1 == vertex.id + assert "person" == vertex.label + + edge = self.graphson_reader.readObject( + self.graphson_writer.writeObject(Edge(3, Vertex(1, "person"), "knows", Vertex(2, "dog")))) + assert "knows" == edge.label + assert 3 == edge.id + assert 1 == edge.outV.id + assert 2 == edge.inV.id + + vertex_property = self.graphson_reader.readObject( + self.graphson_writer.writeObject(VertexProperty(1, "age", 32))) + assert 1 == vertex_property.id + assert "age" == vertex_property.key + assert 32 == vertex_property.value + + property = self.graphson_reader.readObject(self.graphson_writer.writeObject(Property("age", 32.2))) + assert "age" == property.key + assert 32.2 == property.value http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1f823983/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 d418944..b45dfbb 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 @@ -63,6 +63,8 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, Edge> get_g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX(); + public abstract Traversal<Vertex, Edge> get_g_withSideEffectXb_bX_VXaX_addEXknowsX_toXbX_propertyXweight_0_5X(); + /////// @Deprecated @@ -262,6 +264,22 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest { @Test @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + public void g_withSideEffectXb_bX_VXaX_addEXknowsX_toXbX_propertyXweight_0_5X() { + final Traversal<Vertex, Edge> traversal = get_g_withSideEffectXb_bX_VXaX_addEXknowsX_toXbX_propertyXweight_0_5X(); + final Edge edge = traversal.next(); + assertFalse(traversal.hasNext()); + assertEquals(edge.outVertex(), convertToVertex(graph, "marko")); + assertEquals(edge.inVertex(), convertToVertex(graph, "peter")); + assertEquals("knows", edge.label()); + assertEquals(1, IteratorUtils.count(edge.properties())); + assertEquals(0.5d, edge.value("weight"), 0.1d); + assertEquals(6, IteratorUtils.count(graph.vertices())); + assertEquals(7, IteratorUtils.count(graph.edges())); + } + + @Test + @LoadGraphWith(MODERN) @Deprecated @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) public void g_V_asXaX_inXcreatedX_addInEXcreatedBy_a_year_2009_acl_publicX() { @@ -354,6 +372,13 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest { return g.V().as("a").in("created").addE("createdBy").from("a").property("year", 2009).property("acl", "public"); } + @Override + public Traversal<Vertex, Edge> get_g_withSideEffectXb_bX_VXaX_addEXknowsX_toXbX_propertyXweight_0_5X() { + final Vertex a = g.V().has("name", "marko").next(); + final Vertex b = g.V().has("name", "peter").next(); + return g.withSideEffect("b", b).V(a).addE("knows").to("b").property("weight", 0.5d); + } + /////// @Override
