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/29 20:43:05 UTC
[02/31] incubator-tinkerpop git commit: Neo4j-Gremlin back in the mix
-- All Apache2. Builds -- tests fail cause it can't find impl classes.
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/master
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