/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.datastore.neo4j.utils;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.ogm.datastore.neo4j.HttpNeo4jDialect;
import org.hibernate.ogm.datastore.neo4j.logging.impl.Log;
import org.hibernate.ogm.datastore.neo4j.logging.impl.LoggerFactory;
import org.hibernate.ogm.datastore.neo4j.remote.http.impl.HttpNeo4jClient;
import org.hibernate.ogm.datastore.neo4j.remote.http.impl.HttpNeo4jDatastoreProvider;
import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.ErrorResponse;
import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.Graph;
import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.Graph.Node;
import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.Row;
import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.Statement;
import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.Statements;
import org.hibernate.ogm.datastore.neo4j.remote.http.json.impl.StatementsResponse;
import org.hibernate.ogm.datastore.neo4j.test.dsl.NodeForGraphAssertions;
import org.hibernate.ogm.datastore.neo4j.test.dsl.RelationshipsChainForGraphAssertions;
import org.hibernate.ogm.datastore.spi.DatastoreProvider;
import org.hibernate.ogm.dialect.impl.IdentifiableDriver;
import org.hibernate.ogm.dialect.spi.GridDialect;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Davide D'Alto
*/
class HttpNeo4jTestHelperDelegate implements Neo4jTestHelperDelegate {
public static final HttpNeo4jTestHelperDelegate INSTANCE = new HttpNeo4jTestHelperDelegate();
private static final Log log = LoggerFactory.getLogger();
private HttpNeo4jTestHelperDelegate() {
}
@Override
public long getNumberOfEntities(Session session, DatastoreProvider provider) {
HttpNeo4jClient remoteNeo4j = createClient( provider );
Statement statement = new Statement( ENTITY_COUNT_QUERY );
statement.setResultDataContents( Arrays.asList( Statement.AS_ROW ) );
return readCountFromResponse( session, remoteNeo4j, statement );
}
@Override
public long getNumberOfAssociations(Session session, DatastoreProvider provider) {
HttpNeo4jClient remoteNeo4j = createClient( provider );
Statement statement = new Statement( ASSOCIATION_COUNT_QUERY );
statement.setResultDataContents( Arrays.asList( Statement.AS_ROW ) );
return readCountFromResponse( session, remoteNeo4j, statement );
}
private HttpNeo4jClient createClient(DatastoreProvider provider) {
HttpNeo4jClient remoteNeo4j = ( (HttpNeo4jDatastoreProvider) provider ).getClient();
return remoteNeo4j;
}
@Override
public GridDialect getDialect(DatastoreProvider datastoreProvider) {
return new HttpNeo4jDialect( (HttpNeo4jDatastoreProvider) datastoreProvider );
}
@Override
public Graph.Node findNode(DatastoreProvider datastoreProvider, NodeForGraphAssertions node) {
String nodeAsCypher = node.toCypher();
String query = "MATCH " + nodeAsCypher + " RETURN " + node.getAlias();
Statements statements = new Statements();
statements.addStatement( query, node.getParams() );
HttpNeo4jClient client = createClient( datastoreProvider );
StatementsResponse response = client.executeQueriesInNewTransaction( statements );
validate( response );
List<Row> data = response.getResults().get( 0 ).getData();
if ( data.isEmpty() ) {
return null;
}
List<Node> nodes = data.get( 0 ).getGraph().getNodes();
if ( nodes.size() > 1 ) {
throw new HibernateException( "Unique result expected" );
}
Graph.Node nodeFound = nodes.get( 0 );
return nodeFound;
}
@Override
public Map<String, Object> findProperties(DatastoreProvider datastoreProvider, NodeForGraphAssertions node) {
Graph.Node result = findNode( datastoreProvider, node );
return result.getProperties();
}
@Override
public Object findRelationshipStartNode(DatastoreProvider datastoreProvider, RelationshipsChainForGraphAssertions relationship) {
HttpNeo4jClient client = createClient( datastoreProvider );
String relationshipAsCypher = relationship.toCypher();
NodeForGraphAssertions node = relationship.getStart();
String query = "MATCH " + relationshipAsCypher + " RETURN " + node.getAlias();
Statements statements = new Statements();
statements.addStatement( query, relationship.getParams() );
StatementsResponse response = client.executeQueriesInNewTransaction( statements );
validate( response );
List<Row> data = response.getResults().get( 0 ).getData();
if ( data.isEmpty() ) {
return null;
}
List<Node> nodes = data.get( 0 ).getGraph().getNodes();
if ( nodes.size() > 1 ) {
throw new NotUniqueException();
}
return nodes.get( 0 );
}
private long readCountFromResponse(Session session, HttpNeo4jClient remoteNeo4j, Statement statement) {
Statements statements = new Statements();
statements.addStatement( statement );
StatementsResponse response = null;
if ( session != null ) {
Long transactionId = transactionId( session );
if ( transactionId != null ) {
response = remoteNeo4j.executeQueriesInOpenTransaction( transactionId, statements );
}
else {
// Transaction rollbacked or committed
response = remoteNeo4j.executeQueriesInNewTransaction( statements );
}
}
else {
response = remoteNeo4j.executeQueriesInNewTransaction( statements );
}
return ( (Integer) response.getResults().get( 0 ).getData().get( 0 ).getRow().get( 0 ) ).longValue();
}
@Override
public void deleteAllElements(DatastoreProvider datastoreProvider) {
HttpNeo4jDatastoreProvider remoteProvider = (HttpNeo4jDatastoreProvider) datastoreProvider;
Statements statements = new Statements();
statements.addStatement( DELETE_ALL );
( (HttpNeo4jClient) remoteProvider.getClient() ).executeQueriesInNewTransaction( statements );
}
@Override
public void dropDatabase(DatastoreProvider datastoreProvider) {
deleteAllElements( datastoreProvider );
}
@Override
public void executeCypherQuery(DatastoreProvider datastoreProvider, String query, Map<String, Object> parameters) {
HttpNeo4jDatastoreProvider provider = (HttpNeo4jDatastoreProvider) datastoreProvider;
Statements statements = new Statements();
statements.addStatement( query, parameters );
StatementsResponse statementsResponse = ( (HttpNeo4jClient) provider.getClient() ).executeQueriesInNewTransaction( statements );
validate( statementsResponse );
}
private void validate(StatementsResponse readEntity) {
if ( !readEntity.getErrors().isEmpty() ) {
ErrorResponse errorResponse = readEntity.getErrors().get( 0 );
throw log.nativeQueryException( errorResponse.getCode(), errorResponse.getMessage(), null );
}
}
@Override
public Long executeCountQuery(DatastoreProvider datastoreProvider, String queryString) {
Statement statement = new Statement( queryString );
statement.setResultDataContents( Arrays.asList( Statement.AS_ROW ) );
Statements statements = new Statements();
statements.addStatement( statement );
HttpNeo4jDatastoreProvider remoteProvider = (HttpNeo4jDatastoreProvider) datastoreProvider;
StatementsResponse response = ( (HttpNeo4jClient) remoteProvider.getClient() ).executeQueriesInNewTransaction( statements );
Object count = response.getResults().get( 0 ).getData().get( 0 ).getRow().get( 0 );
return Long.valueOf( count.toString() );
}
private Long transactionId(Session session) {
IdentifiableDriver driver = (IdentifiableDriver) ( ( (SessionImplementor) session ).getTransactionCoordinator().getTransactionDriverControl() );
if ( session.getTransaction().getStatus() != TransactionStatus.ACTIVE ) {
return null;
}
Long transactionId = (Long) driver.getTransactionId();
return transactionId;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class RowStatementsResponse {
private List<RowStatementsResult> results;
private List<ErrorResponse> errors;
public List<RowStatementsResult> getResults() {
return results;
}
public void setResults(List<RowStatementsResult> results) {
this.results = results;
}
public List<ErrorResponse> getErrors() {
return errors;
}
public void setErrors(List<ErrorResponse> errors) {
this.errors = errors;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class RowStatementsResult {
private List<String> columns;
private List<RowArray> data;
public List<String> getColumns() {
return columns;
}
public void setColumns(List<String> columns) {
this.columns = columns;
}
public List<RowArray> getData() {
return data;
}
public void setData(List<RowArray> data) {
this.data = data;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class RowArray {
private List<Object> row;
public List<Object> getRow() {
return row;
}
public void setRow(List<Object> row) {
this.row = row;
}
}
}