diff --git a/spring-data-graph-parent/pom.xml b/spring-data-graph-parent/pom.xml index bfce9fcc2..5ae344b25 100644 --- a/spring-data-graph-parent/pom.xml +++ b/spring-data-graph-parent/pom.xml @@ -187,6 +187,12 @@ + + com.tinkerpop.blueprints + blueprints-core + ${blueprints.version} + optional + com.tinkerpop.blueprints blueprints-neo4j-graph diff --git a/spring-data-neo4j-rest/pom.xml b/spring-data-neo4j-rest/pom.xml index 5f027d10f..a695d3384 100644 --- a/spring-data-neo4j-rest/pom.xml +++ b/spring-data-neo4j-rest/pom.xml @@ -58,6 +58,13 @@ org.neo4j.server.plugin neo4j-cypher-plugin ${neo4j.version} + test + + + org.neo4j.server.plugin + neo4j-gremlin-plugin + ${neo4j.version} + test org.neo4j.app diff --git a/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/JsonHelper.java b/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/JsonHelper.java index 4f8f0eca0..abe8834ea 100644 --- a/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/JsonHelper.java +++ b/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/JsonHelper.java @@ -44,7 +44,7 @@ public class JsonHelper { return (List>) readJson( json ); } - private static Object readJson( String json ) { + public static Object readJson( String json ) { ObjectMapper mapper = new ObjectMapper(); try { return mapper.readValue( json, Object.class ); diff --git a/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/RestCypherQueryEngine.java b/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/RestCypherQueryEngine.java index c55edd66e..2906ffaa3 100644 --- a/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/RestCypherQueryEngine.java +++ b/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/RestCypherQueryEngine.java @@ -26,7 +26,7 @@ import java.util.*; * @author mh * @since 22.06.11 */ -public class RestCypherQueryEngine implements QueryEngine { +public class RestCypherQueryEngine implements QueryEngine> { private final RestRequest restRequest; private final RestGraphDatabase restGraphDatabase; private final ResultConverter resultConverter; @@ -41,7 +41,7 @@ public class RestCypherQueryEngine implements QueryEngine { } @Override - public QueryResult> query(String statement) { + public QueryResult> query(String statement, Map params) { return executeStatement(statement); } @@ -52,8 +52,6 @@ public class RestCypherQueryEngine implements QueryEngine { static class RestQueryResult implements QueryResult> { QueryResultBuilder> result; - private final RestGraphDatabase restGraphDatabase; - @Override public ConvertedResult to(Class type) { @@ -76,49 +74,9 @@ public class RestCypherQueryEngine implements QueryEngine { } public RestQueryResult(Map result, RestGraphDatabase restGraphDatabase, ResultConverter resultConverter) { - this.restGraphDatabase = restGraphDatabase; - List columns= (List) result.get("columns"); - final List> data = extractData(result, columns); + final RestTableResultExtractor extractor = new RestTableResultExtractor(new RestEntityExtractor(restGraphDatabase)); + final List> data = extractor.extract(result); this.result=new QueryResultBuilder>(data, resultConverter); } - - private List> extractData(Map restResult, List columns) { - List> rows= (List>) restResult.get("data"); - List> result=new ArrayList>(rows.size()); - for (List row : rows) { - result.add(mapRow(columns,row)); - } - return result; - } - - private Map mapRow(List columns, List row) { - int columnCount=columns.size(); - Map newRow=new HashMap(columnCount); - for (int i = 0; i < columnCount; i++) { - final Object value = row.get(i); - newRow.put(columns.get(i), convertFromRepresentation(value)); - } - return newRow; - } - - private Object convertFromRepresentation(Object value) { - if (value instanceof Map) { - RestEntity restEntity = createRestEntity((Map) value); - if (restEntity!=null) return restEntity; - } - return value; - } - - private RestEntity createRestEntity(Map data) { - final String uri = (String) data.get("self"); - if (uri == null || uri.isEmpty()) return null; - if (uri.contains("/node/")) { - return new RestNode(data,restGraphDatabase); - } - if (uri.contains("/relationship/")) { - return new RestRelationship(data,restGraphDatabase); - } - return null; - } } } diff --git a/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/RestGraphDatabase.java b/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/RestGraphDatabase.java index 645786954..bcf37dd60 100644 --- a/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/RestGraphDatabase.java +++ b/spring-data-neo4j-rest/src/main/java/org/springframework/data/neo4j/rest/RestGraphDatabase.java @@ -23,9 +23,9 @@ import org.neo4j.graphdb.event.TransactionEventHandler; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.TraversalDescription; import org.springframework.core.convert.ConversionService; +import org.springframework.data.neo4j.conversion.ResultConverter; import org.springframework.data.neo4j.core.GraphDatabase; import org.springframework.data.neo4j.core.Property; -import org.springframework.data.neo4j.conversion.ResultConverter; import org.springframework.data.neo4j.rest.index.RestIndexManager; import org.springframework.data.neo4j.support.query.ConversionServiceQueryResultConverter; import org.springframework.data.neo4j.support.query.QueryEngine; @@ -105,7 +105,11 @@ public class RestGraphDatabase implements GraphDatabaseService, GraphDatabase { @Override public QueryEngine queryEngineFor(QueryEngine.Type type) { - return new RestCypherQueryEngine(this, createResultConverter()); + switch (type) { + case Cypher: return new RestCypherQueryEngine(this, createResultConverter()); + case Gremlin: return new RestGremlinQueryEngine(this, createResultConverter()); + } + throw new IllegalArgumentException("Unknown Query Engine Type "+type); } private ResultConverter createResultConverter() { diff --git a/spring-data-neo4j/pom.xml b/spring-data-neo4j/pom.xml index e60c9c0ca..e6119c143 100644 --- a/spring-data-neo4j/pom.xml +++ b/spring-data-neo4j/pom.xml @@ -132,12 +132,23 @@ optional + + com.tinkerpop.blueprints + blueprints-core + optional + com.tinkerpop.blueprints blueprints-neo4j-graph optional + + com.tinkerpop + gremlin + optional + + commons-configuration commons-configuration diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/conversion/QueryResultBuilder.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/conversion/QueryResultBuilder.java index 4a81fe2ac..3c820967b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/conversion/QueryResultBuilder.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/conversion/QueryResultBuilder.java @@ -21,6 +21,7 @@ import org.neo4j.helpers.collection.ClosableIterable; import org.neo4j.helpers.collection.IteratorWrapper; import java.util.Iterator; +import java.util.Map; /** * @author mh @@ -42,6 +43,14 @@ public class QueryResultBuilder implements QueryResult { this.defaultConverter = defaultConverter; } + public static String replaceParams(String statement, Map params) { + if (params==null || params.isEmpty()) return statement; + for (Map.Entry param : params.entrySet()) { + statement = statement.replaceAll(":"+param.getKey(),""+param.getValue()); + } + return statement; + } + @Override public ConvertedResult to(Class type) { return this.to(type, defaultConverter); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/GraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/GraphDatabase.java index aeaa2c95e..9c70c8456 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/GraphDatabase.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/GraphDatabase.java @@ -23,7 +23,6 @@ import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.TraversalDescription; import org.springframework.core.convert.ConversionService; -import org.springframework.data.neo4j.support.query.CypherQueryEngine; import org.springframework.data.neo4j.support.query.QueryEngine; @@ -95,7 +94,7 @@ public interface GraphDatabase { */ TraversalDescription createTraversalDescription(); - QueryEngine queryEngineFor(CypherQueryEngine.Type type); + QueryEngine queryEngineFor(QueryEngine.Type type); void setConversionService(ConversionService conversionService); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactory.java index 3ef2d3fc4..f443c4a57 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactory.java @@ -27,7 +27,7 @@ import org.springframework.data.neo4j.core.NodeBacked; import org.springframework.data.neo4j.core.RelationshipBacked; import org.springframework.data.neo4j.support.GenericTypeExtractor; import org.springframework.data.neo4j.support.GraphDatabaseContext; -import org.springframework.data.neo4j.support.query.QueryExecutor; +import org.springframework.data.neo4j.support.query.CypherQueryExecutor; import org.springframework.data.repository.core.EntityInformation; import org.springframework.data.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryMetadata; @@ -235,14 +235,14 @@ public class GraphRepositoryFactory extends RepositoryFactorySupport { } private static class GraphRepositoryQuery implements RepositoryQuery { - private QueryExecutor queryExecutor; + private CypherQueryExecutor queryExecutor; private final GraphQueryMethod queryMethod; private final RepositoryMetadata metadata; private boolean iterableResult; private Class compoundType; public GraphRepositoryQuery(GraphQueryMethod queryMethod, RepositoryMetadata metadata, final GraphDatabaseContext graphDatabaseContext) { - queryExecutor = new QueryExecutor(graphDatabaseContext); + queryExecutor = new CypherQueryExecutor(graphDatabaseContext); this.queryMethod = queryMethod; this.metadata = metadata; this.iterableResult = Iterable.class.isAssignableFrom(queryMethod.getReturnType()); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/DelegatingGraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/DelegatingGraphDatabase.java index 292cea6e5..1f14efeae 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/DelegatingGraphDatabase.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/DelegatingGraphDatabase.java @@ -27,7 +27,9 @@ import org.springframework.data.neo4j.core.GraphDatabase; import org.springframework.data.neo4j.core.Property; import org.springframework.data.neo4j.support.query.ConversionServiceQueryResultConverter; import org.springframework.data.neo4j.support.query.CypherQueryEngine; +import org.springframework.data.neo4j.support.query.GremlinQueryEngine; import org.springframework.data.neo4j.support.query.QueryEngine; + import java.util.Map; /** @@ -126,8 +128,12 @@ public class DelegatingGraphDatabase implements GraphDatabase { } @Override - public QueryEngine queryEngineFor(CypherQueryEngine.Type type) { - return new CypherQueryEngine(delegate, createResultConverter()); + public QueryEngine queryEngineFor(QueryEngine.Type type) { + switch (type) { + case Cypher: return (QueryEngine)new CypherQueryEngine(delegate, createResultConverter()); + case Gremlin: return (QueryEngine) new GremlinQueryEngine(delegate); + } + throw new IllegalArgumentException("Unknown Query Engine Type "+type); } private ConversionServiceQueryResultConverter createResultConverter() { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/GraphDatabaseContext.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/GraphDatabaseContext.java index 5c4e4129b..93405f769 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/GraphDatabaseContext.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/GraphDatabaseContext.java @@ -240,8 +240,6 @@ public class GraphDatabaseContext { return graphDatabaseService.getRelationshipById(id); } - - public GraphDatabaseService getGraphDatabaseService() { return graphDatabaseService; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj index b126cbfad..2e9a3d358 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj @@ -42,7 +42,7 @@ import org.springframework.data.neo4j.core.EntityState; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.neo4j.support.path.EntityPathPathIterableWrapper; -import org.springframework.data.neo4j.support.query.QueryExecutor; +import org.springframework.data.neo4j.support.query.CypherQueryExecutor; import javax.persistence.Transient; import javax.persistence.Entity; @@ -184,17 +184,17 @@ public aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields(traverser, targetType, Neo4jNodeBacking.aspectOf().graphDatabaseContext); } public Iterable NodeBacked.findAllByQuery(final String query, final Class targetType) { - final QueryExecutor executor = new QueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext); + final CypherQueryExecutor executor = new CypherQueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext); return executor.query(query, targetType); } public Iterable> NodeBacked.findAllByQuery(final String query) { - final QueryExecutor executor = new QueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext); + final CypherQueryExecutor executor = new CypherQueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext); return executor.queryForList(query); } public T NodeBacked.findByQuery(final String query, final Class targetType) { - final QueryExecutor executor = new QueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext); + final CypherQueryExecutor executor = new CypherQueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext); return executor.queryForObject(query, targetType); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/CypherQueryEngine.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/CypherQueryEngine.java index 95ccc4766..b62f88772 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/CypherQueryEngine.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/CypherQueryEngine.java @@ -29,11 +29,10 @@ import org.springframework.data.neo4j.conversion.ResultConverter; import java.util.Map; -public class CypherQueryEngine implements QueryEngine, QueryOperations { +public class CypherQueryEngine implements QueryEngine> { final ExecutionEngine executionEngine; private ResultConverter resultConverter; - private final DefaultQueryOperations queryOperations; public CypherQueryEngine(GraphDatabaseService graphDatabaseService) { this(graphDatabaseService, new DefaultConverter()); @@ -43,34 +42,19 @@ public class CypherQueryEngine implements QueryEngine, QueryOperations { public CypherQueryEngine(GraphDatabaseService graphDatabaseService, ResultConverter resultConverter) { this.resultConverter = resultConverter != null ? resultConverter : new DefaultConverter(); this.executionEngine = new ExecutionEngine(graphDatabaseService); - this.queryOperations = new DefaultQueryOperations(this); } @Override - public QueryResult> query(String statement) { + public QueryResult> query(String statement, Map params) { try { - ExecutionResult result = parseAndExecuteQuery(statement); + String parametrizedQuery = QueryResultBuilder.replaceParams(statement,params); + ExecutionResult result = parseAndExecuteQuery(parametrizedQuery); return new QueryResultBuilder>(result,resultConverter); } catch (Exception e) { throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, e); } } - @Override - public Iterable> queryForList(String statement) { - return queryOperations.queryForList(statement); - } - - @Override - public Iterable query(String statement, Class type) { - return queryOperations.query(statement, type); - } - - @Override - public T queryForObject(String statement, Class type) { - return queryOperations.queryForObject(statement, type); - } - private ExecutionResult parseAndExecuteQuery(String statement) { try { CypherParser parser = new CypherParser(); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryExecutor.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/CypherQueryExecutor.java similarity index 55% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryExecutor.java rename to spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/CypherQueryExecutor.java index a5df1bafe..8262005ff 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryExecutor.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/CypherQueryExecutor.java @@ -19,6 +19,8 @@ package org.springframework.data.neo4j.support.query; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.neo4j.support.conversion.EntityResultConverter; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; /** @@ -26,24 +28,31 @@ import java.util.Map; * @since 10.06.11 * todo limits */ -public class QueryExecutor implements QueryOperations { - private final QueryEngine queryEngine; +public class CypherQueryExecutor implements QueryOperations> { + private final CypherQueryEngine queryEngine; - public QueryExecutor(GraphDatabaseContext ctx) { + public CypherQueryExecutor(GraphDatabaseContext ctx) { EntityResultConverter converter = new EntityResultConverter(ctx); queryEngine = new CypherQueryEngine(ctx.getGraphDatabaseService(), converter); } - public Iterable> queryForList(String statement) { - return queryEngine.query(statement); + public Iterable> queryForList(String statement, Map...params) { + return queryEngine.query(statement,mergeParams(params)); } - public Iterable query(String statement, Class type) { - return queryEngine.query(statement).to(type); + public Iterable query(String statement, Class type, Map...params) { + return queryEngine.query(statement,mergeParams(params)).to(type); } - public T queryForObject(String statement, Class type) { - return queryEngine.query(statement).to(type).single(); + public T queryForObject(String statement, Class type, Map...params) { + return (T) queryEngine.query(statement,mergeParams(params)).to(type).single(); + } + private Map mergeParams(Map...params) { + if (params==null || params.length==0) return Collections.emptyMap(); + Map result=new HashMap(); + for (Map map : params) { + result.putAll(map); + } + return result; } - } \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/DefaultQueryOperations.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/DefaultQueryOperations.java deleted file mode 100644 index 43af09ac6..000000000 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/DefaultQueryOperations.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2011 the original author or authors. - * - * Licensed 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.springframework.data.neo4j.support.query; - -import java.util.Map; - -/** - * @author mh - * @since 28.06.11 - */ -public class DefaultQueryOperations implements QueryOperations { - private QueryEngine queryEngine; - - public DefaultQueryOperations(QueryEngine queryEngine) { - this.queryEngine = queryEngine; - } - - public Iterable> queryForList(String statement) { - return queryEngine.query(statement); - } - - public Iterable query(String statement, Class type) { - return queryEngine.query(statement).to(type); - } - - public T queryForObject(String statement, Class type) { - return queryEngine.query(statement).to(type).single(); - } - -} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/GremlinExecutor.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/GremlinExecutor.java new file mode 100644 index 000000000..2b3a0718f --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/GremlinExecutor.java @@ -0,0 +1,108 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed 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.springframework.data.neo4j.support.query; + +import com.tinkerpop.blueprints.pgm.Edge; +import com.tinkerpop.blueprints.pgm.Vertex; +import com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jEdge; +import com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jGraph; +import com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jVertex; +import com.tinkerpop.gremlin.pipes.util.Table; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.helpers.collection.IterableWrapper; + +import javax.script.*; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +@SuppressWarnings("ALL") +public class GremlinExecutor { + + public static final int REFRESH_ENGINE_COUNT = 10000; + private final String g = "g"; + private volatile ScriptEngine engine; + + private ScriptEngine createScriptEngine() { + return new ScriptEngineManager().getEngineByName("gremlin"); + } + + private static final AtomicInteger executionCount = new AtomicInteger(); + private final GraphDatabaseService graphDatabaseService; + + public GremlinExecutor(GraphDatabaseService graphDatabaseService) { + this.graphDatabaseService = graphDatabaseService; + } + + public Iterable query(String statement, Map params) { + try { + final Bindings bindings = createBindings(params); + final ScriptEngine engine = engine(); + final Object result = engine.eval(statement, bindings); + return getRepresentation(result); + } catch (final ScriptException e) { + throw new RuntimeException("Error executing statement " + statement, e); + } + } + + private Bindings createBindings(Map params) { + final Bindings bindings = new SimpleBindings(); + bindings.put(g, new Neo4jGraph(graphDatabaseService)); + if (params==null) return bindings; + for (Map.Entry entry : params.entrySet()) { + bindings.put(entry.getKey(),entry.getValue()); + } + return bindings; + } + + private ScriptEngine engine() { + if (engine == null || executionCount.incrementAndGet() > REFRESH_ENGINE_COUNT) { + executionCount.set(0); + this.engine = new ScriptEngineManager().getEngineByName("gremlin"); + } + return this.engine; + } + + + public static Iterable getRepresentation(final Object result) { + if (result instanceof Iterable) { + if (result instanceof Table) { + Table table = (Table) result; + } + return new IterableWrapper((Iterable) result) { + @Override + protected Object underlyingObjectToObject(Object object) { + return getSingleResult(object); + } + }; + } else { + return Collections.singleton(getSingleResult(result)); + } + } + + private static Object getSingleResult(Object result) { + if (result instanceof Vertex) { + return ((Neo4jVertex) result).getRawVertex(); + } else if (result instanceof Edge) { + return ((Neo4jEdge) result).getRawEdge(); + } else if (result instanceof Neo4jGraph) { + return ((Neo4jGraph) result).getRawGraph(); + } else { + return result; + } + } +} \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/GremlinQueryEngine.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/GremlinQueryEngine.java index d6d22b484..1b2066eda 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/GremlinQueryEngine.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/GremlinQueryEngine.java @@ -16,43 +16,19 @@ package org.springframework.data.neo4j.support.query; -import com.tinkerpop.blueprints.pgm.Edge; -import com.tinkerpop.blueprints.pgm.Graph; -import com.tinkerpop.blueprints.pgm.Vertex; -import com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jEdge; -import com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jGraph; -import com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jVertex; -import com.tinkerpop.gremlin.pipes.util.Table; - -import org.neo4j.cypher.commands.Query; -import org.neo4j.cypher.javacompat.CypherParser; -import org.neo4j.cypher.javacompat.ExecutionResult; import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.server.rest.repr.ListRepresentation; -import org.neo4j.server.rest.repr.Representation; -import org.neo4j.server.rest.repr.RepresentationType; -import org.neo4j.server.rest.repr.ValueRepresentation; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.neo4j.conversion.DefaultConverter; import org.springframework.data.neo4j.conversion.QueryResult; import org.springframework.data.neo4j.conversion.QueryResultBuilder; import org.springframework.data.neo4j.conversion.ResultConverter; -import sun.security.provider.certpath.Vertex; -import javax.script.*; -import java.util.ArrayList; -import java.util.List; import java.util.Map; -public class GremlinQueryEngine implements QueryEngine { +public class GremlinQueryEngine implements QueryEngine { - private final String g = "g"; - private static final ScriptEngine engine = new ScriptEngineManager().getEngineByName("gremlin"); - - - private ResultConverter resultConverter; - private final DefaultQueryOperations queryOperations; - private final GraphDatabaseService graphDatabaseService; + private final GremlinExecutor gremlinExecutor; + private final ResultConverter resultConverter; public GremlinQueryEngine(GraphDatabaseService graphDatabaseService) { this(graphDatabaseService, new DefaultConverter()); @@ -60,110 +36,15 @@ public class GremlinQueryEngine implements QueryEngine { public GremlinQueryEngine(GraphDatabaseService graphDatabaseService, ResultConverter resultConverter) { - this.graphDatabaseService = graphDatabaseService; this.resultConverter = resultConverter != null ? resultConverter : new DefaultConverter(); - this.queryOperations = new DefaultQueryOperations(this); + this.gremlinExecutor = new GremlinExecutor(graphDatabaseService); } - @Override - public QueryResult> query(String statement) { + public QueryResult query(String statement, Map params) { try { - final Neo4jGraph graph = new Neo4jGraph(graphDatabaseService); - final Bindings bindings = new SimpleBindings(); - bindings.put(g, graph); - final Object result = GremlinQueryEngine.engine.eval(statement, bindings); - return getRepresentation(graph, result); - } catch (final ScriptException e) { - throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, e); - } - - try { - - ExecutionResult result = parseAndExecuteQuery(statement); - return new QueryResultBuilder>(result, resultConverter); - } catch (Exception e) { - throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, e); - } - } - - - public static Representation getRepresentation(final Neo4jGraph graph, - final Object result) { - if (result instanceof Iterable) { - RepresentationType type = RepresentationType.STRING; - final List results = new ArrayList(); - if (result instanceof Table) { - type = RepresentationType.STRING; - results.add(new GremlinTableRepresentation((Table) result, graph)); - return new ListRepresentation(type, results); - } - for (final Object r : (Iterable) result) { - if (r instanceof Vertex) { - type = RepresentationType.NODE; - results.add(new NodeRepresentation( - ((Neo4jVertex) r).getRawVertex())); - } else if (r instanceof Edge) { - type = RepresentationType.RELATIONSHIP; - results.add(new RelationshipRepresentation( - ((Neo4jEdge) r).getRawEdge())); - } else if (r instanceof Graph) { - type = RepresentationType.STRING; - results.add(ValueRepresentation.string(graph.getRawGraph().toString())); - } else if (r instanceof Double || r instanceof Float) { - type = RepresentationType.DOUBLE; - results.add(ValueRepresentation.number(((Number) r).doubleValue())); - } else if (r instanceof Long || r instanceof Integer) { - type = RepresentationType.LONG; - results.add(ValueRepresentation.number(((Number) r).longValue())); - } else { - System.out.println("GremlinPlugin: got back" + r); - type = RepresentationType.STRING; - results.add(ValueRepresentation.string(r.toString())); - } - } - return new ListRepresentation(type, results); - } else { - return getSingleResult(graph, result); - } - } - - private static Object getSingleResult(Object result) { - if (result instanceof Vertex) { - return ((Neo4jVertex) result).getRawVertex(); - } else if (result instanceof Edge) { - return ((Neo4jEdge) result).getRawEdge(); - } else if (result instanceof Graph) { - return ValueRepresentation.string(graph.getRawGraph().toString()); - } else if (result instanceof Double || result instanceof Float) { - return ValueRepresentation.number(((Number) result).doubleValue()); - } else if (result instanceof Long || result instanceof Integer) { - return ValueRepresentation.number(((Number) result).longValue()); - } else { - return ValueRepresentation.string(result + ""); - } - } - - @Override - public Iterable> queryForList(String statement) { - return queryOperations.queryForList(statement); - } - - @Override - public Iterable query(String statement, Class type) { - return queryOperations.query(statement, type); - } - - @Override - public T queryForObject(String statement, Class type) { - return queryOperations.queryForObject(statement, type); - } - - private ExecutionResult parseAndExecuteQuery(String statement) { - try { - CypherParser parser = new CypherParser(); - Query query = parser.parse(statement); - return executionEngine.execute(query); + Iterable result = gremlinExecutor.query(statement, null); + return new QueryResultBuilder(result,resultConverter); } catch (Exception e) { throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, e); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryEngine.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryEngine.java index 56e313195..a67d5c1ce 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryEngine.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryEngine.java @@ -24,8 +24,8 @@ import java.util.Map; * @author mh * @since 22.06.11 */ -public interface QueryEngine { - QueryResult> query(String statement); +public interface QueryEngine { + QueryResult query(String statement, Map params); public enum Type { Cypher, Gremlin } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryOperations.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryOperations.java index dfa6221e4..5c5f2cf7a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryOperations.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/query/QueryOperations.java @@ -22,10 +22,10 @@ import java.util.Map; * @author mh * @since 28.06.11 */ -public interface QueryOperations { - Iterable> queryForList(String statement); +public interface QueryOperations { + Iterable queryForList(String statement, Map...params); - Iterable query(String statement, Class type); + Iterable query(String statement, Class type, Map...params); - T queryForObject(String statement, Class type); + T queryForObject(String statement, Class type, Map...params); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jOperations.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jOperations.java index a74ef89ea..fe86350ed 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jOperations.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jOperations.java @@ -99,13 +99,40 @@ public interface Neo4jOperations { */ T index(String indexName, T element, String field, Object value); + /** + * Converts the Iterable into a QueryResult object for uniform handling. E.g. + * template.convert(node.getRelationships()); + */ QueryResult convert(Iterable iterable); - QueryResult> query(String statement); + /** + * Runs the given cypher statement and packages the result in a QueryResult, simple conversions via the + * registered converter-factories are already executed via this method. + */ + QueryResult> query(String statement,Map params); + /** + * Executes the given Gremlin statement and returns the result packaged as QueryResult as Neo4j types, not + * Gremlin types. Table rows are converted to Map. + */ + QueryResult execute(String statement, Map params); + + /** + * Traverses the graph starting at the given node with the provided traversal description. The Path's of the + * traversal will be packaged into a QueryResult which can be easily converted into Nodes, Relationships or + * Graph-Entities. + */ QueryResult traverse(Node startNode, TraversalDescription traversal); + /** + * The value is looked up in the Neo4j index returning the IndexHits wrapped in a QueryResult to be converted + * into Paths or Entities. + */ QueryResult lookup(String indexName, String field, Object value); - QueryResult lookup(String indexName, Object valueOrQueryObject); + /** + * The query is executed on the index returning the IndexHits wrapped in a QueryResult to be converted + * into Paths or Entities. + */ + QueryResult lookup(String indexName, Object query); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jTemplate.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jTemplate.java index 2e7e22365..9c2c376a8 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jTemplate.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jTemplate.java @@ -18,20 +18,13 @@ package org.springframework.data.neo4j.template; import org.neo4j.graphdb.*; import org.neo4j.graphdb.index.Index; -import org.neo4j.graphdb.index.IndexHits; import org.neo4j.graphdb.traversal.TraversalDescription; -import org.neo4j.helpers.collection.ClosableIterable; -import org.neo4j.helpers.collection.IterableWrapper; import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.neo4j.conversion.QueryResult; import org.springframework.data.neo4j.conversion.QueryResultBuilder; import org.springframework.data.neo4j.core.GraphDatabase; import org.springframework.data.neo4j.core.Property; -import org.springframework.data.neo4j.support.path.NodePath; -import org.springframework.data.neo4j.support.path.PathMapper; -import org.springframework.data.neo4j.support.path.RelationshipPath; -import org.springframework.data.neo4j.support.query.CypherQueryEngine; import org.springframework.data.neo4j.support.query.QueryEngine; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; @@ -170,31 +163,10 @@ public class Neo4jTemplate implements Neo4jOperations { return new QueryResultBuilder(iterable); } - private QueryEngine queryEngineFor(CypherQueryEngine.Type type) { + private QueryEngine queryEngineFor(QueryEngine.Type type) { return graphDatabase.queryEngineFor(type); } - private ClosableIterable mapNodes(final IndexHits nodes, final PathMapper pathMapper) { - assert nodes != null; - assert pathMapper != null; - return new IndexHitsIterableWrapper(nodes, pathMapper) { - @Override - protected Path createPath(Node node) { - return new NodePath(node); - } - }; - } - private ClosableIterable mapRelationships(final IndexHits relationships, final PathMapper pathMapper) { - assert relationships != null; - assert pathMapper != null; - return new IndexHitsIterableWrapper(relationships, pathMapper) { - @Override - protected Path createPath(Relationship relationship) { - return new RelationshipPath(relationship); - } - }; - } - @Override public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Property... properties) { notNull(startNode, "startNode", endNode, "endNode", relationshipType, "relationshipType", properties, "properties"); @@ -206,33 +178,16 @@ public class Neo4jTemplate implements Neo4jOperations { }); } - private static abstract class IndexHitsIterableWrapper extends IterableWrapper implements ClosableIterable { - private final IndexHits indexHits; - private final PathMapper pathMapper; - - public IndexHitsIterableWrapper(IndexHits indexHits, PathMapper pathMapper) { - super(indexHits); - this.indexHits = indexHits; - this.pathMapper = pathMapper; - } - - @Override - protected T underlyingObjectToObject(S node) { - return pathMapper.mapPath(createPath(node)); - } - - protected abstract Path createPath(S element); - - @Override - public void close() { - indexHits.close(); - } + @Override + public QueryResult> query(String statement, Map params) { + notNull(statement, "statement"); + return queryEngineFor(QueryEngine.Type.Cypher).query(statement, params); } @Override - public QueryResult> query(String statement) { + public QueryResult execute(String statement, Map params) { notNull(statement, "statement"); - return queryEngineFor(QueryEngine.Type.Cypher).query(statement); + return queryEngineFor(QueryEngine.Type.Gremlin).query(statement, params); } @Override @@ -256,11 +211,11 @@ public class Neo4jTemplate implements Neo4jOperations { } } @Override - public QueryResult lookup(String indexName, Object valueOrQueryObject) { - notNull(valueOrQueryObject, "valueOrQueryObject", indexName, "indexName"); + public QueryResult lookup(String indexName, Object query) { + notNull(query, "valueOrQueryObject", indexName, "indexName"); try { Index index = graphDatabase.getIndex(indexName); - return new QueryResultBuilder(index.query(valueOrQueryObject)); + return new QueryResultBuilder(index.query(query)); } catch (RuntimeException e) { throw translateExceptionIfPossible(e); } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryEngineTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryEngineTest.java index c502191e5..70baa7b31 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryEngineTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryEngineTest.java @@ -21,15 +21,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.neo4j.graphdb.Node; import org.neo4j.helpers.collection.IteratorUtil; +import org.neo4j.helpers.collection.MapUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.ConversionService; import org.springframework.data.neo4j.Person; import org.springframework.data.neo4j.Personality; +import org.springframework.data.neo4j.conversion.ResultConverter; import org.springframework.data.neo4j.core.GraphDatabase; import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.Person; -import org.springframework.data.neo4j.Personality; -import org.springframework.data.neo4j.conversion.ResultConverter; import org.springframework.data.neo4j.support.DelegatingGraphDatabase; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.neo4j.support.TestTeam; @@ -59,7 +58,7 @@ public class QueryEngineTest { protected ConversionService conversionService; @Autowired private GraphDatabaseContext graphDatabaseContext; - private QueryEngine queryEngine; + private QueryEngine> queryEngine; private TestTeam testTeam; private Person michael; private GraphDatabase graphDatabase; @@ -87,31 +86,35 @@ public class QueryEngineTest { @Test @Transactional public void testQueryList() throws Exception { - final String queryString = String.format("start person=(%d,%d) return person.name, person.age", idFor(michael), idFor(testTeam.david)); - final Collection> result = IteratorUtil.asCollection(queryEngine.query(queryString)); + final String queryString = "start person=(:michael,:david) return person.name, person.age"; + final Collection> result = IteratorUtil.asCollection(queryEngine.query(queryString, MapUtil.map("michael",idFor(michael), "david",idFor(testTeam.david)))); assertEquals(asList(testTeam.simpleRowFor(michael,"person"),testTeam.simpleRowFor(testTeam.david,"person")),result); } @Test public void testQueryListOfTypeNode() throws Exception { - final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:boss]- (boss) return boss", michael.getName()); - final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString).to(Node.class)); + final String queryString = "start person=(name_index,name,\":name\") match (person) <-[:boss]- (boss) return boss"; + final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString, michaelsName()).to(Node.class)); assertEquals(asList(nodeFor(testTeam.emil)),result); } @Test public void testQueryListOfTypePerson() throws Exception { - final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:boss]- (boss) return boss", michael.getName()); - final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString).to(Person.class, new EntityResultConverter(graphDatabaseContext))); + final String queryString = "start person=(name_index,name,\":name\") match (person) <-[:boss]- (boss) return boss"; + final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString, michaelsName()).to(Person.class, new EntityResultConverter(graphDatabaseContext))); assertEquals(asList(testTeam.emil),result); } + private Map michaelsName() { + return MapUtil.map("name", michael.getName()); + } + @Test public void testQuerySingleOfTypePerson() throws Exception { - final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:boss]- (boss) return boss", michael.getName()); - final Person result = queryEngine.query(queryString).to(Person.class, new EntityResultConverter,Person>(graphDatabaseContext)).single(); + final String queryString = "start person=(name_index,name,\":name\") match (person) <-[:boss]- (boss) return boss"; + final Person result = queryEngine.query(queryString, michaelsName()).to(Person.class, new EntityResultConverter,Person>(graphDatabaseContext)).single(); assertEquals(testTeam.emil,result); } @@ -119,7 +122,7 @@ public class QueryEngineTest { @Test public void testQueryListWithCustomConverter() throws Exception { final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:boss]- (boss) return boss", michael.getName()); - final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString).to(String.class, new ResultConverter, String>() { + final Collection result = IteratorUtil.asCollection(queryEngine.query(queryString, michaelsName()).to(String.class, new ResultConverter, String>() { @Override public String convert(Map row, Class target) { return (String) ((Node) row.get("boss")).getProperty("name"); @@ -138,15 +141,15 @@ public class QueryEngineTest { @Test public void testQueryForObjectAsString() throws Exception { - final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:persons]- (team) return team.name", michael.getName()); - final String result = queryEngine.query(queryString).to(String.class).single(); + final String queryString = "start person=(name_index,name,\":name\") match (person) <-[:persons]- (team) return team.name"; + final String result = queryEngine.query(queryString, michaelsName()).to(String.class).single(); assertEquals(testTeam.sdg.getName(),result); } @Test public void testQueryForObjectAsEnum() throws Exception { - final String queryString = String.format("start person=(name_index,name,\"%s\") return person.personality", michael.getName()); - final Personality result = queryEngine.query(queryString).to(Personality.class).single(); + final String queryString = "start person=(name_index,name,\":name\") return person.personality"; + final Personality result = queryEngine.query(queryString, michaelsName()).to(Personality.class).single(); assertEquals(michael.getPersonality(),result); } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryExecutorTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryExecutorTest.java index d705ab4f7..ef8d10ac6 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryExecutorTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryExecutorTest.java @@ -23,7 +23,6 @@ import org.junit.runner.RunWith; import org.neo4j.helpers.collection.IteratorUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.neo4j.Group; -import org.springframework.data.neo4j.*; import org.springframework.data.neo4j.Person; import org.springframework.data.neo4j.Personality; import org.springframework.data.neo4j.support.GraphDatabaseContext; @@ -49,7 +48,7 @@ import static org.junit.Assert.assertThat; public class QueryExecutorTest { @Autowired GraphDatabaseContext graphDatabaseContext; - private QueryExecutor queryExecutor; + private CypherQueryExecutor queryExecutor; private TestTeam testTeam; private Person michael; @@ -57,7 +56,7 @@ public class QueryExecutorTest { public void setUp() throws Exception { testTeam = new TestTeam(); testTeam.createSDGTeam(); - queryExecutor = new QueryExecutor(graphDatabaseContext); + queryExecutor = new CypherQueryExecutor(graphDatabaseContext); michael = testTeam.michael; } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryOperationsTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryOperationsTest.java index 6749775c3..bb690a97a 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryOperationsTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryOperationsTest.java @@ -59,7 +59,7 @@ public class QueryOperationsTest { protected ConversionService conversionService; @Autowired private GraphDatabaseContext graphDatabaseContext; - private QueryOperations queryOperations; + private CypherQueryExecutor queryOperations; private TestTeam testTeam; private Person michael; private GraphDatabase graphDatabase; @@ -69,7 +69,8 @@ public class QueryOperationsTest { graphDatabase = createGraphDatabase(); testTeam = new TestTeam(); testTeam.createSDGTeam(); - queryOperations = new DefaultQueryOperations(graphDatabase.queryEngineFor(QueryEngine.Type.Cypher)); + queryOperations = new CypherQueryExecutor(graphDatabaseContext); + //new DefaultQueryOperations>(graphDatabase.queryEngineFor(QueryEngine.Type.Cypher)); michael = testTeam.michael; } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/template/Neo4jTemplateApiTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/template/Neo4jTemplateApiTest.java index a000ce3aa..820227048 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/template/Neo4jTemplateApiTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/template/Neo4jTemplateApiTest.java @@ -273,6 +273,16 @@ public class Neo4jTemplateApiTest { assertSingleResult("node1",template.traverse(referenceNode, description).to(String.class,new PathNodeNameMapper())); } + @Test + public void shouldFindNextNodeViaCypher() throws Exception { + assertSingleResult(node1, template.query("start n=(0) match n-->m return m", null).to(Node.class)); + } + + @Test + public void shouldFindNextNodeViaGremlin() throws Exception { + assertSingleResult(node1, template.execute("g.v(0).out", null).to(Node.class)); + } + @Test public void shouldGetDirectRelationship() throws Exception { assertSingleResult("rel1", template.convert(referenceNode.getRelationships()).to(String.class, new RelationshipNameConverter())); diff --git a/spring-data-neo4j/template.mf b/spring-data-neo4j/template.mf index a21ddae63..490d63cc7 100644 --- a/spring-data-neo4j/template.mf +++ b/spring-data-neo4j/template.mf @@ -27,9 +27,12 @@ Import-Template: javax.validation.*;version="0";resolution:=optional, javax.annotation.*;version="0";resolution:=optional, javax.naming.*;version="0";resolution:=optional, + javax.script.*;version="0";resolution:=optional, javax.persistence.*;version="[1.0.0, 3.0.0)";resolution:=optional, javax.persistence.spi.*;version="[1.0.0, 3.0.0)";resolution:=optional, - javax.transaction.*;version="[1.0.1, 2.0.0)";resolution:=optional -Excluded-Imports: + javax.transaction.*;version="[1.0.1, 2.0.0)";resolution:=optional, + com.tinkerpop.blueprints.*;version="[0.8,1.0)";resolution:=optional, + com.tinkerpop.gremlin.*;version="[1.1,2.0)";resolution:=optional +Excluded-Imports: org.neo4j.*.impl.*