From 98eb844b17ea34eab695a4ae983622aa4796803b Mon Sep 17 00:00:00 2001 From: SeH <1s1e1h1@gmail.com> Date: Sun, 7 Jun 2015 21:35:30 -0400 Subject: [PATCH] passes vertex test suite, edge and properties remaining --- pom.xml | 7 + .../spangraph/ConcurrentHashMapGraph.java | 30 +++++ .../com/syncleus/spangraph/HashMapGraph.java | 30 +++++ .../java/com/syncleus/spangraph/MapGraph.java | 122 +++++++++++------- .../spangraph/SpanGraphBlueprintsTest.java | 89 +++++++++++++ .../com/syncleus/spangraph/SpanGraphTest.java | 12 +- 6 files changed, 240 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/syncleus/spangraph/ConcurrentHashMapGraph.java create mode 100644 src/main/java/com/syncleus/spangraph/HashMapGraph.java create mode 100644 src/test/java/com/syncleus/spangraph/SpanGraphBlueprintsTest.java diff --git a/pom.xml b/pom.xml index 25de644..e559587 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,13 @@ <version>2.6.0</version> </dependency> + <dependency> + <groupId>com.tinkerpop.blueprints</groupId> + <artifactId>blueprints-test</artifactId> + <version>2.6.0</version> + <scope>test</scope> + </dependency> + </dependencies> diff --git a/src/main/java/com/syncleus/spangraph/ConcurrentHashMapGraph.java b/src/main/java/com/syncleus/spangraph/ConcurrentHashMapGraph.java new file mode 100644 index 0000000..79ac655 --- /dev/null +++ b/src/main/java/com/syncleus/spangraph/ConcurrentHashMapGraph.java @@ -0,0 +1,30 @@ +package com.syncleus.spangraph; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Uses java.util.LinkedHashMap to implement adjacency + */ +public class ConcurrentHashMapGraph extends MapGraph { + + protected ConcurrentHashMapGraph() { + super(); + + init(); + } + + protected ConcurrentHashMapGraph(String globalGraphID) { + super(globalGraphID); + } + + @Override + protected Map newEdgeMap() { + return new ConcurrentHashMap<>(); + } + + @Override + protected Map newVertexMap() { + return new ConcurrentHashMap<>(); + } +} diff --git a/src/main/java/com/syncleus/spangraph/HashMapGraph.java b/src/main/java/com/syncleus/spangraph/HashMapGraph.java new file mode 100644 index 0000000..50a975f --- /dev/null +++ b/src/main/java/com/syncleus/spangraph/HashMapGraph.java @@ -0,0 +1,30 @@ +package com.syncleus.spangraph; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Uses java.util.LinkedHashMap to implement adjacency + */ +public class HashMapGraph extends MapGraph { + + protected HashMapGraph() { + super(); + + init(); + } + + protected HashMapGraph(String globalGraphID) { + super(globalGraphID); + } + + @Override + protected Map newEdgeMap() { + return new LinkedHashMap<>(); + } + + @Override + protected Map newVertexMap() { + return new LinkedHashMap<>(); + } +} diff --git a/src/main/java/com/syncleus/spangraph/MapGraph.java b/src/main/java/com/syncleus/spangraph/MapGraph.java index b82a725..ad896d1 100644 --- a/src/main/java/com/syncleus/spangraph/MapGraph.java +++ b/src/main/java/com/syncleus/spangraph/MapGraph.java @@ -2,6 +2,8 @@ package com.syncleus.spangraph; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; +import com.google.common.primitives.Bytes; +import com.google.common.primitives.Longs; import com.tinkerpop.blueprints.*; import com.tinkerpop.blueprints.util.*; import org.infinispan.commons.util.InfinispanCollections; @@ -9,6 +11,7 @@ import org.infinispan.commons.util.WeakValueHashMap; import java.io.*; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * Blueprints Graph interface with adjacency implemented by some Map implementation. @@ -84,6 +87,10 @@ abstract public class MapGraph<X extends Serializable> implements Graph { GRAPHSON } + protected MapGraph() { + super(); + this.id = this.globalID = null; + } protected MapGraph(String id) { this(id, id); @@ -110,15 +117,31 @@ abstract public class MapGraph<X extends Serializable> implements Graph { protected abstract Map<X, Vertex> newVertexMap(); - public MVertex<X> addVertex(final Object id) { + public String newID() { + String r; + do { + long low = UUID.randomUUID().getLeastSignificantBits(); + long high = UUID.randomUUID().getMostSignificantBits(); + r = new String(Base64.getEncoder().encode( + Bytes.concat( + Longs.toByteArray(low), + Longs.toByteArray(high) + ) + )); + } while (this.vertices.containsKey(r)); + return r; + } - if (null != id) { - if (this.vertices.containsKey(id)) { - throw ExceptionFactory.vertexWithIdAlreadyExists(id); - } - } else { - throw new RuntimeException("id must be non-null"); + public MVertex<X> addVertex(Object id) { + + + if (null == id) { + id = newID(); + } + else { + + //throw new RuntimeException("id must be non-null"); // boolean done = false; // while (!done) { // idString = this.getNextId(); @@ -126,6 +149,10 @@ abstract public class MapGraph<X extends Serializable> implements Graph { // if (null == vertex) // done = true; // } + + if (this.vertices.containsKey(id)) { + throw ExceptionFactory.vertexWithIdAlreadyExists(id); + } } MVertex<X> vertex = new MVertex<X>((X) id, this); @@ -153,9 +180,6 @@ abstract public class MapGraph<X extends Serializable> implements Graph { if (!(obj instanceof MapGraph)) return false; MapGraph m = (MapGraph)obj; - - - if ((vertices.size() != m.vertices.size()) || (edges.size() != m.edges.size())) return false; @@ -241,7 +265,7 @@ abstract public class MapGraph<X extends Serializable> implements Graph { return addEdge(edgeID, o, i, null); } - public Edge addEdge(final Object id, final Vertex outVertex, final Vertex inVertex, final String label) { + public Edge addEdge(Object id, final Vertex outVertex, final Vertex inVertex, final String label) { /*if (label == null) throw ExceptionFactory.edgeLabelCanNotBeNull();*/ @@ -252,14 +276,7 @@ abstract public class MapGraph<X extends Serializable> implements Graph { throw ExceptionFactory.edgeWithIdAlreadyExist(id); } } else { - throw new RuntimeException("ID must be non-null"); -// boolean done = false; -// while (!done) { -// idString = this.getNextId(); -// edge = this.edges.get(idString); -// if (null == edge) -// done = true; -// } + id = newID(); } MEdge<X> edge = new MEdge<X>((X)id, @@ -405,8 +422,12 @@ abstract public class MapGraph<X extends Serializable> implements Graph { public Map<String, Serializable> prop(final boolean createIfMissing) { if (properties == null) { - if (createIfMissing) - properties = new LinkedHashMap(2); + if (createIfMissing) { + + //TODO make this an abstract method of MapGraph + //properties = new LinkedHashMap(2); + properties = new ConcurrentHashMap(2); + } else return InfinispanCollections.emptyMap(); } @@ -458,19 +479,24 @@ abstract public class MapGraph<X extends Serializable> implements Graph { + public boolean isGlobal() { + return graphID != null; + } + public String global() { - if (graphID == null) return null; + if (!isGlobal()) return null; - if (globalID == null) - globalID = graphID.substring(0, graphID.indexOf(':')); - return globalID; + return globalID = graphID.substring(0, graphID.indexOf(':')); } + abstract protected void beforeRemove(final String key, final Serializable oldValue); final static int PRIME = 31; final static int PRIME2 = 92821; public int hashCode() { + if (graphID == null) return id.hashCode(); + return hashL(this.id.hashCode(), global().hashCode()) ; } @@ -490,7 +516,12 @@ abstract public class MapGraph<X extends Serializable> implements Graph { public boolean equals(final Object object) { if (object.getClass()!=getClass()) return false; MElement o = (MElement)object; - return o.getId().equals(getId()) && o.global().equals(global()); + if (o.getId().equals(getId())) { + if (isGlobal()) + return o.global().equals(global()); + return true; + } + return false; } /** also includes properties in the equality test */ @@ -510,14 +541,14 @@ abstract public class MapGraph<X extends Serializable> implements Graph { public static class MVertex<X extends Serializable> extends MElement<X> implements Vertex, Serializable { - public final Map<X, Set<Edge>> outEdges = new LinkedHashMap(); - public final Map<X, Set<Edge>> inEdges = new LinkedHashMap(); + public final Map<String, Set<Edge>> outEdges = new LinkedHashMap(); + public final Map<String, Set<Edge>> inEdges = new LinkedHashMap(); - protected MVertex(final X id, final MapGraph graph) { + protected MVertex(final X id, final MapGraph<X> graph) { super(id, graph); } - public Iterable<Edge> getEdges(final Direction direction, final X... labels) { + public Iterable<Edge> getEdges(final Direction direction, final String... labels) { if (direction.equals(Direction.OUT)) { return this.getOutEdges(labels); } else if (direction.equals(Direction.IN)) @@ -527,16 +558,13 @@ abstract public class MapGraph<X extends Serializable> implements Graph { } } - @Override - public Iterable<Edge> getEdges(Direction direction, String... strings) { - return null; - } + public Iterable<Vertex> getVertices(final Direction direction, final String... labels) { return new VerticesFromEdgesIterable(this, direction, labels); } - private Iterable<Edge> getEdges(final Map<X, Set<Edge>> e, final X... labels) { + public Iterable<Edge> getEdges(final Map<String, Set<Edge>> e, final String... labels) { if (labels.length == 0) { return Iterables.concat(e.values()); } else if (labels.length == 1) { @@ -547,7 +575,7 @@ abstract public class MapGraph<X extends Serializable> implements Graph { return edges; } } else { - final Set<X> labelSet = Sets.newHashSet(labels); + final Set<String> labelSet = Sets.newHashSet(labels); return Iterables.concat(Iterables.transform(e.entrySet(), x -> { if (labelSet.contains(x.getKey())) return x.getValue(); @@ -556,10 +584,11 @@ abstract public class MapGraph<X extends Serializable> implements Graph { } } - private Iterable<Edge> getInEdges(final X... labels) { + + private Iterable<Edge> getInEdges(final String... labels) { return getEdges(inEdges, labels); } - private Iterable<Edge> getOutEdges(final X... labels) { + private Iterable<Edge> getOutEdges(final String... labels) { return getEdges(outEdges, labels); } @@ -568,7 +597,11 @@ abstract public class MapGraph<X extends Serializable> implements Graph { } public String toString() { - return new StringBuilder().append("v[").append(global()).append(':').append(getId()).append("]").toString(); + if (isGlobal()) + return new StringBuilder().append("v[").append(global()).append(':').append(getId()).append("]").toString(); + else + return new StringBuilder().append("v[").append(getId()).append("]").toString(); + } public Edge addEdge(final String label, final Vertex vertex) { @@ -603,7 +636,7 @@ abstract public class MapGraph<X extends Serializable> implements Graph { public boolean remove(Object edgeID, boolean incoming) { - Map<X, Set<Edge>> target = incoming ? inEdges : outEdges; + Map<String, Set<Edge>> target = incoming ? outEdges : inEdges; if (target.remove(edgeID)!=null) { graph().update(this); return true; @@ -613,11 +646,12 @@ abstract public class MapGraph<X extends Serializable> implements Graph { } public boolean add(MEdge<X> edge, boolean incoming) { - Map<X, Set<Edge>> target = incoming ? inEdges : outEdges; - X e = edge.id; - Set<Edge> edges = target.get(e); + Map<String, Set<Edge>> target = incoming ? outEdges : inEdges; + + String label = edge.getLabel(); + Set<Edge> edges = target.get(label); if (null == edges) { - target.put(e, edges = newEdgeSet(1)); + target.put(label, edges = newEdgeSet(1)); } if (edges.add(edge)) { graph().update(this); diff --git a/src/test/java/com/syncleus/spangraph/SpanGraphBlueprintsTest.java b/src/test/java/com/syncleus/spangraph/SpanGraphBlueprintsTest.java new file mode 100644 index 0000000..f3df7f3 --- /dev/null +++ b/src/test/java/com/syncleus/spangraph/SpanGraphBlueprintsTest.java @@ -0,0 +1,89 @@ +package com.syncleus.spangraph; + +import com.tinkerpop.blueprints.*; +import com.tinkerpop.blueprints.impls.GraphTest; + +import java.lang.reflect.Method; + +/** + * https://github.com/tinkerpop/blueprints/wiki/Property-Graph-Model-Test-Suite + */ +public class SpanGraphBlueprintsTest extends GraphTest { + + public void testVertexTestSuite() throws Exception { + this.stopWatch(); + doTestSuite(new VertexTestSuite(this)); + printTestPerformance("VertexTestSuite", this.stopWatch()); + } + + public void testEdgeTestSuite() throws Exception { + this.stopWatch(); + doTestSuite(new EdgeTestSuite(this)); + printTestPerformance("EdgeTestSuite", this.stopWatch()); + } + + public void testGraphTestSuite() throws Exception { + this.stopWatch(); + doTestSuite(new GraphTestSuite(this)); + printTestPerformance("GraphTestSuite", this.stopWatch()); + } + +// public void testKeyIndexableGraphTestSuite() throws Exception { +// this.stopWatch(); +// doTestSuite(new KeyIndexableGraphTestSuite(this)); +// printTestPerformance("KeyIndexableGraphTestSuite", this.stopWatch()); +// } +// +// public void testIndexableGraphTestSuite() throws Exception { +// this.stopWatch(); +// doTestSuite(new IndexableGraphTestSuite(this)); +// printTestPerformance("IndexableGraphTestSuite", this.stopWatch()); +// } +// +// public void testIndexTestSuite() throws Exception { +// this.stopWatch(); +// doTestSuite(new IndexTestSuite(this)); +// printTestPerformance("IndexTestSuite", this.stopWatch()); +// } +// +// public void testGraphMLReaderTestSuite() throws Exception { +// this.stopWatch(); +// doTestSuite(new GraphMLReaderTestSuite(this)); +// printTestPerformance("GraphMLReaderTestSuite", this.stopWatch()); +// } +// +// public void testGMLReaderTestSuite() throws Exception { +// this.stopWatch(); +// doTestSuite(new GMLReaderTestSuite(this)); +// printTestPerformance("GMLReaderTestSuite", this.stopWatch()); +// } +// +// public void testGraphSONReaderTestSuite() throws Exception { +// this.stopWatch(); +// doTestSuite(new GraphSONReaderTestSuite(this)); +// printTestPerformance("GraphSONReaderTestSuite", this.stopWatch()); +// } + + public Graph generateGraph() { + return new ConcurrentHashMapGraph(); + } + + @Override + public Graph generateGraph(String s) { + System.err.println("generateGraph(String ??): " + s); + return null; + } + + public void doTestSuite(final TestSuite testSuite) throws Exception { + String doTest = System.getProperty("testTinkerGraph"); + if (doTest == null || doTest.equals("true")) { + for (Method method : testSuite.getClass().getDeclaredMethods()) { + if (method.getName().startsWith("test")) { + System.out.println(method); + System.out.println("Testing " + method.getName() + "..."); + method.invoke(testSuite); + } + } + } + } +} diff --git a/src/test/java/com/syncleus/spangraph/SpanGraphTest.java b/src/test/java/com/syncleus/spangraph/SpanGraphTest.java index c2cf3d8..d919666 100644 --- a/src/test/java/com/syncleus/spangraph/SpanGraphTest.java +++ b/src/test/java/com/syncleus/spangraph/SpanGraphTest.java @@ -13,12 +13,12 @@ import static org.junit.Assert.assertTrue; public class SpanGraphTest { -// @Test -// public void testVertexEdgePropagationMemory() throws InterruptedException { -// testVertexPropagation(x -> -// new SpanGraph("memory", InfiniPeer.cluster(x)) -// ); -// } + @Test + public void testVertexEdgePropagationMemory() throws InterruptedException { + testVertexPropagation(x -> + new SpanGraph("local", InfiniPeer.local(x)) + ); + } @Test public void testVertexEdgePropagationNetwork() throws InterruptedException { -- GitLab