You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2015/05/28 17:56:00 UTC
[1/2] incubator-tinkerpop git commit: Neo4j-Gremlin back in the mix
-- All Apache2. Builds -- tests fail cause it can't find impl classes.
Repository: incubator-tinkerpop
Updated Branches:
refs/heads/neo4j-gremlin-apache [created] b2c49ddd3
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertex.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertex.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertex.java
new file mode 100644
index 0000000..ed148ef
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertex.java
@@ -0,0 +1,283 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.structure.util.wrapped.WrappedVertex;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import org.neo4j.tinkerpop.api.Neo4jDirection;
+import org.neo4j.tinkerpop.api.Neo4jNode;
+import org.neo4j.tinkerpop.api.Neo4jRelationship;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Stream;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class Neo4jVertex extends Neo4jElement implements Vertex, WrappedVertex<Neo4jNode> {
+
+ protected static final String LABEL_DELIMINATOR = "::";
+
+ public Neo4jVertex(final Neo4jNode node, final Neo4jGraph graph) {
+ super(node, graph);
+ }
+
+ @Override
+ public <V> VertexProperty<V> property(final String key) {
+ if (this.removed) throw Element.Exceptions.elementAlreadyRemoved(Vertex.class, this.getBaseVertex().getId());
+ this.graph.tx().readWrite();
+ if (!this.graph.supportsMultiProperties) {
+ return existsInNeo4j(key) ? new Neo4jVertexProperty<V>(this, key, (V) this.getBaseVertex().getProperty(key)) : VertexProperty.<V>empty();
+ } else {
+ if (existsInNeo4j(key)) {
+ if (this.getBaseVertex().getProperty(key).equals(Neo4jVertexProperty.VERTEX_PROPERTY_TOKEN)) {
+ if (this.getBaseVertex().degree(Neo4jDirection.OUTGOING, Neo4jVertexProperty.VERTEX_PROPERTY_PREFIX.concat(key)) > 1)
+ throw Vertex.Exceptions.multiplePropertiesExistForProvidedKey(key);
+ else
+ return new Neo4jVertexProperty<>(this, this.getBaseVertex().relationships(
+ Neo4jDirection.OUTGOING,
+ Neo4jVertexProperty.VERTEX_PROPERTY_PREFIX.concat(key))
+ .iterator().next().end());
+ } else {
+ return new Neo4jVertexProperty<>(this, key, (V) this.getBaseVertex().getProperty(key));
+ }
+ } else
+ return VertexProperty.<V>empty();
+ }
+ }
+
+ @Override
+ public <V> VertexProperty<V> property(final String key, final V value) {
+ return this.property(VertexProperty.Cardinality.single, key, value);
+ }
+
+ // TODO:?
+ @Override
+ public <V> VertexProperty<V> property(String key, V value, Object... keyValues) {
+ return this.property(VertexProperty.Cardinality.single, key, value, keyValues);
+ }
+
+ @Override
+ public <V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, final V value, final Object... keyValues) {
+ if (this.removed) throw Element.Exceptions.elementAlreadyRemoved(Vertex.class, this.getBaseVertex().getId());
+ ElementHelper.validateProperty(key, value);
+ if (ElementHelper.getIdValue(keyValues).isPresent())
+ throw VertexProperty.Exceptions.userSuppliedIdsNotSupported();
+ this.graph.tx().readWrite();
+ try {
+ if (!this.graph.supportsMultiProperties) {
+ this.getBaseVertex().setProperty(key, value);
+ return new Neo4jVertexProperty<>(this, key, value);
+ } else {
+ final Optional<VertexProperty<V>> optionalVertexProperty = ElementHelper.stageVertexProperty(this, cardinality, key, value, keyValues);
+ if (optionalVertexProperty.isPresent()) return optionalVertexProperty.get();
+
+ final String prefixedKey = Neo4jVertexProperty.VERTEX_PROPERTY_PREFIX.concat(key);
+ if (this.getBaseVertex().hasProperty(key)) {
+ if (this.getBaseVertex().getProperty(key).equals(Neo4jVertexProperty.VERTEX_PROPERTY_TOKEN)) {
+ final Neo4jNode node = this.graph.getBaseGraph().createNode(Neo4jVertexProperty.VERTEX_PROPERTY_LABEL, key);
+ node.setProperty(T.key.getAccessor(), key);
+ node.setProperty(T.value.getAccessor(), value);
+ this.getBaseVertex().connectTo(node, prefixedKey);
+ final Neo4jVertexProperty<V> property = new Neo4jVertexProperty<>(this, node);
+ ElementHelper.attachProperties(property, keyValues); // TODO: make this inlined
+ return property;
+ } else {
+ Neo4jNode node = this.graph.getBaseGraph().createNode(Neo4jVertexProperty.VERTEX_PROPERTY_LABEL, key);
+ node.setProperty(T.key.getAccessor(), key);
+ node.setProperty(T.value.getAccessor(), this.getBaseVertex().removeProperty(key));
+ this.getBaseVertex().connectTo(node, prefixedKey);
+ this.getBaseVertex().setProperty(key, Neo4jVertexProperty.VERTEX_PROPERTY_TOKEN);
+ node = this.graph.getBaseGraph().createNode(Neo4jVertexProperty.VERTEX_PROPERTY_LABEL, key);
+ node.setProperty(T.key.getAccessor(), key);
+ node.setProperty(T.value.getAccessor(), value);
+ this.getBaseVertex().connectTo(node, prefixedKey);
+ final Neo4jVertexProperty<V> property = new Neo4jVertexProperty<>(this, node);
+ ElementHelper.attachProperties(property, keyValues); // TODO: make this inlined
+ return property;
+ }
+ } else {
+ this.getBaseVertex().setProperty(key, value);
+ final Neo4jVertexProperty<V> property = new Neo4jVertexProperty<>(this, key, value);
+ ElementHelper.attachProperties(property, keyValues); // TODO: make this inlined
+ return property;
+ }
+ }
+ } catch (IllegalArgumentException iae) {
+ throw Property.Exceptions.dataTypeOfPropertyValueNotSupported(value);
+ }
+ }
+
+ @Override
+ public void remove() {
+ if (this.removed) throw Element.Exceptions.elementAlreadyRemoved(Vertex.class, this.getBaseVertex().getId());
+ this.removed = true;
+ this.graph.tx().readWrite();
+ try {
+ final Neo4jNode node = this.getBaseVertex();
+ for (final Neo4jRelationship relationship : node.relationships(Neo4jDirection.BOTH)) {
+ final Neo4jNode otherNode = relationship.other(node);
+ if (otherNode.hasLabel(Neo4jVertexProperty.VERTEX_PROPERTY_LABEL)) {
+ otherNode.relationships(null).forEach(Neo4jRelationship::delete);
+ otherNode.delete(); // meta property node
+ } else
+ relationship.delete();
+ }
+ node.delete();
+ } catch (final IllegalStateException ignored) {
+ // this one happens if the vertex is still chilling in the tx
+ } catch (final RuntimeException ex) {
+ if (!Neo4jHelper.isNotFound(ex)) throw ex;
+ // this one happens if the vertex is committed
+ }
+ }
+
+ @Override
+ public Edge addEdge(final String label, final Vertex inVertex, final Object... keyValues) {
+ if (null == inVertex) throw Graph.Exceptions.argumentCanNotBeNull("vertex");
+ if (this.removed) throw Element.Exceptions.elementAlreadyRemoved(Vertex.class, this.getBaseVertex().getId());
+ ElementHelper.validateLabel(label);
+ ElementHelper.legalPropertyKeyValueArray(keyValues);
+ if (ElementHelper.getIdValue(keyValues).isPresent())
+ throw Edge.Exceptions.userSuppliedIdsNotSupported();
+
+ this.graph.tx().readWrite();
+ final Neo4jNode node = (Neo4jNode) this.baseElement;
+ final Neo4jEdge edge = new Neo4jEdge(node.connectTo(((Neo4jVertex) inVertex).getBaseVertex(),
+ label), this.graph);
+ ElementHelper.attachProperties(edge, keyValues);
+ return edge;
+ }
+
+ @Override
+ public Neo4jNode getBaseVertex() {
+ return (Neo4jNode) this.baseElement;
+ }
+
+ @Override
+ public String label() {
+ this.graph.tx().readWrite();
+ return String.join(LABEL_DELIMINATOR, this.labels());
+ }
+
+ /////////////// Neo4jVertex Specific Methods for Multi-Label Support ///////////////
+ public Set<String> labels() {
+ this.graph.tx().readWrite();
+ final Set<String> labels = new TreeSet<>(this.getBaseVertex().labels());
+ return Collections.unmodifiableSet(labels);
+ }
+
+ public void addLabel(final String label) {
+ this.graph.tx().readWrite();
+ this.getBaseVertex().addLabel(label);
+ }
+
+ public void removeLabel(final String label) {
+ this.graph.tx().readWrite();
+ this.getBaseVertex().removeLabel(label);
+ }
+ //////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public String toString() {
+ return StringFactory.vertexString(this);
+ }
+
+ @Override
+ public Iterator<Vertex> vertices(final Direction direction, final String... edgeLabels) {
+ this.graph.tx().readWrite();
+ return new Iterator<Vertex>() {
+ final Iterator<Neo4jRelationship> relationshipIterator = IteratorUtils.filter(0 == edgeLabels.length ?
+ getBaseVertex().relationships(Neo4jHelper.mapDirection(direction)).iterator() :
+ getBaseVertex().relationships(Neo4jHelper.mapDirection(direction), (edgeLabels)).iterator(), r -> !r.type().startsWith(Neo4jVertexProperty.VERTEX_PROPERTY_PREFIX));
+
+ @Override
+ public boolean hasNext() {
+ return this.relationshipIterator.hasNext();
+ }
+
+ @Override
+ public Neo4jVertex next() {
+ return new Neo4jVertex(this.relationshipIterator.next().other(getBaseVertex()), graph);
+ }
+ };
+ }
+
+ @Override
+ public Iterator<Edge> edges(final Direction direction, final String... edgeLabels) {
+ this.graph.tx().readWrite();
+ return new Iterator<Edge>() {
+ final Iterator<Neo4jRelationship> relationshipIterator = IteratorUtils.filter(0 == edgeLabels.length ?
+ getBaseVertex().relationships(Neo4jHelper.mapDirection(direction)).iterator() :
+ getBaseVertex().relationships(Neo4jHelper.mapDirection(direction), (edgeLabels)).iterator(), r -> !r.type().startsWith(Neo4jVertexProperty.VERTEX_PROPERTY_PREFIX));
+
+ @Override
+ public boolean hasNext() {
+ return this.relationshipIterator.hasNext();
+ }
+
+ @Override
+ public Neo4jEdge next() {
+ return new Neo4jEdge(this.relationshipIterator.next(), graph);
+ }
+ };
+ }
+
+ @Override
+ public <V> Iterator<VertexProperty<V>> properties(final String... propertyKeys) {
+ this.graph.tx().readWrite();
+ return IteratorUtils.stream(getBaseVertex().getKeys())
+ .filter(key -> ElementHelper.keyExists(key, propertyKeys))
+ .flatMap(key -> {
+ if (getBaseVertex().getProperty(key).equals(Neo4jVertexProperty.VERTEX_PROPERTY_TOKEN))
+ return IteratorUtils.stream(getBaseVertex().relationships(Neo4jDirection.OUTGOING, (Neo4jVertexProperty.VERTEX_PROPERTY_PREFIX.concat(key))))
+ .map(relationship -> (VertexProperty<V>) new Neo4jVertexProperty(Neo4jVertex.this, relationship.end()));
+ else
+ return Stream.of(new Neo4jVertexProperty<>(Neo4jVertex.this, key, (V) this.getBaseVertex().getProperty(key)));
+ }).iterator();
+ }
+
+ private boolean existsInNeo4j(final String key) {
+ try {
+ return this.getBaseVertex().hasProperty(key);
+ } catch (IllegalStateException ex) {
+ // if vertex is removed before/after transaction close
+ throw Element.Exceptions.elementAlreadyRemoved(Vertex.class, this.id());
+ } catch (RuntimeException ex) {
+ // if vertex is removed before/after transaction close
+ if (Neo4jHelper.isNotFound(ex))
+ throw Element.Exceptions.elementAlreadyRemoved(Vertex.class, this.id());
+ throw ex;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertexProperty.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertexProperty.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertexProperty.java
new file mode 100644
index 0000000..1c538b8
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertexProperty.java
@@ -0,0 +1,211 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.structure.util.wrapped.WrappedVertex;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import org.neo4j.tinkerpop.api.Neo4jDirection;
+import org.neo4j.tinkerpop.api.Neo4jNode;
+import org.neo4j.tinkerpop.api.Neo4jRelationship;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class Neo4jVertexProperty<V> implements VertexProperty<V>, WrappedVertex<Neo4jNode> {
+
+ public static final String VERTEX_PROPERTY_LABEL = "vertexProperty";
+ public static final String VERTEX_PROPERTY_PREFIX = Graph.Hidden.hide("");
+ public static final String VERTEX_PROPERTY_TOKEN = Graph.Hidden.hide("vertexProperty");
+
+
+ private Neo4jNode node;
+ private final Neo4jVertex vertex;
+ private final String key;
+ private final V value;
+
+
+ public Neo4jVertexProperty(final Neo4jVertex vertex, final String key, final V value) {
+ this.vertex = vertex;
+ this.key = key;
+ this.value = value;
+ this.node = null;
+ }
+
+ public Neo4jVertexProperty(final Neo4jVertex vertex, final Neo4jNode node) {
+ this.vertex = vertex;
+ this.node = node;
+ this.key = (String) node.getProperty(T.key.getAccessor());
+ this.value = (V) node.getProperty(T.value.getAccessor());
+ }
+
+ @Override
+ public Vertex element() {
+ return this.vertex;
+ }
+
+ @Override
+ public Object id() {
+ // TODO: Neo4j needs a better ID system for VertexProperties
+ return (long) (this.key.hashCode() + this.value.hashCode() + this.vertex.id().hashCode());
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ return ElementHelper.areEqual(this, object);
+ }
+
+ @Override
+ public int hashCode() {
+ return ElementHelper.hashCode((Element) this);
+ }
+
+ @Override
+ public Neo4jNode getBaseVertex() {
+ return this.node;
+ }
+
+ @Override
+ public <U> Property<U> property(String key, U value) {
+ if (!this.vertex.graph.supportsMetaProperties)
+ throw VertexProperty.Exceptions.metaPropertiesNotSupported();
+
+ ElementHelper.validateProperty(key, value);
+ this.vertex.graph.tx().readWrite();
+ if (isNode()) {
+ this.node.setProperty(key, value);
+ return new Neo4jProperty<>(this, key, value);
+ } else {
+ this.node = this.vertex.graph.getBaseGraph().createNode(VERTEX_PROPERTY_LABEL, this.label());
+ this.node.setProperty(T.key.getAccessor(), this.key);
+ this.node.setProperty(T.value.getAccessor(), this.value);
+ this.node.setProperty(key, value);
+ this.vertex.getBaseVertex().connectTo(this.node, VERTEX_PROPERTY_PREFIX.concat(this.key));
+ this.vertex.getBaseVertex().setProperty(this.key, VERTEX_PROPERTY_TOKEN);
+ return new Neo4jProperty<>(this, key, value);
+ }
+ }
+
+ @Override
+ public <U> Property<U> property(final String key) {
+ if (!this.vertex.graph.supportsMetaProperties)
+ throw VertexProperty.Exceptions.metaPropertiesNotSupported();
+
+ this.vertex.graph.tx().readWrite();
+ try {
+ if (isNode() && this.node.hasProperty(key))
+ return new Neo4jProperty<>(this, key, (U) this.node.getProperty(key));
+ else
+ return Property.empty();
+ } catch (IllegalStateException ex) {
+ throw Element.Exceptions.elementAlreadyRemoved(this.getClass(), this.id());
+ } catch (RuntimeException ex) {
+ if (Neo4jHelper.isNotFound(ex))
+ throw Element.Exceptions.elementAlreadyRemoved(this.getClass(), this.id());
+ throw ex;
+ }
+ }
+
+ @Override
+ public String key() {
+ return this.key;
+ }
+
+ @Override
+ public V value() throws NoSuchElementException {
+ return this.value;
+ }
+
+ @Override
+ public Set<String> keys() {
+ if (!this.vertex.graph.supportsMetaProperties)
+ throw VertexProperty.Exceptions.metaPropertiesNotSupported();
+
+ if (isNode()) {
+ this.vertex.graph.tx().readWrite();
+ final Set<String> keys = new HashSet<>();
+ for (final String key : this.node.getKeys()) {
+ if (!Graph.Hidden.isHidden(key))
+ keys.add(key);
+ }
+ return keys;
+ } else {
+ return Collections.emptySet();
+ }
+ }
+
+ @Override
+ public boolean isPresent() {
+ return null != this.value;
+ }
+
+ @Override
+ public void remove() {
+ this.vertex.graph.tx().readWrite();
+ if (!this.vertex.graph.supportsMetaProperties) {
+ if (this.vertex.getBaseVertex().hasProperty(this.key))
+ this.vertex.getBaseVertex().removeProperty(this.key);
+ } else {
+ if (isNode()) {
+ this.node.relationships(null).forEach(Neo4jRelationship::delete);
+ this.node.delete();
+ if (this.vertex.getBaseVertex().degree(Neo4jDirection.OUTGOING, VERTEX_PROPERTY_PREFIX.concat(this.key)) == 0) {
+ if (this.vertex.getBaseVertex().hasProperty(this.key))
+ this.vertex.getBaseVertex().removeProperty(this.key);
+ }
+ } else {
+ if (this.vertex.getBaseVertex().degree(Neo4jDirection.OUTGOING, VERTEX_PROPERTY_PREFIX.concat(this.key)) == 0) {
+ if (this.vertex.getBaseVertex().hasProperty(this.key))
+ this.vertex.getBaseVertex().removeProperty(this.key);
+ }
+ }
+ }
+ }
+
+ private boolean isNode() {
+ return null != this.node;
+ }
+
+ @Override
+ public String toString() {
+ return StringFactory.propertyString(this);
+ }
+
+ @Override
+ public <U> Iterator<Property<U>> properties(final String... propertyKeys) {
+ if (!isNode()) return Collections.emptyIterator();
+ else {
+ this.vertex.graph().tx().readWrite();
+ return IteratorUtils.map(IteratorUtils.filter(this.node.getKeys().iterator(), key -> !key.equals(T.key.getAccessor()) && !key.equals(T.value.getAccessor()) && ElementHelper.keyExists(key, propertyKeys)), key -> (Property<U>) new Neo4jProperty<>(Neo4jVertexProperty.this, key, (V) this.node.getProperty(key)));
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin b/neo4j-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin
new file mode 100644
index 0000000..f4cbea8
--- /dev/null
+++ b/neo4j-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin
@@ -0,0 +1 @@
+org.apache.tinkerpop.gremlin.neo4j.groovy.plugin.Neo4jGremlinPlugin
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/AbstractNeo4jGraphProvider.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/AbstractNeo4jGraphProvider.java b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/AbstractNeo4jGraphProvider.java
new file mode 100644
index 0000000..b96c52a
--- /dev/null
+++ b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/AbstractNeo4jGraphProvider.java
@@ -0,0 +1,156 @@
+/*
+ * 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.neo4j;
+
+import org.apache.tinkerpop.gremlin.AbstractGraphProvider;
+import org.apache.tinkerpop.gremlin.LoadGraphWith;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jEdge;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jElement;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraphVariables;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jProperty;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jVertex;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jVertexProperty;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.commons.configuration.Configuration;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class AbstractNeo4jGraphProvider extends AbstractGraphProvider {
+ private static final Set<Class> IMPLEMENTATIONS = new HashSet<Class>() {{
+ add(Neo4jEdge.class);
+ add(Neo4jElement.class);
+ add(Neo4jGraph.class);
+ add(Neo4jGraphVariables.class);
+ add(Neo4jProperty.class);
+ add(Neo4jVertex.class);
+ add(Neo4jVertexProperty.class);
+ }};
+
+ @Override
+ public void clear(final Graph graph, final Configuration configuration) throws Exception {
+ if (null != graph) {
+ if (graph.features().graph().supportsTransactions() && graph.tx().isOpen())
+ graph.tx().rollback();
+ graph.close();
+ }
+
+ if (configuration.containsKey("gremlin.neo4j.directory")) {
+ // this is a non-in-sideEffects configuration so blow away the directory
+ final File graphDirectory = new File(configuration.getString("gremlin.neo4j.directory"));
+ deleteDirectory(graphDirectory);
+ }
+ }
+
+ @Override
+ public void loadGraphData(final Graph graph, final LoadGraphWith loadGraphWith, final Class testClass, final String testName) {
+ if (loadGraphWith != null) this.createIndices((Neo4jGraph) graph, loadGraphWith.value());
+ super.loadGraphData(graph, loadGraphWith, testClass, testName);
+ }
+
+ private void createIndices(final Neo4jGraph g, final LoadGraphWith.GraphData graphData) {
+ final Random random = new Random();
+ final int pick = random.nextInt(3);
+ //final int pick = 2;
+ if (graphData.equals(LoadGraphWith.GraphData.GRATEFUL)) {
+ if (pick == 1) {
+ g.tx().readWrite();
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :artist(name)");
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :song(name)");
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :song(songType)");
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :song(performances)");
+ g.tx().commit();
+ } else if (pick == 2) {
+ g.tx().readWrite();
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "name");
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "songType");
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "performances");
+ g.tx().commit();
+ }
+ } else if (graphData.equals(LoadGraphWith.GraphData.MODERN)) {
+ if (pick == 1) {
+ g.tx().readWrite();
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :person(name)");
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :person(age)");
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :software(name)");
+ if (random.nextBoolean()) {
+ createIndex(g, "CREATE INDEX ON :software(lang)");
+ }
+ g.tx().commit();
+ } else if (pick == 2) {
+ g.tx().readWrite();
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "name");
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "age");
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "lang");
+ g.tx().commit();
+ }
+ } else if (graphData.equals(LoadGraphWith.GraphData.CLASSIC)) {
+ if (pick == 1) {
+ g.tx().readWrite();
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :vertex(name)");
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :vertex(age)");
+ if (random.nextBoolean())
+ createIndex(g, "CREATE INDEX ON :vertex(lang)");
+ g.tx().commit();
+ } else if (pick == 2) {
+ g.tx().readWrite();
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "name");
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "age");
+ if (random.nextBoolean())
+ g.getBaseGraph().autoIndexProperties(true, "lang");
+ g.tx().commit();
+ }
+ } else {
+ // TODO: add CREW work here.
+ // TODO: add meta_property indices when meta_property graph is provided
+ //throw new RuntimeException("Could not load graph with " + graphData);
+ }
+ }
+
+ private void createIndex(Neo4jGraph g, String indexQuery) {
+ Iterator<Map<String, Object>> it = g.getBaseGraph().execute(indexQuery, null);
+ while (it.hasNext()) it.next();
+ }
+
+ @Override
+ public Set<Class> getImplementations() {
+ return IMPLEMENTATIONS;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/BaseNeo4jGraphTest.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/BaseNeo4jGraphTest.java b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/BaseNeo4jGraphTest.java
new file mode 100644
index 0000000..f283898
--- /dev/null
+++ b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/BaseNeo4jGraphTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.neo4j;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This should only be used for Neo4j-specific testing that is not related to the Gremlin test suite.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class BaseNeo4jGraphTest {
+ protected Configuration conf;
+ protected final DefaultNeo4jGraphProvider graphProvider = new DefaultNeo4jGraphProvider();
+ protected Neo4jGraph graph;
+ protected GraphTraversalSource g;
+
+ @Rule
+ public TestName name = new TestName();
+
+ /*@Before
+ public void before() throws Exception {
+ // tests that involve legacy indices need legacy indices turned on at startup of the graph.
+ final Map<String, Object> neo4jSettings = new HashMap<>();
+ if (name.getMethodName().contains("NoMultiProperties"))
+ neo4jSettings.put(Neo4jGraph.CONFIG_MULTI_PROPERTIES, false);
+ if (name.getMethodName().contains("NoMetaProperties"))
+ neo4jSettings.put(Neo4jGraph.CONFIG_META_PROPERTIES, false);
+ if (name.getMethodName().contains("Legacy")) {
+ neo4jSettings.put("gremlin.neo4j.conf.node_auto_indexing", "true");
+ neo4jSettings.put("gremlin.neo4j.conf.relationship_auto_indexing", "true");
+ }
+
+ this.conf = neo4jSettings.size() == 0 ?
+ this.graphProvider.newGraphConfiguration("standard", this.getClass(), name.getMethodName()) :
+ this.graphProvider.newGraphConfiguration("standard", this.getClass(), name.getMethodName(), neo4jSettings);
+
+ this.graphProvider.clear(this.conf);
+ this.graph = Neo4jGraph.open(this.conf);
+ this.g = this.graph.traversal();
+
+ }*/
+
+ @After
+ public void after() throws Exception {
+ this.graphProvider.clear(this.graph, this.conf);
+ }
+
+ protected void tryCommit(final Neo4jGraph g, final Consumer<Neo4jGraph> assertFunction) {
+ assertFunction.accept(g);
+ if (g.features().graph().supportsTransactions()) {
+ g.tx().commit();
+ assertFunction.accept(g);
+ }
+ }
+
+ protected static int countIterable(final Iterable iterable) {
+ int count = 0;
+ for (Object object : iterable) {
+ count++;
+ }
+ return count;
+ }
+
+ protected static void validateCounts(final Neo4jGraph graph, int gV, int gE, int gN, int gR) {
+ assertEquals(gV, IteratorUtils.count(graph.vertices()));
+ assertEquals(gE, IteratorUtils.count(graph.edges()));
+ assertEquals(gN, countIterable(graph.getBaseGraph().allNodes()));
+ assertEquals(gR, countIterable(graph.getBaseGraph().allRelationships()));
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/DefaultNeo4jGraphProvider.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/DefaultNeo4jGraphProvider.java b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/DefaultNeo4jGraphProvider.java
new file mode 100644
index 0000000..7430b96
--- /dev/null
+++ b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/DefaultNeo4jGraphProvider.java
@@ -0,0 +1,50 @@
+/*
+ * 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.neo4j;
+
+import org.apache.tinkerpop.gremlin.LoadGraphWith;
+import org.apache.tinkerpop.gremlin.TestHelper;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class DefaultNeo4jGraphProvider extends AbstractNeo4jGraphProvider {
+ @Override
+ public Map<String, Object> getBaseConfiguration(final String graphName, final Class<?> test, final String testMethodName, final LoadGraphWith.GraphData graphData) {
+ return new HashMap<String, Object>() {{
+ put(Graph.GRAPH, Neo4jGraph.class.getName());
+ String directory = getWorkingDirectory() + File.separator + TestHelper.cleanPathSegment(graphName) + File.separator + cleanParameters(TestHelper.cleanPathSegment(testMethodName));
+ put(Neo4jGraph.CONFIG_DIRECTORY, directory);
+ put(Neo4jGraph.CONFIG_META_PROPERTIES, true);
+ put(Neo4jGraph.CONFIG_MULTI_PROPERTIES, true);
+ put(Neo4jGraph.CONFIG_CHECK_ELEMENTS_IN_TRANSACTION, true);
+ }};
+ }
+
+ private String cleanParameters(String methodName) {
+ int random = (int) (Math.random() * Integer.MAX_VALUE);
+ return methodName.replaceAll("[0-9, -]+$", String.valueOf(random));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/Neo4jCypherStartTest.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/Neo4jCypherStartTest.java b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/Neo4jCypherStartTest.java
new file mode 100644
index 0000000..aab5ea0
--- /dev/null
+++ b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/Neo4jCypherStartTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.neo4j.process;
+
+import org.apache.tinkerpop.gremlin.neo4j.BaseNeo4jGraphTest;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class Neo4jCypherStartTest extends BaseNeo4jGraphTest {
+ @Test
+ public void shouldExecuteCypher() throws Exception {
+ this.graph.addVertex("name", "marko");
+ this.graph.tx().commit();
+ final Iterator<Map<String, Object>> result = graph.cypher("MATCH (a {name:\"marko\"}) RETURN a", Collections.emptyMap());
+ assertNotNull(result);
+ assertTrue(result.hasNext());
+ }
+
+ @Test
+ public void shouldExecuteCypherWithArgs() throws Exception {
+ this.graph.addVertex("name", "marko");
+ this.graph.tx().commit();
+ final Map<String, Object> bindings = new HashMap<>();
+ bindings.put("n", "marko");
+ final Iterator<Map<String, Object>> result = graph.cypher("MATCH (a {name:{n}}) RETURN a", bindings);
+ assertNotNull(result);
+ assertTrue(result.hasNext());
+ }
+
+ @Test
+ public void shouldExecuteCypherWithArgsUsingVertexIdList() throws Exception {
+ final Vertex v = this.graph.addVertex("name", "marko");
+ final List<Object> idList = Arrays.asList(v.id());
+ this.graph.tx().commit();
+
+ final Map<String, Object> bindings = new HashMap<>();
+ bindings.put("ids", idList);
+ final Iterator<String> result = graph.cypher("START n=node({ids}) RETURN n", bindings).select("n").values("name");
+ assertNotNull(result);
+ assertTrue(result.hasNext());
+ assertEquals("marko", result.next());
+ }
+
+ @Test
+ public void shouldExecuteCypherAndBackToGremlin() throws Exception {
+ this.graph.addVertex("name", "marko", "age", 29, "color", "red");
+ this.graph.addVertex("name", "marko", "age", 30, "color", "yellow");
+
+ this.graph.tx().commit();
+ final Traversal result = graph.cypher("MATCH (a {name:\"marko\"}) RETURN a").select("a").has("age", 29).values("color");
+ assertNotNull(result);
+ assertTrue(result.hasNext());
+ assertEquals("red", result.next().toString());
+ }
+
+ @Test
+ public void shouldExecuteMultiIdWhereCypher() throws Exception {
+ this.graph.addVertex("name", "marko", "age", 29, "color", "red");
+ this.graph.addVertex("name", "marko", "age", 30, "color", "yellow");
+ this.graph.addVertex("name", "marko", "age", 30, "color", "orange");
+ this.graph.tx().commit();
+
+ final List<Object> result = graph.cypher("MATCH n WHERE id(n) IN [1,2] RETURN n").select("n").id().toList();
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ assertTrue(result.contains(1l));
+ assertTrue(result.contains(2l));
+ }
+
+ @Test
+ public void shouldExecuteMultiIdWhereWithParamCypher() throws Exception {
+ final Vertex v1 = this.graph.addVertex("name", "marko", "age", 29, "color", "red");
+ final Vertex v2 = this.graph.addVertex("name", "marko", "age", 30, "color", "yellow");
+ this.graph.addVertex("name", "marko", "age", 30, "color", "orange");
+ this.graph.tx().commit();
+
+ final List<Object> ids = Arrays.asList(v1.id(), v2.id());
+ final Map<String, Object> m = new HashMap<>();
+ m.put("ids", ids);
+ final List<Object> result = graph.cypher("MATCH n WHERE id(n) IN {ids} RETURN n", m).select("n").id().toList();
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ assertTrue(result.contains(v1.id()));
+ assertTrue(result.contains(v2.id()));
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/Neo4jGraphProcessStandardTest.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/Neo4jGraphProcessStandardTest.java b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/Neo4jGraphProcessStandardTest.java
new file mode 100644
index 0000000..4c8a011
--- /dev/null
+++ b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/Neo4jGraphProcessStandardTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.neo4j.process;
+
+import org.apache.tinkerpop.gremlin.GraphProviderClass;
+import org.apache.tinkerpop.gremlin.neo4j.DefaultNeo4jGraphProvider;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph;
+import org.apache.tinkerpop.gremlin.process.ProcessStandardSuite;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Executes the Standard Gremlin Structure Test Suite using Neo4j.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@RunWith(ProcessStandardSuite.class)
+@GraphProviderClass(provider = DefaultNeo4jGraphProvider.class, graph = Neo4jGraph.class)
+public class Neo4jGraphProcessStandardTest {
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/groovy/Neo4jGraphGroovyProcessStandardTest.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/groovy/Neo4jGraphGroovyProcessStandardTest.java b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/groovy/Neo4jGraphGroovyProcessStandardTest.java
new file mode 100644
index 0000000..db63d38
--- /dev/null
+++ b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/process/groovy/Neo4jGraphGroovyProcessStandardTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.neo4j.process.groovy;
+
+import org.apache.tinkerpop.gremlin.GraphProviderClass;
+import org.apache.tinkerpop.gremlin.neo4j.DefaultNeo4jGraphProvider;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph;
+import org.apache.tinkerpop.gremlin.process.GroovyProcessStandardSuite;
+import org.junit.runner.RunWith;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+@RunWith(GroovyProcessStandardSuite.class)
+@GraphProviderClass(provider = DefaultNeo4jGraphProvider.class, graph = Neo4jGraph.class)
+public class Neo4jGraphGroovyProcessStandardTest {
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraphStructureStandardTest.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraphStructureStandardTest.java b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraphStructureStandardTest.java
new file mode 100644
index 0000000..fca281e
--- /dev/null
+++ b/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraphStructureStandardTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.tinkerpop.gremlin.GraphProviderClass;
+import org.apache.tinkerpop.gremlin.neo4j.DefaultNeo4jGraphProvider;
+import org.apache.tinkerpop.gremlin.structure.StructureStandardSuite;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Executes the Standard Gremlin Structure Test Suite using Neo4j.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@RunWith(StructureStandardSuite.class)
+@GraphProviderClass(provider = DefaultNeo4jGraphProvider.class, graph = Neo4jGraph.class)
+public class Neo4jGraphStructureStandardTest {
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index efe30db..76bccae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,6 +82,7 @@ limitations under the License.
<module>gremlin-groovy-test</module>
<module>tinkergraph-gremlin</module>
<module>hadoop-gremlin</module>
+ <module>neo4j-gremlin</module>
<module>gremlin-driver</module>
<module>gremlin-console</module>
<module>gremlin-server</module>
[2/2] incubator-tinkerpop git commit: Neo4j-Gremlin back in the mix
-- All Apache2. Builds -- tests fail cause it can't find impl classes.
Posted by ok...@apache.org.
Neo4j-Gremlin back in the mix -- All Apache2. Builds -- tests fail cause it can't find impl classes.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/b2c49ddd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/b2c49ddd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/b2c49ddd
Branch: refs/heads/neo4j-gremlin-apache
Commit: b2c49ddd378eca1231875781d1dcef7499d4863f
Parents: 19bc349
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Thu May 28 09:56:05 2015 -0600
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Thu May 28 09:56:05 2015 -0600
----------------------------------------------------------------------
.../gremlin/util/iterator/IteratorUtils.java | 9 +-
neo4j-gremlin/pom.xml | 141 +++++
.../neo4j/groovy/plugin/Neo4jGremlinPlugin.java | 53 ++
.../step/sideEffect/Neo4jGraphStep.java | 226 +++++++
.../optimization/Neo4jGraphStepStrategy.java | 70 +++
.../neo4j/process/util/Neo4jCypherIterator.java | 64 ++
.../gremlin/neo4j/structure/Neo4jEdge.java | 91 +++
.../gremlin/neo4j/structure/Neo4jElement.java | 113 ++++
.../gremlin/neo4j/structure/Neo4jGraph.java | 584 +++++++++++++++++++
.../neo4j/structure/Neo4jGraphVariables.java | 176 ++++++
.../gremlin/neo4j/structure/Neo4jHelper.java | 63 ++
.../gremlin/neo4j/structure/Neo4jProperty.java | 99 ++++
.../gremlin/neo4j/structure/Neo4jVertex.java | 283 +++++++++
.../neo4j/structure/Neo4jVertexProperty.java | 211 +++++++
...inkerpop.gremlin.groovy.plugin.GremlinPlugin | 1 +
.../neo4j/AbstractNeo4jGraphProvider.java | 156 +++++
.../gremlin/neo4j/BaseNeo4jGraphTest.java | 100 ++++
.../neo4j/DefaultNeo4jGraphProvider.java | 50 ++
.../neo4j/process/Neo4jCypherStartTest.java | 117 ++++
.../process/Neo4jGraphProcessStandardTest.java | 36 ++
.../Neo4jGraphGroovyProcessStandardTest.java | 33 ++
.../Neo4jGraphStructureStandardTest.java | 35 ++
pom.xml | 1 +
23 files changed, 2710 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/IteratorUtils.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/IteratorUtils.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/IteratorUtils.java
index d9d54cb..6937bf4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/IteratorUtils.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/IteratorUtils.java
@@ -44,7 +44,8 @@ import java.util.stream.StreamSupport;
*/
public final class IteratorUtils {
- private IteratorUtils() {}
+ private IteratorUtils() {
+ }
public static final <S> Iterator<S> of(final S a) {
return new SingleIterator<>(a);
@@ -64,7 +65,7 @@ public final class IteratorUtils {
}
public static void iterate(final Iterator iterator) {
- while(iterator.hasNext()) {
+ while (iterator.hasNext()) {
iterator.next();
}
}
@@ -339,4 +340,8 @@ public final class IteratorUtils {
public static <T> Stream<T> stream(final Iterator<T> iterator) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.IMMUTABLE | Spliterator.SIZED), false);
}
+
+ public static <T> Stream<T> stream(final Iterable<T> iterable) {
+ return IteratorUtils.stream(iterable.iterator());
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/pom.xml
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/pom.xml b/neo4j-gremlin/pom.xml
new file mode 100644
index 0000000..b93e840
--- /dev/null
+++ b/neo4j-gremlin/pom.xml
@@ -0,0 +1,141 @@
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>tinkerpop</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>neo4j-gremlin</artifactId>
+ <name>Apache TinkerPop :: Neo4j Gremlin</name>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>gremlin-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>gremlin-groovy</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.neo4j</groupId>
+ <artifactId>neo4j-tinkerpop-api</artifactId>
+ <version>0.1</version>
+ </dependency>
+ <!-- TESTING -->
+ <dependency>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>gremlin-test</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>gremlin-groovy-test</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <directory>${basedir}/target</directory>
+ <finalName>${project.artifactId}-${project.version}</finalName>
+ <resources>
+ <resource>
+ <directory>${basedir}/src/main/resources
+ </directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.gmavenplus</groupId>
+ <artifactId>gmavenplus-plugin</artifactId>
+ <version>1.2</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>addSources</goal>
+ <goal>addTestSources</goal>
+ <goal>generateStubs</goal>
+ <goal>compile</goal>
+ <goal>testGenerateStubs</goal>
+ <goal>testCompile</goal>
+ <goal>removeStubs</goal>
+ <goal>removeTestStubs</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <invokeDynamic>true</invokeDynamic>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>lucky</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ <property>
+ <name>feelingLucky</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>true</skipTests>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <configuration>
+ <skipTests>true</skipTests>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java
new file mode 100644
index 0000000..b112e43
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java
@@ -0,0 +1,53 @@
+/*
+ * 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.neo4j.groovy.plugin;
+
+import org.apache.tinkerpop.gremlin.groovy.plugin.AbstractGremlinPlugin;
+import org.apache.tinkerpop.gremlin.groovy.plugin.IllegalEnvironmentException;
+import org.apache.tinkerpop.gremlin.groovy.plugin.PluginAcceptor;
+import org.apache.tinkerpop.gremlin.groovy.plugin.PluginInitializationException;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class Neo4jGremlinPlugin extends AbstractGremlinPlugin {
+
+ private static final Set<String> IMPORTS = new HashSet<String>() {{
+ add(IMPORT_SPACE + Neo4jGraph.class.getPackage().getName() + DOT_STAR);
+ }};
+
+ @Override
+ public String getName() {
+ return "tinkerpop.neo4j";
+ }
+
+ @Override
+ public void pluginTo(final PluginAcceptor pluginAcceptor) throws PluginInitializationException, IllegalEnvironmentException {
+ pluginAcceptor.addImports(IMPORTS);
+ }
+
+ @Override
+ public void afterPluginTo(final PluginAcceptor pluginAcceptor) throws IllegalEnvironmentException, PluginInitializationException {
+
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..aafad32
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/step/sideEffect/Neo4jGraphStep.java
@@ -0,0 +1,226 @@
+/*
+ * 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.neo4j.process.traversal.step.sideEffect;
+
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jEdge;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jVertex;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jVertexProperty;
+import org.apache.tinkerpop.gremlin.process.traversal.Compare;
+import org.apache.tinkerpop.gremlin.process.traversal.Contains;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GraphStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import org.javatuples.Pair;
+import org.neo4j.tinkerpop.api.Neo4jDirection;
+import org.neo4j.tinkerpop.api.Neo4jGraphAPI;
+import org.neo4j.tinkerpop.api.Neo4jNode;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Stream;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author Pieter Martin
+ */
+public final class Neo4jGraphStep<S extends Element> extends GraphStep<S> {
+
+ public final List<HasContainer> hasContainers = new ArrayList<>();
+
+ public Neo4jGraphStep(final GraphStep<S> originalGraphStep) {
+ super(originalGraphStep.getTraversal(), originalGraphStep.getReturnClass(), originalGraphStep.getIds());
+ originalGraphStep.getLabels().forEach(this::addLabel);
+ //No need to do anything if the first element is an Element, all elements are guaranteed to be an element and will be return as is
+ if ((this.ids.length == 0 || !(this.ids[0] instanceof Element)))
+ this.setIteratorSupplier(() -> (Iterator<S>) (Vertex.class.isAssignableFrom(this.returnClass) ? this.vertices() : this.edges()));
+ }
+
+ private Iterator<? extends Edge> edges() {
+ final Neo4jGraph graph = (Neo4jGraph) this.getTraversal().getGraph().get();
+ graph.tx().readWrite();
+ // ids are present, filter on them first
+ if (this.ids != null && this.ids.length > 0)
+ return IteratorUtils.filter(graph.edges(this.ids), edge -> HasContainer.testAll((Edge) edge, this.hasContainers));
+ final HasContainer hasContainer = this.getHasContainerForAutomaticIndex(Edge.class);
+ return (null == hasContainer) ?
+ IteratorUtils.filter(graph.edges(), edge -> HasContainer.testAll((Edge) edge, this.hasContainers)) :
+ getEdgesUsingAutomaticIndex(hasContainer).filter(edge -> HasContainer.testAll((Edge) edge, this.hasContainers)).iterator();
+ }
+
+ private Iterator<? extends Vertex> vertices() {
+ final Neo4jGraph graph = (Neo4jGraph) this.getTraversal().getGraph().get();
+ graph.tx().readWrite();
+ // ids are present, filter on them first
+ if (this.ids != null && this.ids.length > 0)
+ return IteratorUtils.filter(graph.vertices(this.ids), vertex -> HasContainer.testAll((Vertex) vertex, this.hasContainers));
+ // a label and a property
+ final Pair<String, HasContainer> labelHasPair = this.getHasContainerForLabelIndex();
+ if (null != labelHasPair)
+ return this.getVerticesUsingLabelAndProperty(labelHasPair.getValue0(), labelHasPair.getValue1())
+ .filter(vertex -> HasContainer.testAll((Vertex) vertex, this.hasContainers)).iterator();
+ // use automatic indices
+ final HasContainer hasContainer = this.getHasContainerForAutomaticIndex(Vertex.class);
+ if (null != hasContainer)
+ return this.getVerticesUsingAutomaticIndex(hasContainer)
+ .filter(vertex -> HasContainer.testAll((Vertex) vertex, this.hasContainers)).iterator();
+ // only labels
+ final List<String> labels = this.getInternalLabels();
+ if (null != labels)
+ return this.getVerticesUsingOnlyLabels(labels).filter(vertex -> HasContainer.testAll((Vertex) vertex, this.hasContainers)).iterator();
+ // linear scan
+ return IteratorUtils.filter(graph.vertices(), vertex -> HasContainer.testAll((Vertex) vertex, this.hasContainers));
+ }
+
+
+ private Stream<Neo4jVertex> getVerticesUsingLabelAndProperty(final String label, final HasContainer hasContainer) {
+ //System.out.println("labelProperty: " + label + ":" + hasContainer);
+ final Neo4jGraph graph = (Neo4jGraph) this.getTraversal().getGraph().get();
+ final Iterable<Neo4jNode> iterator1 = graph.getBaseGraph().findNodes(label, hasContainer.getKey(), hasContainer.getValue());
+ final Iterable<Neo4jNode> iterator2 = graph.getBaseGraph().findNodes(hasContainer.getKey(), T.value.getAccessor(), hasContainer.getValue());
+ final Stream<Neo4jVertex> stream1 = IteratorUtils.stream(iterator1)
+ .filter(node -> ElementHelper.idExists(node.getId(), this.ids))
+ .map(node -> new Neo4jVertex(node, graph));
+ final Stream<Neo4jVertex> stream2 = IteratorUtils.stream(iterator2)
+ .filter(node -> ElementHelper.idExists(node.getId(), this.ids))
+ .filter(node -> node.getProperty(T.key.getAccessor()).equals(hasContainer.getKey()))
+ .map(node -> node.relationships(Neo4jDirection.INCOMING).iterator().next().start())
+ .map(node -> new Neo4jVertex(node, graph));
+ return Stream.concat(stream1, stream2);
+ }
+
+ private Stream<Neo4jVertex> getVerticesUsingOnlyLabels(final List<String> labels) {
+ //System.out.println("labels: " + labels);
+ final Neo4jGraph graph = (Neo4jGraph) this.getTraversal().getGraph().get();
+ return labels.stream()
+ .filter(label -> !label.equals(Neo4jVertexProperty.VERTEX_PROPERTY_LABEL))
+ .flatMap(label -> IteratorUtils.stream(graph.getBaseGraph().findNodes(label)))
+ .filter(node -> !node.hasLabel(Neo4jVertexProperty.VERTEX_PROPERTY_LABEL))
+ .filter(node -> ElementHelper.idExists(node.getId(), this.ids))
+ .map(node -> new Neo4jVertex(node, graph));
+ }
+
+ private Stream<Neo4jVertex> getVerticesUsingAutomaticIndex(final HasContainer hasContainer) {
+ //System.out.println("automatic index: " + hasContainer);
+ final Neo4jGraph graph = (Neo4jGraph) this.getTraversal().getGraph().get();
+ return IteratorUtils.stream(graph.getBaseGraph().findNodes(hasContainer.getKey(), hasContainer.getValue()).iterator())
+ .map(node -> node.hasLabel(Neo4jVertexProperty.VERTEX_PROPERTY_LABEL) ?
+ node.relationships(Neo4jDirection.INCOMING).iterator().next().start() :
+ node)
+ .filter(node -> ElementHelper.idExists(node.getId(), this.ids))
+ .map(node -> new Neo4jVertex(node, graph));
+ }
+
+ private Stream<Neo4jEdge> getEdgesUsingAutomaticIndex(final HasContainer hasContainer) {
+ final Neo4jGraph graph = (Neo4jGraph) this.getTraversal().getGraph().get();
+ return IteratorUtils.stream(graph.getBaseGraph().findRelationships(hasContainer.getKey(), hasContainer.getValue()).iterator())
+ .filter(relationship -> ElementHelper.idExists(relationship.getId(), this.ids))
+ .filter(relationship -> !relationship.type().startsWith(Neo4jVertexProperty.VERTEX_PROPERTY_PREFIX))
+ .map(relationship -> new Neo4jEdge(relationship, graph));
+ }
+
+ private Pair<String, HasContainer> getHasContainerForLabelIndex() {
+ final Neo4jGraph graph = (Neo4jGraph) this.getTraversal().getGraph().get();
+ Neo4jGraphAPI baseGraph = graph.getBaseGraph();
+ for (final HasContainer hasContainer : this.hasContainers) {
+ if (hasContainer.getKey().equals(T.label.getAccessor()) && hasContainer.getBiPredicate().equals(Compare.eq)) {
+ if (baseGraph.hasSchemaIndex(
+ (String) hasContainer.getValue(), hasContainer.getKey())) {
+ return Pair.with((String) hasContainer.getValue(), hasContainer);
+ }
+ }
+ }
+ return null;
+ }
+
+ private List<String> getInternalLabels() {
+ for (final HasContainer hasContainer : this.hasContainers) {
+ if (hasContainer.getKey().equals(T.label.getAccessor()) && hasContainer.getBiPredicate().equals(Compare.eq))
+ return Arrays.asList(((String) hasContainer.getValue()));
+ else if (hasContainer.getKey().equals(T.label.getAccessor()) && hasContainer.getBiPredicate().equals(Contains.within))
+ return new ArrayList<>((Collection<String>) hasContainer.getValue());
+ }
+ return null;
+ }
+
+ private HasContainer getHasContainerForAutomaticIndex(final Class<? extends Element> elementClass) {
+ final Neo4jGraph graph = (Neo4jGraph) this.getTraversal().getGraph().get();
+ Neo4jGraphAPI baseGraph = graph.getBaseGraph();
+ boolean isNode = elementClass.equals(Vertex.class);
+ for (final HasContainer hasContainer : this.hasContainers) {
+ if (hasContainer.getBiPredicate().equals(Compare.eq) &&
+ baseGraph.hasAutoIndex(isNode, hasContainer.getKey())) {
+ return hasContainer;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ if (this.hasContainers.isEmpty())
+ return super.toString();
+ else
+ return 0 == this.ids.length ?
+ StringFactory.stepString(this, this.hasContainers) :
+ StringFactory.stepString(this, Arrays.toString(this.ids), this.hasContainers);
+ }
+
+ /*private String makeCypherQuery() {
+ final StringBuilder builder = new StringBuilder("MATCH node WHERE ");
+ int counter = 0;
+ for (final HasContainer hasContainer : this.hasContainers) {
+ if (hasContainer.key.equals(T.label.getAccessor()) && hasContainer.predicate.equals(Compare.EQUAL)) {
+ if (counter++ > 0) builder.append(" AND ");
+ builder.append("node:").append(hasContainer.value);
+ } else {
+ if (counter++ > 0) builder.append(" AND ");
+ builder.append("node.").append(hasContainer.key).append(" ");
+ if (hasContainer.predicate instanceof Compare) {
+ builder.append(((Compare) hasContainer.predicate).asString()).append(" ").append(toStringOfValue(hasContainer.value));
+ } else if (hasContainer.predicate.equals(Contains.IN)) {
+ builder.append("IN [");
+ for (Object object : (Collection) hasContainer.value) {
+ builder.append(toStringOfValue(object)).append(",");
+ }
+ builder.replace(builder.length() - 1, builder.length(), "").append("]");
+ }
+ }
+
+ }
+ System.out.println(builder);
+ return builder.toString();
+ }
+
+ private String toStringOfValue(final Object value) {
+ if (value instanceof String)
+ return "'" + value + "'";
+ else return value.toString();
+ }*/
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/strategy/optimization/Neo4jGraphStepStrategy.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/strategy/optimization/Neo4jGraphStepStrategy.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/strategy/optimization/Neo4jGraphStepStrategy.java
new file mode 100644
index 0000000..eb98825
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/traversal/strategy/optimization/Neo4jGraphStepStrategy.java
@@ -0,0 +1,70 @@
+/*
+ * 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.neo4j.process.traversal.strategy.optimization;
+
+import org.apache.tinkerpop.gremlin.neo4j.process.traversal.step.sideEffect.Neo4jGraphStep;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.GraphStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+
+/**
+ * @author Pieter Martin
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class Neo4jGraphStepStrategy extends AbstractTraversalStrategy<TraversalStrategy.VendorOptimizationStrategy> {
+
+ private static final Neo4jGraphStepStrategy INSTANCE = new Neo4jGraphStepStrategy();
+
+ private Neo4jGraphStepStrategy() {
+ }
+
+ @Override
+ public void apply(final Traversal.Admin<?, ?> traversal) {
+ if (traversal.getEngine().isComputer())
+ return;
+
+ final Step<?, ?> startStep = traversal.getStartStep();
+ if (startStep instanceof GraphStep) {
+ final GraphStep<?> originalGraphStep = (GraphStep) startStep;
+ final Neo4jGraphStep<?> neo4jGraphStep = new Neo4jGraphStep<>(originalGraphStep);
+ TraversalHelper.replaceStep(startStep, (Step) neo4jGraphStep, traversal);
+
+ Step<?, ?> currentStep = neo4jGraphStep.getNextStep();
+ while (true) {
+ if (currentStep instanceof HasContainerHolder) {
+ neo4jGraphStep.hasContainers.addAll(((HasContainerHolder) currentStep).getHasContainers());
+ currentStep.getLabels().forEach(neo4jGraphStep::addLabel);
+ traversal.removeStep(currentStep);
+ } else {
+ break;
+ }
+ currentStep = currentStep.getNextStep();
+ }
+ }
+ }
+
+ public static Neo4jGraphStepStrategy instance() {
+ return INSTANCE;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/util/Neo4jCypherIterator.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/util/Neo4jCypherIterator.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/util/Neo4jCypherIterator.java
new file mode 100644
index 0000000..9117ab2
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/process/util/Neo4jCypherIterator.java
@@ -0,0 +1,64 @@
+/*
+ * 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.neo4j.process.util;
+
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jEdge;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph;
+import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jVertex;
+import org.neo4j.tinkerpop.api.Neo4jNode;
+import org.neo4j.tinkerpop.api.Neo4jRelationship;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class Neo4jCypherIterator<T> implements Iterator<Map<String, T>> {
+
+ private final Iterator<Map<String, T>> iterator;
+ private final Neo4jGraph graph;
+
+ public Neo4jCypherIterator(final Iterator<Map<String, T>> iterator, final Neo4jGraph graph) {
+ this.iterator = iterator;
+ this.graph = graph;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return this.iterator.hasNext();
+ }
+
+ @Override
+ public Map<String, T> next() {
+ return this.iterator.next().entrySet().stream().collect(Collectors.toMap(
+ Map.Entry::getKey,
+ entry -> {
+ final T val = entry.getValue();
+ if (Neo4jNode.class.isAssignableFrom(val.getClass())) {
+ return (T) new Neo4jVertex((Neo4jNode) val, this.graph);
+ } else if (Neo4jRelationship.class.isAssignableFrom(val.getClass())) {
+ return (T) new Neo4jEdge((Neo4jRelationship) val, this.graph);
+ } else {
+ return val;
+ }
+ }));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jEdge.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jEdge.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jEdge.java
new file mode 100644
index 0000000..a08fd8d
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jEdge.java
@@ -0,0 +1,91 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.tinkerpop.gremlin.structure.Direction;
+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 org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.structure.util.wrapped.WrappedEdge;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import org.neo4j.tinkerpop.api.Neo4jRelationship;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class Neo4jEdge extends Neo4jElement implements Edge, WrappedEdge<Neo4jRelationship> {
+
+ public Neo4jEdge(final Neo4jRelationship relationship, final Neo4jGraph graph) {
+ super(relationship, graph);
+ }
+
+ @Override
+ public void remove() {
+ if (this.removed) throw Element.Exceptions.elementAlreadyRemoved(Edge.class, this.getBaseEdge().getId());
+ this.removed = true;
+ this.graph.tx().readWrite();
+ try {
+ ((Neo4jRelationship) this.baseElement).delete();
+ } catch (IllegalStateException ignored) {
+ // NotFoundException happens if the edge is committed
+ // IllegalStateException happens if the edge is still chilling in the tx
+ } catch (RuntimeException e) {
+ if (!Neo4jHelper.isNotFound(e)) throw e;
+ // NotFoundException happens if the edge is committed
+ // IllegalStateException happens if the edge is still chilling in the tx
+ }
+ }
+
+ public String toString() {
+ return StringFactory.edgeString(this);
+ }
+
+ @Override
+ public String label() {
+ this.graph.tx().readWrite();
+ return this.getBaseEdge().type();
+ }
+
+ @Override
+ public Neo4jRelationship getBaseEdge() {
+ return (Neo4jRelationship) this.baseElement;
+ }
+
+ @Override
+ public <V> Iterator<Property<V>> properties(final String... propertyKeys) {
+ return (Iterator) super.properties(propertyKeys);
+ }
+
+ @Override
+ public Iterator<Vertex> vertices(final Direction direction) {
+ this.graph.tx().readWrite();
+ switch (direction) {
+ case OUT:
+ return IteratorUtils.of(new Neo4jVertex(this.getBaseEdge().start(), this.graph));
+ case IN:
+ return IteratorUtils.of(new Neo4jVertex(this.getBaseEdge().end(), this.graph));
+ default:
+ return IteratorUtils.of(new Neo4jVertex(this.getBaseEdge().start(), this.graph), new Neo4jVertex(this.getBaseEdge().end(), this.graph));
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jElement.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jElement.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jElement.java
new file mode 100644
index 0000000..770d763
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jElement.java
@@ -0,0 +1,113 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.apache.tinkerpop.gremlin.structure.util.wrapped.WrappedElement;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import org.neo4j.tinkerpop.api.Neo4jEntity;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class Neo4jElement implements Element, WrappedElement<Neo4jEntity> {
+ protected final Neo4jGraph graph;
+ protected final Neo4jEntity baseElement;
+ protected boolean removed = false;
+
+ public Neo4jElement(final Neo4jEntity baseElement, final Neo4jGraph graph) {
+ this.baseElement = baseElement;
+ this.graph = graph;
+ }
+
+ @Override
+ public Graph graph() {
+ return this.graph;
+ }
+
+ @Override
+ public Object id() {
+ this.graph.tx().readWrite();
+ return this.baseElement.getId();
+ }
+
+ @Override
+ public Set<String> keys() {
+ this.graph.tx().readWrite();
+ return Element.super.keys();
+ }
+
+ @Override
+ public <V> Property<V> property(final String key) {
+ this.graph.tx().readWrite();
+ try {
+ if (this.baseElement.hasProperty(key))
+ return new Neo4jProperty<>(this, key, (V) this.baseElement.getProperty(key));
+ else
+ return Property.empty();
+ } catch (final IllegalStateException e) {
+ throw Element.Exceptions.elementAlreadyRemoved(this.getClass(), this.id());
+ }
+ }
+
+ @Override
+ public <V> Property<V> property(final String key, final V value) {
+ ElementHelper.validateProperty(key, value);
+ this.graph.tx().readWrite();
+
+ try {
+ this.baseElement.setProperty(key, value);
+ return new Neo4jProperty<>(this, key, value);
+ } catch (final IllegalArgumentException e) {
+ throw Property.Exceptions.dataTypeOfPropertyValueNotSupported(value);
+ }
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ return ElementHelper.areEqual(this, object);
+ }
+
+ @Override
+ public int hashCode() {
+ return ElementHelper.hashCode(this);
+ }
+
+ @Override
+ public Neo4jEntity getBaseElement() {
+ return this.baseElement;
+ }
+
+ @Override
+ public <V> Iterator<? extends Property<V>> properties(final String... propertyKeys) {
+ this.graph.tx().readWrite();
+ Iterable<String> keys = this.baseElement.getKeys();
+ Iterator<String> filter = IteratorUtils.filter(keys.iterator(),
+ key -> ElementHelper.keyExists(key, propertyKeys));
+ return IteratorUtils.map(filter,
+ key -> new Neo4jProperty<>(this, key, (V) this.baseElement.getProperty(key)));
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
new file mode 100644
index 0000000..e1ff965
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
@@ -0,0 +1,584 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationConverter;
+import org.apache.tinkerpop.gremlin.neo4j.process.traversal.strategy.optimization.Neo4jGraphStepStrategy;
+import org.apache.tinkerpop.gremlin.neo4j.process.util.Neo4jCypherIterator;
+import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.StartStep;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Transaction;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.AbstractTransaction;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.structure.util.wrapped.WrappedGraph;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import org.neo4j.tinkerpop.api.Neo4jFactory;
+import org.neo4j.tinkerpop.api.Neo4jGraphAPI;
+import org.neo4j.tinkerpop.api.Neo4jTx;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Pieter Martin
+ */
+@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_STANDARD)
+@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_INTEGRATE)
+@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_PERFORMANCE)
+@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD)
+@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_PERFORMANCE)
+@Graph.OptIn(Graph.OptIn.SUITE_GROOVY_PROCESS_STANDARD)
+@Graph.OptIn(Graph.OptIn.SUITE_GROOVY_ENVIRONMENT)
+@Graph.OptIn(Graph.OptIn.SUITE_GROOVY_ENVIRONMENT_INTEGRATE)
+@Graph.OptIn(Graph.OptIn.SUITE_GROOVY_ENVIRONMENT_PERFORMANCE)
+/*@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.structure.VertexTest$ExceptionConsistencyWhenVertexRemovedTest",
+ method = "shouldThrowExceptionIfVertexWasRemovedWhenCallingProperty",
+ specific = "property(single,k,v)",
+ reason = "Neo4j throws a NodeNotFoundException instead of the desired IllegalStateException.")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexTest$Traversals",
+ method = "g_V_addVXlabel_animal_age_0X",
+ reason = "Neo4j global graph operators stream created vertices created after the access to the global iterator."
+)
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.GroovyAddVertexTest$StandardTraversals",
+ method = "g_V_addVXlabel_animal_age_0X",
+ reason = "Neo4j global graph operators stream created vertices created after the access to the global iterator."
+)
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.structure.GraphTest",
+ method = "shouldRemoveEdgesWithoutConcurrentModificationException",
+ reason = "Neo4j global graph operators stream removes edges after access to the global iterator."
+)*/
+public final class Neo4jGraph implements Graph, WrappedGraph<Neo4jGraphAPI> {
+
+ static {
+ TraversalStrategies.GlobalCache.registerStrategies(Neo4jGraph.class, TraversalStrategies.GlobalCache.getStrategies(Graph.class).clone().addStrategies(Neo4jGraphStepStrategy.instance()));
+ }
+
+ private static final Configuration EMPTY_CONFIGURATION = new BaseConfiguration() {{
+ this.setProperty(Graph.GRAPH, Neo4jGraph.class.getName());
+ }};
+
+ private final Features features = new Neo4jGraphFeatures();
+
+ private Neo4jGraphAPI baseGraph;
+ private BaseConfiguration configuration = new BaseConfiguration();
+
+ public static final String CONFIG_DIRECTORY = "gremlin.neo4j.directory";
+ public static final String CONFIG_CONF = "gremlin.neo4j.conf";
+ public static final String CONFIG_META_PROPERTIES = "gremlin.neo4j.metaProperties";
+ public static final String CONFIG_MULTI_PROPERTIES = "gremlin.neo4j.multiProperties";
+ public static final String CONFIG_CHECK_ELEMENTS_IN_TRANSACTION = "gremlin.neo4j.checkElementsInTransaction";
+
+ private final Neo4jTransaction neo4jTransaction = new Neo4jTransaction();
+ private final Neo4jGraphVariables neo4jGraphVariables;
+
+ protected final boolean supportsMetaProperties;
+ protected final boolean supportsMultiProperties;
+ protected boolean checkElementsInTransaction = false;
+
+ private Neo4jGraph(final Neo4jGraphAPI baseGraph) {
+ this.configuration.copy(EMPTY_CONFIGURATION);
+ this.baseGraph = baseGraph;
+ this.neo4jGraphVariables = new Neo4jGraphVariables(this);
+
+ ///////////
+ final Optional<Boolean> metaProperties = this.neo4jGraphVariables.get(Hidden.hide(CONFIG_META_PROPERTIES));
+ if (metaProperties.isPresent()) {
+ this.supportsMetaProperties = metaProperties.get();
+ } else {
+ this.supportsMetaProperties = false;
+ this.neo4jGraphVariables.set(Hidden.hide(CONFIG_META_PROPERTIES), false);
+ }
+ final Optional<Boolean> multiProperties = this.neo4jGraphVariables.get(Hidden.hide(CONFIG_MULTI_PROPERTIES));
+ if (multiProperties.isPresent()) {
+ this.supportsMultiProperties = multiProperties.get();
+ } else {
+ this.supportsMultiProperties = false;
+ this.neo4jGraphVariables.set(Hidden.hide(CONFIG_MULTI_PROPERTIES), false);
+ }
+ if ((this.supportsMetaProperties && !this.supportsMultiProperties) || (!this.supportsMetaProperties && this.supportsMultiProperties)) {
+ tx().rollback();
+ throw new UnsupportedOperationException("Neo4jGraph currently requires either both meta- and multi-properties activated or neither activated");
+ }
+ final Optional<Boolean> elementsInTransaction = this.neo4jGraphVariables.get(Hidden.hide(CONFIG_CHECK_ELEMENTS_IN_TRANSACTION));
+ if (elementsInTransaction.isPresent()) {
+ this.checkElementsInTransaction = elementsInTransaction.get();
+ } else {
+ this.checkElementsInTransaction = false;
+ this.neo4jGraphVariables.set(Hidden.hide(CONFIG_CHECK_ELEMENTS_IN_TRANSACTION), false);
+ }
+ tx().commit();
+ ///////////
+ }
+
+ private Neo4jGraph(final Configuration configuration) {
+ try {
+ this.configuration.copy(EMPTY_CONFIGURATION);
+ this.configuration.copy(configuration);
+ final String directory = this.configuration.getString(CONFIG_DIRECTORY);
+ final Map neo4jSpecificConfig = ConfigurationConverter.getMap(this.configuration.subset(CONFIG_CONF));
+ this.baseGraph = Neo4jFactory.Builder.open(directory, neo4jSpecificConfig);
+ this.neo4jGraphVariables = new Neo4jGraphVariables(this);
+ ///////////
+ if (!this.neo4jGraphVariables.get(Hidden.hide(CONFIG_META_PROPERTIES)).isPresent())
+ this.neo4jGraphVariables.set(Hidden.hide(CONFIG_META_PROPERTIES), this.configuration.getBoolean(CONFIG_META_PROPERTIES, false));
+ // TODO: Logger saying the configuration properties are ignored if already in Graph.Variables
+ if (!this.neo4jGraphVariables.get(Hidden.hide(CONFIG_MULTI_PROPERTIES)).isPresent())
+ this.neo4jGraphVariables.set(Hidden.hide(CONFIG_MULTI_PROPERTIES), this.configuration.getBoolean(CONFIG_MULTI_PROPERTIES, false));
+ // TODO: Logger saying the configuration properties are ignored if already in Graph.Variables
+ this.supportsMetaProperties = this.neo4jGraphVariables.<Boolean>get(Hidden.hide(CONFIG_META_PROPERTIES)).get();
+ this.supportsMultiProperties = this.neo4jGraphVariables.<Boolean>get(Hidden.hide(CONFIG_MULTI_PROPERTIES)).get();
+ if ((this.supportsMetaProperties && !this.supportsMultiProperties) || (!this.supportsMetaProperties && this.supportsMultiProperties)) {
+ tx().rollback();
+ throw new UnsupportedOperationException("Neo4jGraph currently requires either both meta- and multi-properties activated or neither activated");
+ }
+ //
+ // TODO: Logger saying the configuration properties are ignored if already in Graph.Variables
+ if (!this.neo4jGraphVariables.get(Hidden.hide(CONFIG_CHECK_ELEMENTS_IN_TRANSACTION)).isPresent())
+ this.neo4jGraphVariables.set(Hidden.hide(CONFIG_CHECK_ELEMENTS_IN_TRANSACTION), this.configuration.getBoolean(CONFIG_CHECK_ELEMENTS_IN_TRANSACTION, false));
+ this.checkElementsInTransaction = this.neo4jGraphVariables.<Boolean>get(Hidden.hide(CONFIG_CHECK_ELEMENTS_IN_TRANSACTION)).get();
+ tx().commit();
+ ///////////
+ } catch (Exception e) {
+ if (this.baseGraph != null)
+ this.baseGraph.shutdown();
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Open a new {@link Neo4jGraph} instance.
+ *
+ * @param configuration the configuration for the instance
+ * @return a newly opened {@link org.apache.tinkerpop.gremlin.structure.Graph}
+ */
+ public static Neo4jGraph open(final Configuration configuration) {
+ if (null == configuration) throw Graph.Exceptions.argumentCanNotBeNull("configuration");
+ if (!configuration.containsKey(CONFIG_DIRECTORY))
+ throw new IllegalArgumentException(String.format("Neo4j configuration requires that the %s be set", CONFIG_DIRECTORY));
+
+ return new Neo4jGraph(configuration);
+ }
+
+ /**
+ * Construct a Neo4jGraph instance by specifying the directory to create the database in..
+ */
+ public static Neo4jGraph open(final String directory) {
+ final Configuration config = new BaseConfiguration();
+ config.setProperty(CONFIG_DIRECTORY, directory);
+ return open(config);
+ }
+
+ /**
+ * Construct a Neo4jGraph instance using an existing Neo4j raw instance.
+ */
+ public static Neo4jGraph open(final Neo4jGraphAPI baseGraph) {
+ return new Neo4jGraph(Optional.ofNullable(baseGraph).orElseThrow(() -> Graph.Exceptions.argumentCanNotBeNull("baseGraph")));
+ }
+
+ @Override
+ public Vertex addVertex(final Object... keyValues) {
+ ElementHelper.legalPropertyKeyValueArray(keyValues);
+ if (ElementHelper.getIdValue(keyValues).isPresent())
+ throw Vertex.Exceptions.userSuppliedIdsNotSupported();
+
+ final String label = ElementHelper.getLabelValue(keyValues).orElse(Vertex.DEFAULT_LABEL);
+
+ this.tx().readWrite();
+ final Neo4jVertex vertex = new Neo4jVertex(this.baseGraph.createNode(label.split(Neo4jVertex.LABEL_DELIMINATOR)), this);
+ ElementHelper.attachProperties(vertex, VertexProperty.Cardinality.list, keyValues);
+ return vertex;
+ }
+
+ @Override
+ public <C extends GraphComputer> C compute(final Class<C> graphComputerClass) {
+ throw Graph.Exceptions.graphComputerNotSupported();
+ }
+
+ @Override
+ public GraphComputer compute() {
+ throw Graph.Exceptions.graphComputerNotSupported();
+ }
+
+ @Override
+ public Transaction tx() {
+ return this.neo4jTransaction;
+ }
+
+ @Override
+ public Variables variables() {
+ return this.neo4jGraphVariables;
+ }
+
+ @Override
+ public Configuration configuration() {
+ return this.configuration;
+ }
+
+ @Override
+ public Iterator<Vertex> vertices(final Object... vertexIds) {
+ this.tx().readWrite();
+ if (0 == vertexIds.length) {
+ return IteratorUtils.stream(this.getBaseGraph().allNodes())
+ .filter(node -> !this.checkElementsInTransaction || !Neo4jHelper.isDeleted(node))
+ .filter(node -> !node.hasLabel(Neo4jVertexProperty.VERTEX_PROPERTY_LABEL))
+ .map(node -> (Vertex) new Neo4jVertex(node, this)).iterator();
+ } else {
+ return Stream.of(vertexIds)
+ .filter(id -> id instanceof Number)
+ .flatMap(id -> {
+ try {
+ return Stream.of((Vertex) new Neo4jVertex(this.getBaseGraph().getNodeById(((Number) id).longValue()), this));
+ } catch (final RuntimeException e) {
+ if (Neo4jHelper.isNotFound(e)) return Stream.empty();
+ throw e;
+ }
+ }).iterator();
+ }
+ }
+
+ @Override
+ public Iterator<Edge> edges(final Object... edgeIds) {
+ this.tx().readWrite();
+ if (0 == edgeIds.length) {
+ return IteratorUtils.stream(this.getBaseGraph().allRelationships())
+ .filter(relationship -> !this.checkElementsInTransaction || !Neo4jHelper.isDeleted(relationship))
+ .filter(relationship -> !relationship.type().startsWith(Neo4jVertexProperty.VERTEX_PROPERTY_PREFIX))
+ .map(relationship -> (Edge) new Neo4jEdge(relationship, this)).iterator();
+ } else {
+ return Stream.of(edgeIds)
+ .filter(id -> id instanceof Number)
+ .flatMap(id -> {
+ try {
+ return Stream.of((Edge) new Neo4jEdge(this.getBaseGraph().getRelationshipById(((Number) id).longValue()), this));
+ } catch (final RuntimeException e) {
+ if (Neo4jHelper.isNotFound(e)) return Stream.empty();
+ throw e;
+ }
+ }).iterator();
+ }
+
+ }
+
+ /**
+ * This implementation of {@code close} will also close the current transaction on the the thread, but it
+ * is up to the caller to deal with dangling transactions in other threads prior to calling this method.
+ */
+ @Override
+ public void close() throws Exception {
+ this.tx().close();
+ if (this.baseGraph != null) this.baseGraph.shutdown();
+ }
+
+ public String toString() {
+ return StringFactory.graphString(this, baseGraph.toString());
+ }
+
+ @Override
+ public Features features() {
+ return features;
+ }
+
+ @Override
+ public Neo4jGraphAPI getBaseGraph() {
+ return this.baseGraph;
+ }
+
+ /**
+ * Neo4j's transactions are not consistent between the graph and the graph
+ * indices. Moreover, global graph operations are not consistent. For
+ * example, if a vertex is removed and then an index is queried in the same
+ * transaction, the removed vertex can be returned. This method allows the
+ * developer to turn on/off a Neo4jGraph 'hack' that ensures transactional
+ * consistency. The default behavior for Neo4jGraph is {@code true}.
+ *
+ * @param checkElementsInTransaction check whether an element is in the transaction between
+ * returning it
+ */
+ public void checkElementsInTransaction(final boolean checkElementsInTransaction) {
+ this.checkElementsInTransaction = checkElementsInTransaction;
+ }
+
+ /**
+ * Execute the Cypher query and get the result set as a {@link GraphTraversal}.
+ *
+ * @param query the Cypher query to execute
+ * @return a fluent Gremlin traversal
+ */
+ public <S, E> GraphTraversal<S, E> cypher(final String query) {
+ return cypher(query, Collections.emptyMap());
+ }
+
+ /**
+ * Execute the Cypher query with provided parameters and get the result set as a {@link GraphTraversal}.
+ *
+ * @param query the Cypher query to execute
+ * @param parameters the parameters of the Cypher query
+ * @return a fluent Gremlin traversal
+ */
+ public <S, E> GraphTraversal<S, E> cypher(final String query, final Map<String, Object> parameters) {
+ this.tx().readWrite();
+ final GraphTraversal.Admin<S, E> traversal = new DefaultGraphTraversal<>(this);
+ Iterator result = this.baseGraph.execute(query, parameters);
+ traversal.addStep(new StartStep(traversal, new Neo4jCypherIterator<S>(result, this)));
+ return traversal;
+ }
+
+ public Iterator<Map<String, Object>> execute(String query, Map<String, Object> params) {
+ return new Neo4jCypherIterator(baseGraph.execute(query, params), this);
+ }
+
+ class Neo4jTransaction extends AbstractTransaction {
+
+ protected final ThreadLocal<Neo4jTx> threadLocalTx = ThreadLocal.withInitial(() -> null);
+
+ public Neo4jTransaction() {
+ super(Neo4jGraph.this);
+ }
+
+ @Override
+ public void doOpen() {
+ threadLocalTx.set(getBaseGraph().tx());
+ }
+
+ @Override
+ public void doCommit() throws TransactionException {
+ try {
+ threadLocalTx.get().success();
+ } catch (Exception ex) {
+ throw new TransactionException(ex);
+ } finally {
+ threadLocalTx.get().close();
+ threadLocalTx.remove();
+ }
+ }
+
+ @Override
+ public void doRollback() throws TransactionException {
+ try {
+// javax.transaction.Transaction t = transactionManager.getTransaction();
+// if (null == t || t.getStatus() == javax.transaction.Status.STATUS_ROLLEDBACK)
+// return;
+
+ threadLocalTx.get().failure();
+ } catch (Exception e) {
+ throw new TransactionException(e);
+ } finally {
+ threadLocalTx.get().close();
+ threadLocalTx.remove();
+ }
+ }
+
+ @Override
+ public boolean isOpen() {
+ return (threadLocalTx.get() != null);
+ }
+ }
+
+ public class Neo4jGraphFeatures implements Features {
+ private final GraphFeatures graphFeatures = new Neo4jGraphGraphFeatures();
+ private final VertexFeatures vertexFeatures = new Neo4jVertexFeatures();
+ private final EdgeFeatures edgeFeatures = new Neo4jEdgeFeatures();
+
+ @Override
+ public GraphFeatures graph() {
+ return graphFeatures;
+ }
+
+ @Override
+ public VertexFeatures vertex() {
+ return vertexFeatures;
+ }
+
+ @Override
+ public EdgeFeatures edge() {
+ return edgeFeatures;
+ }
+
+ @Override
+ public String toString() {
+ return StringFactory.featureString(this);
+ }
+
+ public class Neo4jGraphGraphFeatures implements GraphFeatures {
+
+ private VariableFeatures variableFeatures = new Neo4jGraphVariables.Neo4jVariableFeatures();
+
+ Neo4jGraphGraphFeatures() {
+ }
+
+ @Override
+ public boolean supportsComputer() {
+ return false;
+ }
+
+ @Override
+ public VariableFeatures variables() {
+ return variableFeatures;
+ }
+
+ @Override
+ public boolean supportsThreadedTransactions() {
+ return false;
+ }
+ }
+
+ public class Neo4jVertexFeatures extends Neo4jElementFeatures implements VertexFeatures {
+
+ private final VertexPropertyFeatures vertexPropertyFeatures = new Neo4jVertexPropertyFeatures();
+
+ Neo4jVertexFeatures() {
+ }
+
+ @Override
+ public VertexPropertyFeatures properties() {
+ return vertexPropertyFeatures;
+ }
+
+ @Override
+ public boolean supportsMetaProperties() {
+ return Neo4jGraph.this.supportsMetaProperties;
+ }
+
+ @Override
+ public boolean supportsMultiProperties() {
+ return Neo4jGraph.this.supportsMultiProperties;
+ }
+ }
+
+ public class Neo4jEdgeFeatures extends Neo4jElementFeatures implements EdgeFeatures {
+
+ private final EdgePropertyFeatures edgePropertyFeatures = new Neo4jEdgePropertyFeatures();
+
+ Neo4jEdgeFeatures() {
+ }
+
+ @Override
+ public EdgePropertyFeatures properties() {
+ return edgePropertyFeatures;
+ }
+ }
+
+ public class Neo4jElementFeatures implements ElementFeatures {
+
+ Neo4jElementFeatures() {
+ }
+
+ @Override
+ public boolean supportsUserSuppliedIds() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsStringIds() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsUuidIds() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsAnyIds() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCustomIds() {
+ return false;
+ }
+ }
+
+ public class Neo4jVertexPropertyFeatures implements VertexPropertyFeatures {
+
+ Neo4jVertexPropertyFeatures() {
+ }
+
+ @Override
+ public boolean supportsMapValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsMixedListValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSerializableValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsUniformListValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsUserSuppliedIds() {
+ return false;
+ }
+ }
+
+ public class Neo4jEdgePropertyFeatures implements EdgePropertyFeatures {
+
+ Neo4jEdgePropertyFeatures() {
+ }
+
+ @Override
+ public boolean supportsMapValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsMixedListValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSerializableValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsUniformListValues() {
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraphVariables.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraphVariables.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraphVariables.java
new file mode 100644
index 0000000..9eef1ed
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraphVariables.java
@@ -0,0 +1,176 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.util.GraphVariableHelper;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.neo4j.tinkerpop.api.Neo4jGraphAPI;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class Neo4jGraphVariables implements Graph.Variables {
+
+ private final Neo4jGraph graph;
+ private final Neo4jGraphAPI baseGraph;
+
+ protected Neo4jGraphVariables(final Neo4jGraph graph) {
+ this.graph = graph;
+ baseGraph = graph.getBaseGraph();
+ }
+
+ @Override
+ public Set<String> keys() {
+ this.graph.tx().readWrite();
+ final Set<String> keys = new HashSet<>();
+ for (final String key : this.baseGraph.getKeys()) {
+ if (!Graph.Hidden.isHidden(key))
+ keys.add(key);
+ }
+ return keys;
+ }
+
+ @Override
+ public <R> Optional<R> get(final String key) {
+ this.graph.tx().readWrite();
+ return this.baseGraph.hasProperty(key) ?
+ Optional.of((R) this.baseGraph.getProperty(key)) :
+ Optional.<R>empty();
+ }
+
+ @Override
+ public void set(final String key, final Object value) {
+ GraphVariableHelper.validateVariable(key, value);
+ this.graph.tx().readWrite();
+ try {
+ this.baseGraph.setProperty(key, value);
+ } catch (final IllegalArgumentException e) {
+ throw Graph.Variables.Exceptions.dataTypeOfVariableValueNotSupported(value);
+ }
+ }
+
+ @Override
+ public void remove(final String key) {
+ this.graph.tx().readWrite();
+ if (this.baseGraph.hasProperty(key))
+ this.baseGraph.removeProperty(key);
+ }
+
+ @Override
+ public String toString() {
+ return StringFactory.graphVariablesString(this);
+ }
+
+ public static class Neo4jVariableFeatures implements Graph.Features.VariableFeatures {
+ @Override
+ public boolean supportsBooleanValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsDoubleValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsFloatValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsIntegerValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsLongValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsMapValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsMixedListValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsByteValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsBooleanArrayValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsByteArrayValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDoubleArrayValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsFloatArrayValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsIntegerArrayValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsLongArrayValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsStringArrayValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsSerializableValues() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsStringValues() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsUniformListValues() {
+ return false;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jHelper.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jHelper.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jHelper.java
new file mode 100644
index 0000000..e5de427
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jHelper.java
@@ -0,0 +1,63 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.neo4j.tinkerpop.api.Neo4jNode;
+import org.neo4j.tinkerpop.api.Neo4jRelationship;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class Neo4jHelper {
+
+ private Neo4jHelper() {
+ }
+
+ public static org.neo4j.tinkerpop.api.Neo4jDirection mapDirection(final Direction direction) {
+ if (direction.equals(Direction.OUT))
+ return org.neo4j.tinkerpop.api.Neo4jDirection.OUTGOING;
+ else if (direction.equals(Direction.IN))
+ return org.neo4j.tinkerpop.api.Neo4jDirection.INCOMING;
+ else
+ return org.neo4j.tinkerpop.api.Neo4jDirection.BOTH;
+ }
+
+ public static boolean isDeleted(final Neo4jNode node) {
+ try {
+ node.getKeys();
+ return false;
+ } catch (final IllegalStateException e) {
+ return true;
+ }
+ }
+
+ public static boolean isDeleted(final Neo4jRelationship relationship) {
+ try {
+ relationship.type();
+ return false;
+ } catch (final IllegalStateException e) {
+ return true;
+ }
+ }
+
+ public static boolean isNotFound(RuntimeException ex) {
+ return ex.getClass().getSimpleName().equals("NotFoundException");
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/b2c49ddd/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jProperty.java
----------------------------------------------------------------------
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jProperty.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jProperty.java
new file mode 100644
index 0000000..69705ab
--- /dev/null
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jProperty.java
@@ -0,0 +1,99 @@
+/*
+ * 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.neo4j.structure;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.neo4j.tinkerpop.api.Neo4jEntity;
+import org.neo4j.tinkerpop.api.Neo4jNode;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class Neo4jProperty<V> implements Property<V> {
+
+ private final Element element;
+ private final String key;
+ private final Neo4jGraph graph;
+ private V value;
+
+ public Neo4jProperty(final Element element, final String key, final V value) {
+ this.element = element;
+ this.key = key;
+ this.value = value;
+ this.graph = element instanceof Neo4jVertexProperty ?
+ ((Neo4jVertex) (((Neo4jVertexProperty) element).element())).graph :
+ ((Neo4jElement) element).graph;
+ }
+
+ @Override
+ public Element element() {
+ return this.element;
+ }
+
+ @Override
+ public String key() {
+ return this.key;
+ }
+
+ @Override
+ public V value() {
+ return this.value;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return null != this.value;
+ }
+
+ @Override
+ public String toString() {
+ return StringFactory.propertyString(this);
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ return ElementHelper.areEqual(this, object);
+ }
+
+ @Override
+ public int hashCode() {
+ return ElementHelper.hashCode(this);
+ }
+
+ @Override
+ public void remove() {
+ this.graph.tx().readWrite();
+ if (this.element instanceof VertexProperty) {
+ final Neo4jNode node = ((Neo4jVertexProperty) this.element).getBaseVertex();
+ if (null != node && node.hasProperty(this.key)) {
+ node.removeProperty(this.key);
+ }
+ } else {
+ final Neo4jEntity entity = ((Neo4jElement) this.element).getBaseElement();
+ if (entity.hasProperty(this.key)) {
+ entity.removeProperty(this.key);
+ }
+ }
+ }
+
+}
\ No newline at end of file