All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.ogm.datastore.neo4j.EmbeddedNeo4jDialect Maven / Gradle / Ivy

There is a newer version: 5.4.2.Final
Show newest version
/*
 * 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 .
 */
package org.hibernate.ogm.datastore.neo4j;

import static org.hibernate.ogm.util.impl.EmbeddedHelper.split;
import static org.neo4j.graphdb.DynamicRelationshipType.withName;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.AssertionFailure;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jAssociationQueries;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jAssociationSnapshot;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jBackendQueryResultIterator;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jEntityQueries;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jNodesTupleIterator;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jSequenceGenerator;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jTupleAssociationSnapshot;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jTupleSnapshot;
import org.hibernate.ogm.datastore.neo4j.embedded.dialect.impl.EmbeddedNeo4jTypeConverter;
import org.hibernate.ogm.datastore.neo4j.embedded.impl.EmbeddedNeo4jDatastoreProvider;
import org.hibernate.ogm.datastore.neo4j.logging.impl.GraphLogger;
import org.hibernate.ogm.datastore.neo4j.logging.impl.Log;
import org.hibernate.ogm.datastore.neo4j.logging.impl.LoggerFactory;
import org.hibernate.ogm.dialect.query.spi.BackendQuery;
import org.hibernate.ogm.dialect.query.spi.ClosableIterator;
import org.hibernate.ogm.dialect.query.spi.QueryParameters;
import org.hibernate.ogm.dialect.spi.AssociationContext;
import org.hibernate.ogm.dialect.spi.ModelConsumer;
import org.hibernate.ogm.dialect.spi.NextValueRequest;
import org.hibernate.ogm.dialect.spi.OperationContext;
import org.hibernate.ogm.dialect.spi.TransactionContext;
import org.hibernate.ogm.dialect.spi.TupleAlreadyExistsException;
import org.hibernate.ogm.dialect.spi.TupleContext;
import org.hibernate.ogm.dialect.spi.TuplesSupplier;
import org.hibernate.ogm.dialect.spi.TupleTypeContext;
import org.hibernate.ogm.entityentry.impl.TuplePointer;
import org.hibernate.ogm.model.key.spi.AssociatedEntityKeyMetadata;
import org.hibernate.ogm.model.key.spi.AssociationKey;
import org.hibernate.ogm.model.key.spi.AssociationKeyMetadata;
import org.hibernate.ogm.model.key.spi.EntityKey;
import org.hibernate.ogm.model.key.spi.EntityKeyMetadata;
import org.hibernate.ogm.model.key.spi.RowKey;
import org.hibernate.ogm.model.spi.Association;
import org.hibernate.ogm.model.spi.AssociationOperation;
import org.hibernate.ogm.model.spi.EntityMetadataInformation;
import org.hibernate.ogm.model.spi.Tuple;
import org.hibernate.ogm.model.spi.Tuple.SnapshotType;
import org.hibernate.ogm.model.spi.TupleOperation;
import org.hibernate.ogm.persister.impl.OgmCollectionPersister;
import org.hibernate.ogm.persister.impl.OgmEntityPersister;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.neo4j.graphdb.ConstraintViolationException;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.QueryExecutionException;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Result;
import org.neo4j.kernel.api.exceptions.schema.UniquePropertyConstraintViolationKernelException;

/**
 * Abstracts Hibernate OGM from Neo4j.
 * 

* A {@link Tuple} is saved as a {@link Node} where the columns are converted into properties of the node.
* An {@link Association} is converted into a {@link Relationship} identified by the {@link AssociationKey} and the * {@link RowKey}. The type of the relationship is the value returned by * {@link AssociationKeyMetadata#getCollectionRole()}. *

* If the value of a property is set to null the property will be removed (Neo4j does not allow to store null values). * * @author Davide D'Alto <[email protected]> */ public class EmbeddedNeo4jDialect extends BaseNeo4jDialect { private static final Log log = LoggerFactory.getLogger(); private final GraphDatabaseService dataBase; private final EmbeddedNeo4jSequenceGenerator sequenceGenerator; private Map entityQueries; private Map associationQueries; public EmbeddedNeo4jDialect(EmbeddedNeo4jDatastoreProvider provider) { super( EmbeddedNeo4jTypeConverter.INSTANCE ); this.dataBase = provider.getDatabase(); this.sequenceGenerator = provider.getSequenceGenerator(); } @Override public void sessionFactoryCreated(SessionFactoryImplementor sessionFactoryImplementor) { this.associationQueries = Collections.unmodifiableMap( initializeAssociationQueries( sessionFactoryImplementor ) ); this.entityQueries = Collections.unmodifiableMap( initializeEntityQueries( sessionFactoryImplementor, associationQueries ) ); } private Map initializeEntityQueries(SessionFactoryImplementor sessionFactoryImplementor, Map associationQueries) { Map entityQueries = initializeEntityQueries( sessionFactoryImplementor ); for ( AssociationKeyMetadata associationKeyMetadata : associationQueries.keySet() ) { EntityKeyMetadata entityKeyMetadata = associationKeyMetadata.getAssociatedEntityKeyMetadata().getEntityKeyMetadata(); if ( !entityQueries.containsKey( entityKeyMetadata ) ) { // Embeddables metadata entityQueries.put( entityKeyMetadata, new EmbeddedNeo4jEntityQueries( entityKeyMetadata ) ); } } return entityQueries; } private Map initializeEntityQueries(SessionFactoryImplementor sessionFactoryImplementor) { Map queryMap = new HashMap(); Collection entityPersisters = sessionFactoryImplementor.getEntityPersisters().values(); for ( EntityPersister entityPersister : entityPersisters ) { if ( entityPersister instanceof OgmEntityPersister ) { OgmEntityPersister ogmEntityPersister = (OgmEntityPersister) entityPersister; queryMap.put( ogmEntityPersister.getEntityKeyMetadata(), new EmbeddedNeo4jEntityQueries( ogmEntityPersister.getEntityKeyMetadata() ) ); } } return queryMap; } private Map initializeAssociationQueries(SessionFactoryImplementor sessionFactoryImplementor) { Map queryMap = new HashMap(); Collection collectionPersisters = sessionFactoryImplementor.getCollectionPersisters().values(); for ( CollectionPersister collectionPersister : collectionPersisters ) { if ( collectionPersister instanceof OgmCollectionPersister ) { OgmCollectionPersister ogmCollectionPersister = (OgmCollectionPersister) collectionPersister; EntityKeyMetadata ownerEntityKeyMetadata = ( (OgmEntityPersister) ( ogmCollectionPersister.getOwnerEntityPersister() ) ).getEntityKeyMetadata(); AssociationKeyMetadata associationKeyMetadata = ogmCollectionPersister.getAssociationKeyMetadata(); queryMap.put( associationKeyMetadata, new EmbeddedNeo4jAssociationQueries( ownerEntityKeyMetadata, associationKeyMetadata ) ); } } return queryMap; } @Override public Tuple getTuple(EntityKey key, OperationContext context) { Node entityNode = entityQueries.get( key.getMetadata() ).findEntity( dataBase, key.getColumnValues() ); if ( entityNode == null ) { return null; } return new Tuple( EmbeddedNeo4jTupleSnapshot.fromNode( entityNode, context.getTupleTypeContext().getAllAssociatedEntityKeyMetadata(), context.getTupleTypeContext().getAllRoles(), key.getMetadata() ), SnapshotType.UPDATE ); } @Override public List getTuples(EntityKey[] keys, TupleContext tupleContext) { if ( keys.length == 0 ) { return Collections.emptyList(); } // We only supports one metadata for now EntityKeyMetadata metadata = keys[0].getMetadata(); // The result returned by the query might not be in the same order as the keys. ResourceIterator nodes = entityQueries.get( metadata ).findEntities( dataBase, keys ); try { return tuplesResult( keys, tupleContext, nodes ); } finally { nodes.close(); } } /* * This method assumes that the nodes might not be in the same order as the keys and some keys might not have a * matching result in the db. */ private List tuplesResult(EntityKey[] keys, TupleContext tupleContext, ResourceIterator nodes) { // The list is initialized with null because some keys might not have a corresponding node Tuple[] tuples = new Tuple[keys.length]; while ( nodes.hasNext() ) { Node node = nodes.next(); for ( int i = 0; i < keys.length; i++ ) { if ( matches( node, keys[i].getColumnNames(), keys[i].getColumnValues() ) ) { tuples[i] = new Tuple( EmbeddedNeo4jTupleSnapshot.fromNode( node, tupleContext.getTupleTypeContext().getAllAssociatedEntityKeyMetadata(), tupleContext.getTupleTypeContext().getAllRoles(), keys[i].getMetadata() ), SnapshotType.UPDATE ); // We assume there are no duplicated keys break; } } } return Arrays.asList( tuples ); } private boolean matches(Node node, String[] properties, Object[] values) { for ( int i = 0; i < properties.length; i++ ) { if ( node.hasProperty( properties[i] ) && !node.getProperty( properties[i] ).equals( values[i] ) ) { return false; } else if ( !node.hasProperty( properties[i] ) && values[i] != null ) { return false; } } return true; } @Override public Tuple createTuple(EntityKey key, OperationContext tupleContext) { return new Tuple( EmbeddedNeo4jTupleSnapshot.emptySnapshot( key.getMetadata() ), SnapshotType.INSERT ); } @Override public void insertOrUpdateTuple(EntityKey key, TuplePointer tuplePointer, TupleContext tupleContext) { Tuple tuple = tuplePointer.getTuple(); EmbeddedNeo4jTupleSnapshot snapshot = (EmbeddedNeo4jTupleSnapshot) tuple.getSnapshot(); // insert if ( snapshot.isNew() ) { Node node = insertTuple( key, tuple ); snapshot.setNode( node ); applyTupleOperations( key, tuple, node, tuple.getOperations(), tupleContext ); GraphLogger.log( "Inserted node: %1$s", node ); } // update else { Node node = snapshot.getNode(); applyTupleOperations( key, tuple, node, tuple.getOperations(), tupleContext ); GraphLogger.log( "Updated node: %1$s", node ); } } private Node insertTuple(EntityKey key, Tuple tuple) { try { return entityQueries.get( key.getMetadata() ).insertEntity( dataBase, key.getColumnValues() ); } catch (QueryExecutionException qee) { if ( CONSTRAINT_VIOLATION_CODE.equals( qee.getStatusCode() ) ) { Throwable cause = findRecognizableCause( qee ); if ( cause instanceof UniquePropertyConstraintViolationKernelException ) { throw new TupleAlreadyExistsException( key, qee ); } } throw qee; } } private Throwable findRecognizableCause(QueryExecutionException qee) { Throwable cause = qee.getCause(); while ( cause.getCause() != null ) { cause = cause.getCause(); } return cause; } @Override public void removeTuple(EntityKey key, TupleContext tupleContext) { entityQueries.get( key.getMetadata() ).removeEntity( dataBase, key.getColumnValues() ); } /** * When dealing with some scenarios like, for example, a bidirectional association, OGM calls this method twice: *

* the first time with the information related to the owner of the association and the {@link RowKey}, * the second time using the same {@link RowKey} but with the {@link AssociationKey} referring to the other side of the association. * @param associatedEntityKeyMetadata */ private Relationship createRelationship(AssociationKey associationKey, Tuple associationRow, AssociatedEntityKeyMetadata associatedEntityKeyMetadata) { switch ( associationKey.getMetadata().getAssociationKind() ) { case EMBEDDED_COLLECTION: return createRelationshipWithEmbeddedNode( associationKey, associationRow, associatedEntityKeyMetadata ); case ASSOCIATION: return findOrCreateRelationshipWithEntityNode( associationKey, associationRow, associatedEntityKeyMetadata ); default: throw new AssertionFailure( "Unrecognized associationKind: " + associationKey.getMetadata().getAssociationKind() ); } } private Relationship createRelationshipWithEmbeddedNode(AssociationKey associationKey, Tuple associationRow, AssociatedEntityKeyMetadata associatedEntityKeyMetadata) { EntityKey embeddedKey = getEntityKey( associationRow, associatedEntityKeyMetadata ); Relationship relationship = associationQueries.get( associationKey.getMetadata() ) .createRelationshipForEmbeddedAssociation( dataBase, associationKey, embeddedKey ); applyProperties( associationKey, associationRow, relationship ); return relationship; } private Relationship findOrCreateRelationshipWithEntityNode(AssociationKey associationKey, Tuple associationRow, AssociatedEntityKeyMetadata associatedEntityKeyMetadata) { EntityKey targetEntityKey = getEntityKey( associationRow, associatedEntityKeyMetadata ); Node targetNode = entityQueries.get( targetEntityKey.getMetadata() ).findEntity( dataBase, targetEntityKey.getColumnValues() ); return createRelationshipWithTargetNode( associationKey, associationRow, targetNode ); } /** * The only properties added to a relationship are the columns representing the index of the association. */ private void applyProperties(AssociationKey associationKey, Tuple associationRow, Relationship relationship) { String[] indexColumns = associationKey.getMetadata().getRowKeyIndexColumnNames(); for ( int i = 0; i < indexColumns.length; i++ ) { String propertyName = indexColumns[i]; Object propertyValue = associationRow.get( propertyName ); relationship.setProperty( propertyName, propertyValue ); } } private Relationship createRelationshipWithTargetNode(AssociationKey associationKey, Tuple associationRow, Node targetNode) { EntityKey entityKey = associationKey.getEntityKey(); Node ownerNode = entityQueries.get( entityKey.getMetadata() ).findEntity( dataBase, entityKey.getColumnValues() ); Relationship relationship = ownerNode.createRelationshipTo( targetNode, withName( associationKey.getMetadata().getCollectionRole() ) ); applyProperties( associationKey, associationRow, relationship ); return relationship; } @Override public Association getAssociation(AssociationKey associationKey, AssociationContext associationContext) { EntityKey entityKey = associationKey.getEntityKey(); Node entityNode = entityQueries.get( entityKey.getMetadata() ).findEntity( dataBase, entityKey.getColumnValues() ); GraphLogger.log( "Found owner node: %1$s", entityNode ); if ( entityNode == null ) { return null; } Map tuples = createAssociationMap( associationKey, associationContext, entityKey ); return new Association( new EmbeddedNeo4jAssociationSnapshot( tuples ) ); } private Map createAssociationMap(AssociationKey associationKey, AssociationContext associationContext, EntityKey entityKey) { String relationshipType = associationContext.getAssociationTypeContext().getRoleOnMainSide(); ResourceIterator relationships = entityQueries.get( entityKey.getMetadata() ) .findAssociation( dataBase, entityKey.getColumnValues(), relationshipType ); Map tuples = new HashMap(); try { while ( relationships.hasNext() ) { Relationship relationship = relationships.next(); AssociatedEntityKeyMetadata associatedEntityKeyMetadata = associationContext.getAssociationTypeContext().getAssociatedEntityKeyMetadata(); EmbeddedNeo4jTupleAssociationSnapshot snapshot = new EmbeddedNeo4jTupleAssociationSnapshot( relationship, associationKey, associatedEntityKeyMetadata ); RowKey rowKey = convert( associationKey, snapshot ); tuples.put( rowKey, new Tuple( snapshot, SnapshotType.UPDATE ) ); } return tuples; } finally { relationships.close(); } } @Override public void insertOrUpdateAssociation(AssociationKey key, Association association, AssociationContext associationContext) { // If this is the inverse side of a bi-directional association, we don't create a relationship for this; this // will happen when updating the main side if ( key.getMetadata().isInverse() ) { return; } for ( AssociationOperation action : association.getOperations() ) { applyAssociationOperation( association, key, action, associationContext ); } } @Override public void removeAssociation(AssociationKey key, AssociationContext associationContext) { // If this is the inverse side of a bi-directional association, we don't manage the relationship from this side if ( key.getMetadata().isInverse() ) { return; } associationQueries.get( key.getMetadata() ).removeAssociation( dataBase, key ); } private void applyAssociationOperation(Association association, AssociationKey key, AssociationOperation operation, AssociationContext associationContext) { switch ( operation.getType() ) { case CLEAR: removeAssociation( key, associationContext ); break; case PUT: putAssociationOperation( association, key, operation, associationContext.getAssociationTypeContext().getAssociatedEntityKeyMetadata() ); break; case REMOVE: removeAssociationOperation( association, key, operation, associationContext.getAssociationTypeContext().getAssociatedEntityKeyMetadata() ); break; } } private void putAssociationOperation(Association association, AssociationKey associationKey, AssociationOperation action, AssociatedEntityKeyMetadata associatedEntityKeyMetadata) { Relationship relationship = associationQueries.get( associationKey.getMetadata() ).findRelationship( dataBase, associationKey, action.getKey() ); if ( relationship != null ) { for ( String relationshipProperty : associationKey.getMetadata().getRowKeyIndexColumnNames() ) { relationship.setProperty( relationshipProperty, action.getValue().get( relationshipProperty ) ); } for ( String column : associationKey.getMetadata().getColumnsWithoutKeyColumns( action.getValue().getColumnNames() ) ) { if ( !isRowKeyColumn( associationKey.getMetadata(), column ) ) { relationship.getEndNode().setProperty( column, action.getValue().get( column ) ); } } GraphLogger.log( "Updated relationship: %1$s", relationship ); } else { relationship = createRelationship( associationKey, action.getValue(), associatedEntityKeyMetadata ); GraphLogger.log( "Created relationship: %1$s", relationship ); } } // TODO replace with method provided by OGM-1035 private boolean isRowKeyColumn(AssociationKeyMetadata metadata, String column) { for ( String rowKeyColumn : metadata.getRowKeyColumnNames() ) { if ( rowKeyColumn.equals( column ) ) { return true; } } return false; } private void removeAssociationOperation(Association association, AssociationKey associationKey, AssociationOperation action, AssociatedEntityKeyMetadata associatedEntityKeyMetadata) { associationQueries.get( associationKey.getMetadata() ).removeAssociationRow( dataBase, associationKey, action.getKey() ); } private void applyTupleOperations(EntityKey entityKey, Tuple tuple, Node node, Set operations, TupleContext tupleContext) { Set processedAssociationRoles = new HashSet(); for ( TupleOperation operation : operations ) { applyOperation( entityKey, tuple, node, operation, tupleContext, processedAssociationRoles ); } } private void applyOperation(EntityKey entityKey, Tuple tuple, Node node, TupleOperation operation, TupleContext tupleContext, Set processedAssociationRoles) { switch ( operation.getType() ) { case PUT: putTupleOperation( entityKey, tuple, node, operation, tupleContext, processedAssociationRoles ); break; case PUT_NULL: case REMOVE: removeTupleOperation( entityKey, node, operation, tupleContext, processedAssociationRoles ); break; } } private void removeTupleOperation(EntityKey entityKey, Node node, TupleOperation operation, TupleContext tupleContext, Set processedAssociationRoles) { if ( !tupleContext.getTupleTypeContext().isPartOfAssociation( operation.getColumn() ) ) { if ( isPartOfRegularEmbedded( entityKey.getColumnNames(), operation.getColumn() ) ) { // Embedded node String[] split = split( operation.getColumn() ); removePropertyForEmbedded( node, split, 0 ); } else if ( node.hasProperty( operation.getColumn() ) ) { node.removeProperty( operation.getColumn() ); } } // if the column represents a to-one association, remove the relationship else { String associationRole = tupleContext.getTupleTypeContext().getRole( operation.getColumn() ); if ( !processedAssociationRoles.contains( associationRole ) ) { Iterator relationships = node.getRelationships( withName( associationRole ) ).iterator(); if ( relationships.hasNext() ) { relationships.next().delete(); } } } } /* * It will remove a property from an embedded node if it exists. * After deleting the property, if the node does not have any more properties and relationships (except for an incoming one), * it will delete the embedded node as well. */ private void removePropertyForEmbedded(Node embeddedNode, String[] embeddedColumnSplit, int i) { if ( i == embeddedColumnSplit.length - 1 ) { // Property String property = embeddedColumnSplit[embeddedColumnSplit.length - 1]; if ( embeddedNode.hasProperty( property ) ) { embeddedNode.removeProperty( property ); } } else { Iterator iterator = embeddedNode.getRelationships( Direction.OUTGOING, withName( embeddedColumnSplit[i] ) ).iterator(); if ( iterator.hasNext() ) { removePropertyForEmbedded( iterator.next().getEndNode(), embeddedColumnSplit, i + 1 ); } } if ( !embeddedNode.getPropertyKeys().iterator().hasNext() ) { // Node without properties Iterator iterator = embeddedNode.getRelationships().iterator(); if ( iterator.hasNext() ) { Relationship relationship = iterator.next(); if ( !iterator.hasNext() ) { // Node with only one relationship and no properties, // we can remove it: // It means we have removed all the properties from the embedded node // and it is NOT an intermediate node like // (entity) --> (embedded1) --> (embedded2) relationship.delete(); embeddedNode.delete(); } } } } private void putTupleOperation(EntityKey entityKey, Tuple tuple, Node node, TupleOperation operation, TupleContext tupleContext, Set processedAssociationRoles) { if ( tupleContext.getTupleTypeContext().isPartOfAssociation( operation.getColumn() ) ) { // the column represents a to-one association, map it as relationship putOneToOneAssociation( tuple, node, operation, tupleContext, processedAssociationRoles ); } else if ( isPartOfRegularEmbedded( entityKey.getMetadata().getColumnNames(), operation.getColumn() ) ) { entityQueries.get( entityKey.getMetadata() ).updateEmbeddedColumn( dataBase, entityKey.getColumnValues(), operation.getColumn(), operation.getValue() ); } else { putProperty( entityKey, node, operation ); } } private void putProperty(EntityKey entityKey, Node node, TupleOperation operation) { try { node.setProperty( operation.getColumn(), operation.getValue() ); } catch (ConstraintViolationException e) { String message = e.getMessage(); if ( message.contains( "already exists" ) ) { throw log.mustNotInsertSameEntityTwice( String.valueOf( operation ), e ); } else { throw log.constraintViolation( entityKey, String.valueOf( operation ), e ); } } } private void putOneToOneAssociation(Tuple tuple, Node node, TupleOperation operation, TupleContext tupleContext, Set processedAssociationRoles) { String associationRole = tupleContext.getTupleTypeContext().getRole( operation.getColumn() ); if ( !processedAssociationRoles.contains( associationRole ) ) { processedAssociationRoles.add( associationRole ); EntityKey targetKey = getEntityKey( tuple, tupleContext.getTupleTypeContext().getAssociatedEntityKeyMetadata( operation.getColumn() ) ); // delete the previous relationship if there is one; for a to-one association, the relationship won't have any // properties, so the type is uniquely identifying it Iterator relationships = node.getRelationships( withName( associationRole ) ).iterator(); if ( relationships.hasNext() ) { relationships.next().delete(); } // create a new relationship Node targetNode = entityQueries.get( targetKey.getMetadata() ).findEntity( dataBase, targetKey.getColumnValues() ); node.createRelationshipTo( targetNode, withName( associationRole ) ); } } @Override public void forEachTuple(ModelConsumer consumer, TupleTypeContext tupleTypeContext, EntityKeyMetadata entityKeyMetadata) { ResourceIterator queryNodes = entityQueries.get( entityKeyMetadata ).findEntities( dataBase ); consumer.consume( new EmbeddedNeo4jTuplesSupplier( queryNodes, tupleTypeContext, entityKeyMetadata ) ); } private static class EmbeddedNeo4jTuplesSupplier implements TuplesSupplier { private final ResourceIterator nodes; private final TupleTypeContext tupleTypeContext; private final EntityKeyMetadata entityKeyMetadata; public EmbeddedNeo4jTuplesSupplier(ResourceIterator nodes, TupleTypeContext tupleTypeContext, EntityKeyMetadata entityKeyMetadata) { this.nodes = nodes; this.tupleTypeContext = tupleTypeContext; this.entityKeyMetadata = entityKeyMetadata; } @Override public ClosableIterator get(TransactionContext transactionContext) { return new EmbeddedNeo4jNodesTupleIterator( nodes, entityKeyMetadata, tupleTypeContext ); } } @Override public Number nextValue(NextValueRequest request) { return sequenceGenerator.nextValue( request ); } @Override public ClosableIterator executeBackendQuery(BackendQuery backendQuery, QueryParameters queryParameters, TupleContext tupleContext) { Map parameters = getParameters( queryParameters ); String nativeQuery = buildNativeQuery( backendQuery, queryParameters ); try { Result result = dataBase.execute( nativeQuery, parameters ); EntityMetadataInformation entityMetadataInformation = backendQuery.getSingleEntityMetadataInformationOrNull(); return new EmbeddedNeo4jBackendQueryResultIterator( result, entityMetadataInformation, tupleContext ); } catch (QueryExecutionException qe) { throw log.nativeQueryException( qe.getStatusCode(), qe.getMessage(), qe ); } } @Override public int executeBackendUpdateQuery(BackendQuery backendQuery, QueryParameters queryParameters, TupleContext tupleContext) { Map parameters = getParameters( queryParameters ); String nativeQuery = buildNativeQuery( backendQuery, queryParameters ); try { Result result = dataBase.execute( nativeQuery, parameters ); return summaryUpdates( result ); } catch (QueryExecutionException qe) { throw log.nativeQueryException( qe.getStatusCode(), qe.getMessage(), qe ); } } private int summaryUpdates(Result result) { int updates = 0; updates += result.getQueryStatistics().getConstraintsAdded(); updates += result.getQueryStatistics().getConstraintsRemoved(); updates += result.getQueryStatistics().getNodesCreated(); updates += result.getQueryStatistics().getNodesDeleted(); updates += result.getQueryStatistics().getRelationshipsCreated(); updates += result.getQueryStatistics().getRelationshipsDeleted(); updates += result.getQueryStatistics().getLabelsAdded(); updates += result.getQueryStatistics().getLabelsRemoved(); updates += result.getQueryStatistics().getPropertiesSet(); return updates; } @Override public String parseNativeQuery(String nativeQuery) { // We return given Cypher queries as they are; Currently there is no API for validating Cypher queries without // actually executing them (see https://github.com/neo4j/neo4j/issues/2766) return nativeQuery; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy