extracted QueryEngine, added REST Query Engine Support
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<org.slf4j.version>1.5.10</org.slf4j.version>
|
||||
<org.springframework.version>3.0.5.RELEASE</org.springframework.version>
|
||||
<data.commons.version>1.1.0.BUILD-SNAPSHOT</data.commons.version>
|
||||
<neo4j.version>1.4.M04</neo4j.version>
|
||||
<neo4j.version>1.4-SNAPSHOT</neo4j.version>
|
||||
<aspectj.version>1.6.12.M1</aspectj.version>
|
||||
</properties>
|
||||
<profiles>
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<artifactId>neo4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.neo4j</groupId>
|
||||
<groupId>org.neo4j.server.plugin</groupId>
|
||||
<artifactId>neo4j-cypher-plugin</artifactId>
|
||||
<version>${neo4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
@@ -22,10 +22,13 @@ import org.neo4j.graphdb.event.KernelEventHandler;
|
||||
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.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
import org.springframework.data.graph.neo4j.rest.support.index.RestIndexManager;
|
||||
import org.springframework.data.graph.neo4j.support.query.ConversionServiceQueryResultConverter;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryEngine;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryResultConverter;
|
||||
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import java.net.URI;
|
||||
@@ -35,6 +38,7 @@ public class RestGraphDatabase implements GraphDatabaseService, GraphDatabase {
|
||||
|
||||
private RestRequest restRequest;
|
||||
private long propertyRefetchTimeInMillis = 1000;
|
||||
private ConversionService conversionService;
|
||||
|
||||
|
||||
public RestGraphDatabase( URI uri ) {
|
||||
@@ -99,7 +103,18 @@ public class RestGraphDatabase implements GraphDatabaseService, GraphDatabase {
|
||||
|
||||
@Override
|
||||
public QueryEngine queryEngineFor(QueryEngine.Type type) {
|
||||
return new RestQueryEngine(restRequest);
|
||||
return new RestQueryEngine(this, createResultConverter());
|
||||
}
|
||||
|
||||
private ConversionServiceQueryResultConverter createResultConverter() {
|
||||
if (conversionService==null) return null;
|
||||
return new ConversionServiceQueryResultConverter(conversionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public RestIndexManager index() {
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 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.graph.neo4j.rest.support;
|
||||
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import org.neo4j.helpers.collection.IterableWrapper;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryEngine;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryResultConverter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 22.06.11
|
||||
*/
|
||||
public class RestQueryEngine implements QueryEngine {
|
||||
private final RestRequest restRequest;
|
||||
private final RestGraphDatabase restGraphDatabase;
|
||||
private final QueryResultConverter resultConverter;
|
||||
|
||||
public RestQueryEngine(RestGraphDatabase restGraphDatabase, QueryResultConverter resultConverter) {
|
||||
this.restGraphDatabase = restGraphDatabase;
|
||||
this.resultConverter = resultConverter;
|
||||
this.restRequest = restGraphDatabase.getRestRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Map<String, Object>> query(String statement) {
|
||||
return executeStatement(statement).getData();
|
||||
}
|
||||
|
||||
private RestQueryResult executeStatement(String statement) {
|
||||
final ClientResponse response = restRequest.get("ext/CypherPlugin/graphdb/execute_query", JsonHelper.createJsonFrom(Collections.singletonMap("query", statement)));
|
||||
return new RestQueryResult(restRequest.toMap(response));
|
||||
}
|
||||
|
||||
class RestQueryResult {
|
||||
List<String> columns;
|
||||
List<Map<String,Object>> data;
|
||||
|
||||
public RestQueryResult(Map<?, ?> result) {
|
||||
columns= (List<String>) result.get("columns");
|
||||
extractData(result);
|
||||
}
|
||||
|
||||
private void extractData(Map<?, ?> result) {
|
||||
List<List<?>> rows= (List<List<?>>) result.get("data");
|
||||
data=new ArrayList<Map<String, Object>>(rows.size());
|
||||
for (List<?> row : rows) {
|
||||
data.add(mapRow(row));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> mapRow(List<?> row) {
|
||||
int columnCount=columns.size();
|
||||
Map<String,Object> newRow=new HashMap<String, Object>(columnCount);
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
final Object value = row.get(i);
|
||||
newRow.put(columns.get(i), convertValue(value));
|
||||
}
|
||||
return newRow;
|
||||
}
|
||||
|
||||
private Object convertValue(Object value) {
|
||||
final Object representationValue = convertFromRepresentation(value);
|
||||
return resultConverter.convertValue(representationValue, null);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public List<String> getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
public <T> T getSingleValue(Class<T> type) {
|
||||
if (data.size()==0) throw new InvalidDataAccessResourceUsageException("Expected single result, got none");
|
||||
if (data.size()!=1) throw new InvalidDataAccessResourceUsageException("Expected single result, got more than one");
|
||||
return getSingleColumn(type).iterator().next();
|
||||
}
|
||||
|
||||
public <T> Iterable<T> getSingleColumn(final Class<T> type) {
|
||||
if (columns.size()==0) throw new InvalidDataAccessResourceUsageException("Expected single column, got none");
|
||||
if (columns.size()!=1) throw new InvalidDataAccessResourceUsageException("Expected single column, got more than one");
|
||||
final String firstColumn = columns.get(0);
|
||||
return new IterableWrapper<T, Map<String,Object>>(data) {
|
||||
@Override
|
||||
protected T underlyingObjectToObject(Map<String,Object> row) {
|
||||
return resultConverter.convertValue(row.get(firstColumn),type);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> query(String statement, Class<T> type) {
|
||||
final RestQueryResult restQueryResult = executeStatement(statement);
|
||||
return restQueryResult.getSingleColumn(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T queryForObject(String statement, Class<T> type) {
|
||||
return executeStatement(statement).getSingleValue(type);
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,14 @@ public class RestRequest {
|
||||
return builder( path ).get( ClientResponse.class );
|
||||
}
|
||||
|
||||
public ClientResponse get( String path, String data ) {
|
||||
Builder builder = builder(path);
|
||||
if ( data != null ) {
|
||||
builder = builder.entity( data, MediaType.APPLICATION_JSON_TYPE );
|
||||
}
|
||||
return builder.get(ClientResponse.class);
|
||||
}
|
||||
|
||||
public ClientResponse delete( String path ) {
|
||||
return builder( path ).delete( ClientResponse.class );
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ public class RestNeo4jTemplateTest extends Neo4jTemplateTest
|
||||
protected GraphDatabase createGraphDatabase() throws Exception
|
||||
{
|
||||
testHelper.cleanDb();
|
||||
return testHelper.createGraphDatabase();
|
||||
final GraphDatabase graphDatabase = testHelper.createGraphDatabase();
|
||||
graphDatabase.setConversionService(conversionService);
|
||||
return graphDatabase;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.springframework.data.graph.neo4j.rest.support;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase;
|
||||
import org.springframework.data.graph.neo4j.support.GraphRepositoryTest;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryEngineTest;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.BeforeTransaction;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 23.06.11
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml",
|
||||
"classpath:RestTest-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestQueryEngineTest extends QueryEngineTest {
|
||||
|
||||
@Autowired
|
||||
RestGraphDatabase restGraphDatabase;
|
||||
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
RestTestBase.startDb();
|
||||
}
|
||||
|
||||
@BeforeTransaction
|
||||
public void cleanDb() {
|
||||
RestTestBase.cleanDb();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
RestTestBase.shutdownDb();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GraphDatabase createGraphDatabase() throws Exception {
|
||||
restGraphDatabase.setConversionService(conversionService);
|
||||
return restGraphDatabase;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@ import org.neo4j.graphdb.Relationship;
|
||||
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.graph.neo4j.support.query.EmbeddedQueryEngine;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryEngine;
|
||||
|
||||
|
||||
public interface GraphDatabase {
|
||||
@@ -91,4 +94,8 @@ public interface GraphDatabase {
|
||||
* @return a TraversalDescription as starting point for defining a traversal
|
||||
*/
|
||||
TraversalDescription createTraversalDescription();
|
||||
|
||||
QueryEngine queryEngineFor(EmbeddedQueryEngine.Type type);
|
||||
|
||||
void setConversionService(ConversionService conversionService);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,13 @@ import org.neo4j.graphdb.index.IndexManager;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.index.impl.lucene.LuceneIndexImplementation;
|
||||
import org.neo4j.kernel.Traversal;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
import org.springframework.data.graph.neo4j.support.query.ConversionServiceQueryResultConverter;
|
||||
import org.springframework.data.graph.neo4j.support.query.EmbeddedQueryEngine;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryEngine;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryResultConverter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -34,14 +39,14 @@ import java.util.Map;
|
||||
public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
|
||||
protected GraphDatabaseService delegate;
|
||||
private ConversionService conversionService;
|
||||
|
||||
public DelegatingGraphDatabase(final GraphDatabaseService delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getReferenceNode() {
|
||||
return delegate.getReferenceNode();
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -122,7 +127,22 @@ public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
return Traversal.description();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryEngine queryEngineFor(EmbeddedQueryEngine.Type type) {
|
||||
return new EmbeddedQueryEngine(delegate, createResultConverter());
|
||||
}
|
||||
|
||||
private ConversionServiceQueryResultConverter createResultConverter() {
|
||||
if (conversionService == null) return null;
|
||||
return new ConversionServiceQueryResultConverter(conversionService);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
delegate.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getReferenceNode() {
|
||||
return delegate.getReferenceNode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.graph.neo4j.support.query;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 22.06.11
|
||||
*/
|
||||
public class ConversionServiceQueryResultConverter implements QueryResultConverter {
|
||||
private final ConversionService conversionService;
|
||||
|
||||
public ConversionServiceQueryResultConverter(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T convertValue(Object value, Class<T> type) {
|
||||
if (type==null || type.isInstance(value)) return (T) value;
|
||||
return conversionService.convert(value,type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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.graph.neo4j.support.query;
|
||||
|
||||
import org.neo4j.cypher.SyntaxError;
|
||||
import org.neo4j.cypher.commands.Query;
|
||||
import org.neo4j.cypher.javacompat.CypherParser;
|
||||
import org.neo4j.cypher.javacompat.ExecutionEngine;
|
||||
import org.neo4j.cypher.javacompat.ExecutionResult;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.helpers.collection.IterableWrapper;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class EmbeddedQueryEngine implements QueryEngine {
|
||||
|
||||
public EmbeddedQueryEngine(GraphDatabaseService graphDatabaseService) {
|
||||
this(graphDatabaseService, QueryResultConverter.NO_OP_QUERY_RESULT_CONVERTER);
|
||||
}
|
||||
|
||||
final ExecutionEngine executionEngine;
|
||||
private QueryResultConverter resultConverter;
|
||||
|
||||
public EmbeddedQueryEngine(GraphDatabaseService graphDatabaseService, QueryResultConverter resultConverter) {
|
||||
this.resultConverter = resultConverter != null ? resultConverter : QueryResultConverter.NO_OP_QUERY_RESULT_CONVERTER;
|
||||
this.executionEngine = new ExecutionEngine(graphDatabaseService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Map<String, Object>> query(String statement) {
|
||||
try {
|
||||
ExecutionResult result = parseAndExecuteQuery(statement);
|
||||
return convertResult(result);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> query(String statement, Class<T> type) {
|
||||
try {
|
||||
ExecutionResult result = parseAndExecuteQuery(statement);
|
||||
return convertResult(result, type);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement + " for type " + type, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T queryForObject(String statement, Class<T> type) {
|
||||
try {
|
||||
ExecutionResult result = parseAndExecuteQuery(statement);
|
||||
final Iterable<T> convertedResult = convertResult(result, type);
|
||||
return extractSingleResult(convertedResult);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement + " for type " + type, e);
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutionResult parseAndExecuteQuery(String statement) {
|
||||
try {
|
||||
CypherParser parser = new CypherParser();
|
||||
Query query = parser.parse(statement);
|
||||
return executionEngine.execute(query);
|
||||
} catch (SyntaxError syntaxError) {
|
||||
throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, syntaxError);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T extractSingleResult(Iterable<T> convertedResult) {
|
||||
final Iterator<T> it = convertedResult.iterator();
|
||||
if (!it.hasNext()) throw new InvalidDataAccessResourceUsageException("Expected single result, got none");
|
||||
T value = it.hasNext() ? it.next() : null;
|
||||
if (it.hasNext())
|
||||
throw new InvalidDataAccessResourceUsageException("Expected single result, got more than one");
|
||||
return value;
|
||||
}
|
||||
|
||||
private <T> Iterable<T> convertResult(ExecutionResult result, final Class<T> type) {
|
||||
final List<String> columns = result.columns();
|
||||
if (columns.size() != 1)
|
||||
throw new InvalidDataAccessResourceUsageException("Expected single column of results, got " + columns);
|
||||
final String column = columns.get(0);
|
||||
return new IterableWrapper<T, Map<String, Object>>(result) {
|
||||
@Override
|
||||
protected T underlyingObjectToObject(Map<String, Object> row) {
|
||||
return resultConverter.convertValue(row.get(column), type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Iterable<Map<String, Object>> convertResult(Iterable<Map<String, Object>> result) {
|
||||
return new IterableWrapper<Map<String, Object>, Map<String, Object>>(result) {
|
||||
@Override
|
||||
protected Map<String, Object> underlyingObjectToObject(Map<String, Object> row) {
|
||||
Map<String,Object> newRow=new HashMap<String,Object>(row); // todo performance
|
||||
for (Map.Entry<String, Object> entry : newRow.entrySet()) {
|
||||
Object value = resultConverter.convertValue(entry.getValue(),null);
|
||||
if (value != entry.getValue()) {
|
||||
entry.setValue(value);
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.graph.neo4j.support.query;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 22.06.11
|
||||
*/
|
||||
public interface QueryEngine {
|
||||
Iterable<Map<String, Object>> query(String statement);
|
||||
|
||||
<T> Iterable<T> query(String statement, Class<T> type);
|
||||
|
||||
<T> T queryForObject(String statement, Class<T> type);
|
||||
|
||||
public enum Type { Cypher, Gremlin }
|
||||
}
|
||||
@@ -16,22 +16,12 @@
|
||||
|
||||
package org.springframework.data.graph.neo4j.support.query;
|
||||
|
||||
import org.neo4j.cypher.SyntaxError;
|
||||
import org.neo4j.cypher.commands.Query;
|
||||
import org.neo4j.cypher.javacompat.CypherParser;
|
||||
import org.neo4j.cypher.javacompat.ExecutionEngine;
|
||||
import org.neo4j.cypher.javacompat.ExecutionResult;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.helpers.collection.IterableWrapper;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.graph.core.TypeRepresentationStrategy;
|
||||
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -39,83 +29,29 @@ import java.util.Map;
|
||||
* @since 10.06.11
|
||||
* todo limits
|
||||
*/
|
||||
public class QueryExecutor {
|
||||
public class QueryExecutor implements QueryResultConverter {
|
||||
private final TypeRepresentationStrategy nodeTypeRepresentationStrategy;
|
||||
private final ExecutionEngine executionEngine;
|
||||
private final TypeRepresentationStrategy relationshipTypeRepresentationStrategy;
|
||||
private final ConversionService conversionService;
|
||||
private final QueryEngine queryEngine;
|
||||
|
||||
public QueryExecutor(GraphDatabaseContext ctx) {
|
||||
this.nodeTypeRepresentationStrategy = ctx.getNodeTypeRepresentationStrategy();
|
||||
relationshipTypeRepresentationStrategy = ctx.getRelationshipTypeRepresentationStrategy();
|
||||
conversionService = ctx.getConversionService();
|
||||
this.executionEngine = new ExecutionEngine(ctx.getGraphDatabaseService());
|
||||
queryEngine = new EmbeddedQueryEngine(ctx.getGraphDatabaseService(), this);
|
||||
}
|
||||
|
||||
public Iterable<Map<String, Object>> query(String statement) {
|
||||
try {
|
||||
ExecutionResult result = parseAndExecuteQuery(statement);
|
||||
return convertResult(result);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, e);
|
||||
}
|
||||
return queryEngine.query(statement);
|
||||
}
|
||||
|
||||
public <T> Iterable<T> query(String statement, Class<T> type) {
|
||||
try {
|
||||
ExecutionResult result = parseAndExecuteQuery(statement);
|
||||
return convertResult(result, type);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement + " for type " + type, e);
|
||||
}
|
||||
return queryEngine.query(statement, type);
|
||||
}
|
||||
|
||||
public <T> T queryForObject(String statement, Class<T> type) {
|
||||
try {
|
||||
ExecutionResult result = parseAndExecuteQuery(statement);
|
||||
final Iterable<T> convertedResult = convertResult(result, type);
|
||||
return extractSingleResult(convertedResult);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement + " for type " + type, e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T extractSingleResult(Iterable<T> convertedResult) {
|
||||
final Iterator<T> it = convertedResult.iterator();
|
||||
if (!it.hasNext()) throw new InvalidDataAccessResourceUsageException("Expected single result, got none");
|
||||
T value = it.hasNext() ? it.next() : null;
|
||||
if (it.hasNext())
|
||||
throw new InvalidDataAccessResourceUsageException("Expected single result, got more than one");
|
||||
return value;
|
||||
}
|
||||
|
||||
private <T> Iterable<T> convertResult(ExecutionResult result, final Class<T> type) {
|
||||
final List<String> columns = result.columns();
|
||||
if (columns.size() != 1)
|
||||
throw new InvalidDataAccessResourceUsageException("Expected single column of results, got " + columns);
|
||||
final String column = columns.get(0);
|
||||
return new IterableWrapper<T, Map<String, Object>>(result) {
|
||||
@Override
|
||||
protected T underlyingObjectToObject(Map<String, Object> row) {
|
||||
return convertValue(row.get(column), type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Iterable<Map<String, Object>> convertResult(Iterable<Map<String, Object>> result) {
|
||||
return new IterableWrapper<Map<String, Object>, Map<String, Object>>(result) {
|
||||
@Override
|
||||
protected Map<String, Object> underlyingObjectToObject(Map<String, Object> row) {
|
||||
Map<String,Object> newRow=new HashMap<String,Object>(row); // todo performance
|
||||
for (Map.Entry<String, Object> entry : newRow.entrySet()) {
|
||||
Object value = convertValue(entry.getValue());
|
||||
if (value != entry.getValue()) {
|
||||
entry.setValue(value);
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
};
|
||||
return queryEngine.queryForObject(statement,type);
|
||||
}
|
||||
|
||||
private Object convertValue(Object value) {
|
||||
@@ -127,23 +63,16 @@ public class QueryExecutor {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
private <T> T convertValue(Object value,Class<T> type) {
|
||||
|
||||
public <T> T convertValue(Object value, Class<T> type) {
|
||||
if (type == null) return (T) convertValue(value);
|
||||
if (type.isInstance(value)) return type.cast(value);
|
||||
if (value instanceof Node) {
|
||||
return (T) nodeTypeRepresentationStrategy.createEntity((Node) value,type);
|
||||
return (T) nodeTypeRepresentationStrategy.createEntity((Node) value, type);
|
||||
}
|
||||
if (value instanceof Relationship) {
|
||||
return (T) relationshipTypeRepresentationStrategy.createEntity((Relationship) value,type);
|
||||
}
|
||||
return conversionService.convert(value,type);
|
||||
}
|
||||
|
||||
private ExecutionResult parseAndExecuteQuery(String statement) {
|
||||
try {
|
||||
CypherParser parser = new CypherParser();
|
||||
Query query = parser.parse(statement);
|
||||
return executionEngine.execute(query);
|
||||
} catch (SyntaxError syntaxError) {
|
||||
throw new InvalidDataAccessResourceUsageException("Error executing statement " + statement, syntaxError);
|
||||
return (T) relationshipTypeRepresentationStrategy.createEntity((Relationship) value, type);
|
||||
}
|
||||
return conversionService.convert(value, type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.graph.neo4j.support.query;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 22.06.11
|
||||
*/
|
||||
public interface QueryResultConverter {
|
||||
<T> T convertValue(Object value, Class<T> type);
|
||||
|
||||
QueryResultConverter NO_OP_QUERY_RESULT_CONVERTER = new QueryResultConverter() {
|
||||
@Override
|
||||
public <T> T convertValue(Object value, Class<T> type) {
|
||||
return (T) value;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -21,6 +21,10 @@ import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.helpers.collection.ClosableIterable;
|
||||
import org.springframework.data.graph.neo4j.support.path.PathMapper;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
import org.springframework.data.graph.neo4j.support.query.EmbeddedQueryEngine;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryEngine;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A template with convenience operations, exception translation and implicit transaction for modifying methods
|
||||
@@ -163,4 +167,11 @@ public interface Neo4jOperations {
|
||||
*/
|
||||
<T extends PropertyContainer> T index(String indexName, T element, String field, Object value);
|
||||
|
||||
|
||||
Iterable<Map<String, Object>> query(QueryEngine.Type engineType, String statement);
|
||||
|
||||
<T> Iterable<T> query(QueryEngine.Type engineType, String statement, Class<T> type);
|
||||
|
||||
<T> T queryForObject(QueryEngine.Type engineType, String statement, Class<T> type);
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import org.springframework.data.graph.neo4j.support.path.NodePath;
|
||||
import org.springframework.data.graph.neo4j.support.path.PathMapper;
|
||||
import org.springframework.data.graph.neo4j.support.path.PathMappingIterator;
|
||||
import org.springframework.data.graph.neo4j.support.path.RelationshipPath;
|
||||
import org.springframework.data.graph.neo4j.support.query.EmbeddedQueryEngine;
|
||||
import org.springframework.data.graph.neo4j.support.query.QueryEngine;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
@@ -179,7 +181,7 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
|
||||
@Override
|
||||
public <T> ClosableIterable<T> query(String indexName, final PathMapper<T> pathMapper, String field, String value) {
|
||||
notNull(field, "field", value, "value", pathMapper, "pathMapper",indexName,"indexName");
|
||||
notNull(field, "field", value, "value", pathMapper, "pathMapper", indexName, "indexName");
|
||||
try {
|
||||
Index<? extends PropertyContainer> index = graphDatabase.getIndex(indexName);
|
||||
if (Relationship.class.isAssignableFrom(index.getEntityType())) {
|
||||
@@ -191,6 +193,10 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
}
|
||||
}
|
||||
|
||||
private QueryEngine queryEngineFor(EmbeddedQueryEngine.Type type) {
|
||||
return graphDatabase.queryEngineFor(type);
|
||||
}
|
||||
|
||||
private <T> ClosableIterable<T> mapNodes(final IndexHits<Node> nodes, final PathMapper<T> pathMapper) {
|
||||
assert nodes != null;
|
||||
assert pathMapper != null;
|
||||
@@ -314,4 +320,34 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
indexHits.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Map<String, Object>> query(QueryEngine.Type engineType, String statement) {
|
||||
return queryEngineFor(engineType).query(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> query(QueryEngine.Type engineType, String statement, Class<T> type) {
|
||||
return queryEngineFor(engineType).query(statement,type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T queryForObject(QueryEngine.Type engineType, String statement, Class<T> type) {
|
||||
return queryEngineFor(engineType).queryForObject(statement,type);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
conversion for all query methods
|
||||
|
||||
-> target type
|
||||
-> target type + mapper
|
||||
-> no conversion (or auto-conversion) -> default mapper
|
||||
|
||||
source Path Node Relationship Primitive, DomainObject
|
||||
Path -> Path, NodePath, RelationshipPath X lastNode()
|
||||
Node -> endNode(), node, Mapper X NodeEntity
|
||||
Relationship lastRel(), Mapper relationship X RelEntity
|
||||
primitive X X X primitive
|
||||
Map<String,Obj> Mapper
|
||||
*/
|
||||
@@ -18,7 +18,6 @@ package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hamcrest.core.Is;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -38,8 +37,7 @@ import java.util.Map;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
|
||||
import static org.springframework.data.graph.neo4j.Person.persistedPerson;
|
||||
@@ -77,7 +75,7 @@ public class GraphRepositoryTest {
|
||||
@Test
|
||||
@Transactional
|
||||
public void testFindIterableOfPersonWithQueryAnnotation() {
|
||||
final TestTeam testTeam = new TestTeam(graphDatabaseContext);
|
||||
final TestTeam testTeam = new TestTeam();
|
||||
testTeam.createSDGTeam();
|
||||
Iterable<Person> teamMembers = personRepository.findAllTeamMembers(testTeam.sdg);
|
||||
assertThat(asCollection(teamMembers), hasItems(testTeam.michael,testTeam.david,testTeam.emil));
|
||||
@@ -85,7 +83,7 @@ public class GraphRepositoryTest {
|
||||
@Test
|
||||
@Transactional
|
||||
public void testFindPersonWithQueryAnnotation() {
|
||||
final TestTeam testTeam = new TestTeam(graphDatabaseContext);
|
||||
final TestTeam testTeam = new TestTeam();
|
||||
testTeam.createSDGTeam();
|
||||
Person boss = personRepository.findBoss(testTeam.michael);
|
||||
assertThat(boss, is(testTeam.emil));
|
||||
@@ -93,7 +91,7 @@ public class GraphRepositoryTest {
|
||||
@Test
|
||||
@Transactional
|
||||
public void testFindIterableMapsWithQueryAnnotation() {
|
||||
final TestTeam testTeam = new TestTeam(graphDatabaseContext);
|
||||
final TestTeam testTeam = new TestTeam();
|
||||
testTeam.createSDGTeam();
|
||||
Iterable<Map<String,Object>> teamMembers = personRepository.findAllTeamMemberData(testTeam.sdg);
|
||||
assertThat(asCollection(teamMembers), hasItems(testTeam.simpleRowFor(testTeam.michael,"member"),testTeam.simpleRowFor(testTeam.david,"member"),testTeam.simpleRowFor(testTeam.emil,"member")));
|
||||
@@ -151,6 +149,20 @@ public class GraphRepositoryTest {
|
||||
assertEquals(p, pById);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testExists() {
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
boolean found = personRepository.exists(p.getNodeId());
|
||||
assertTrue("Found persisted entity", found);
|
||||
}
|
||||
@Test
|
||||
@Transactional
|
||||
public void testDoesntExist() {
|
||||
boolean found = personRepository.exists(Long.MAX_VALUE-1);
|
||||
assertFalse("Non existend id isn't foundpo ", found);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testFinderFindByIdNonexistent() {
|
||||
@@ -162,9 +174,9 @@ public class GraphRepositoryTest {
|
||||
@Test
|
||||
@Transactional
|
||||
public void testFinderCount() {
|
||||
assertEquals((Long)0L, personRepository.count());
|
||||
assertEquals(0L, personRepository.count());
|
||||
Person p = persistedPerson("Michael", 35);
|
||||
assertEquals((Long)1L, personRepository.count());
|
||||
assertEquals(1L, personRepository.count());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
|
||||
@@ -49,7 +48,7 @@ public class NodeEntityQueryTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testTeam = new TestTeam(graphDatabaseContext);
|
||||
testTeam = new TestTeam();
|
||||
testTeam.createSDGTeam();
|
||||
michael = testTeam.michael;
|
||||
}
|
||||
|
||||
@@ -200,16 +200,16 @@ public class SubReferenceNodeTypeRepresentationStrategyTest {
|
||||
log.warn("Created volvo");
|
||||
new Toyota().persist();
|
||||
log.warn("Created volvo");
|
||||
assertEquals("Wrong count for Volvo.", (Long)1L, graphRepositoryFactory.createGraphRepository(Volvo.class).count());
|
||||
assertEquals("Wrong count for Toyota.", (Long)1L, graphRepositoryFactory.createGraphRepository(Toyota.class).count());
|
||||
assertEquals("Wrong count for Car.", (Long)2L, graphRepositoryFactory.createGraphRepository(Car.class).count());
|
||||
assertEquals("Wrong count for Volvo.", 1L, graphRepositoryFactory.createGraphRepository(Volvo.class).count());
|
||||
assertEquals("Wrong count for Toyota.", 1L, graphRepositoryFactory.createGraphRepository(Toyota.class).count());
|
||||
assertEquals("Wrong count for Car.", 2L, graphRepositoryFactory.createGraphRepository(Car.class).count());
|
||||
}
|
||||
@Test
|
||||
@Transactional
|
||||
public void testCountClasses() {
|
||||
persistedPerson("Michael", 36);
|
||||
persistedPerson("David", 25);
|
||||
assertEquals("Wrong Person instance count.", (Long)2L, graphRepositoryFactory.createGraphRepository(Person.class).count());
|
||||
assertEquals("Wrong Person instance count.", 2L, graphRepositoryFactory.createGraphRepository(Person.class).count());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,14 +28,12 @@ import java.util.Map;
|
||||
* @since 13.06.11
|
||||
*/
|
||||
public class TestTeam {
|
||||
private final GraphDatabaseContext graphDatabaseContext;
|
||||
public Person michael;
|
||||
public Person emil;
|
||||
public Person david;
|
||||
public Group sdg;
|
||||
|
||||
public TestTeam(GraphDatabaseContext graphDatabaseContext) {
|
||||
this.graphDatabaseContext = graphDatabaseContext;
|
||||
public TestTeam() {
|
||||
}
|
||||
|
||||
public void createSDGTeam() {
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* 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.graph.neo4j.support.query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.internal.matchers.IsCollectionContaining;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.core.NodeBacked;
|
||||
import org.springframework.data.graph.neo4j.Person;
|
||||
import org.springframework.data.graph.neo4j.Personality;
|
||||
import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase;
|
||||
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
|
||||
import org.springframework.data.graph.neo4j.support.TestTeam;
|
||||
import org.springframework.data.graph.neo4j.support.node.Neo4jHelper;
|
||||
import org.springframework.data.graph.neo4j.template.NeoApiTest;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.transaction.BeforeTransaction;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 13.06.11
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"})
|
||||
@Transactional
|
||||
public class QueryEngineTest {
|
||||
@Autowired
|
||||
protected ConversionService conversionService;
|
||||
@Autowired
|
||||
private GraphDatabaseContext graphDatabaseContext;
|
||||
private QueryEngine queryEngine;
|
||||
private TestTeam testTeam;
|
||||
private Person michael;
|
||||
private GraphDatabase graphDatabase;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
graphDatabase = createGraphDatabase();
|
||||
testTeam = new TestTeam();
|
||||
testTeam.createSDGTeam();
|
||||
queryEngine = graphDatabase.queryEngineFor(QueryEngine.Type.Cypher);
|
||||
michael = testTeam.michael;
|
||||
}
|
||||
|
||||
@BeforeTransaction
|
||||
public void cleanDb() {
|
||||
Neo4jHelper.cleanDb(graphDatabaseContext);
|
||||
}
|
||||
|
||||
protected GraphDatabase createGraphDatabase() throws Exception {
|
||||
final DelegatingGraphDatabase graphDatabase = new DelegatingGraphDatabase(graphDatabaseContext.getGraphDatabaseService());
|
||||
graphDatabase.setConversionService(conversionService);
|
||||
return graphDatabase;
|
||||
}
|
||||
|
||||
@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<Map<String,Object>> result = IteratorUtil.asCollection(queryEngine.query(queryString));
|
||||
|
||||
assertEquals(asList(testTeam.simpleRowFor(michael,"person"),testTeam.simpleRowFor(testTeam.david,"person")),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<Node> result = IteratorUtil.asCollection(queryEngine.query(queryString, Node.class));
|
||||
|
||||
assertEquals(asList(nodeFor(testTeam.emil)),result);
|
||||
|
||||
}
|
||||
|
||||
private Node nodeFor(final NodeBacked entity) {
|
||||
return entity.getPersistentState();
|
||||
}
|
||||
private long idFor(final NodeBacked entity) {
|
||||
return entity.getNodeId();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryOtherTeamMembers() throws Exception {
|
||||
final String queryString = String.format("start person=(%d) match (person)<-[:persons]-(team)-[:persons]->(member) return member", idFor(michael));
|
||||
System.out.println("testTeam = " + testTeam.sdg.getPersons());
|
||||
final Collection<Node> result = IteratorUtil.asCollection(queryEngine.query(queryString, Node.class));
|
||||
|
||||
assertThat(result, IsCollectionContaining.hasItems(nodeFor(testTeam.david), nodeFor(testTeam.emil)));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryAllTeamMembersByTeam() throws Exception {
|
||||
final String queryString = String.format("start team=(Group,name,\"%s\") match (team)-[:persons]->(member) return member", testTeam.sdg.getName());
|
||||
final Collection<Node> result = IteratorUtil.asCollection(queryEngine.query(queryString, Node.class));
|
||||
|
||||
assertThat(result, IsCollectionContaining.hasItems(nodeFor(testTeam.david),nodeFor(testTeam.michael)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryForObjectAsGroup() throws Exception {
|
||||
final String queryString = String.format("start person=(name_index,name,\"%s\") match (person) <-[:persons]- (team) return team", michael.getName());
|
||||
final Node result = queryEngine.queryForObject(queryString, Node.class);
|
||||
|
||||
assertEquals(nodeFor(testTeam.sdg),result);
|
||||
}
|
||||
@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.queryForObject(queryString, String.class);
|
||||
|
||||
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.queryForObject(queryString, Personality.class);
|
||||
|
||||
assertEquals(michael.getPersonality(),result);
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class QueryExecutorTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testTeam = new TestTeam(graphDatabaseContext);
|
||||
testTeam = new TestTeam();
|
||||
testTeam.createSDGTeam();
|
||||
queryExecutor = new QueryExecutor(graphDatabaseContext);
|
||||
michael = testTeam.michael;
|
||||
|
||||
@@ -21,7 +21,9 @@ import org.junit.Before;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.test.ImpermanentGraphDatabase;
|
||||
import org.neo4j.kernel.impl.transaction.SpringTransactionManager;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean;
|
||||
import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
@@ -31,16 +33,22 @@ public abstract class NeoApiTest {
|
||||
protected Neo4jTemplate template;
|
||||
protected PlatformTransactionManager transactionManager;
|
||||
private GraphDatabaseService graphDatabaseService;
|
||||
protected ConversionService conversionService;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
conversionService = createConversionService();
|
||||
graph = createGraphDatabase();
|
||||
transactionManager = createTransactionManager();
|
||||
template = new Neo4jTemplate(graph, transactionManager);
|
||||
}
|
||||
|
||||
private ConversionService createConversionService() throws Exception {
|
||||
return new Neo4jConversionServiceFactoryBean().getObject();
|
||||
}
|
||||
|
||||
protected PlatformTransactionManager createTransactionManager()
|
||||
{
|
||||
return new JtaTransactionManager(new SpringTransactionManager(graphDatabaseService));
|
||||
|
||||
@@ -82,11 +82,10 @@
|
||||
<!--constructor-arg index="0" value="${neo4j.databaseDirectory}" /-->
|
||||
</bean>
|
||||
|
||||
<bean id="conversionService" class="org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean"/>
|
||||
<bean id="graphDatabaseContext" class="org.springframework.data.graph.neo4j.support.GraphDatabaseContext">
|
||||
<property name="graphDatabaseService" ref="graphDatabaseService"/>
|
||||
<property name="conversionService">
|
||||
<bean class="org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean"/>
|
||||
</property>
|
||||
<property name="conversionService" ref="conversionService"/>
|
||||
<property name="nodeTypeRepresentationStrategy" ref="nodeTypeRepresentationStrategy"/>
|
||||
<property name="relationshipTypeRepresentationStrategy" ref="relationshipTypeRepresentationStrategy"/>
|
||||
<property name="validator">
|
||||
|
||||
Reference in New Issue
Block a user