/*
* 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.embedded.dialect.impl;
import static org.hibernate.ogm.util.impl.EmbeddedHelper.isPartOfEmbedded;
import static org.hibernate.ogm.util.impl.EmbeddedHelper.split;
import static org.neo4j.graphdb.DynamicLabel.label;
import static org.neo4j.graphdb.DynamicRelationshipType.withName;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.hibernate.ogm.model.key.spi.AssociatedEntityKeyMetadata;
import org.hibernate.ogm.model.key.spi.AssociationKey;
import org.hibernate.ogm.model.key.spi.AssociationKind;
import org.hibernate.ogm.model.spi.TupleSnapshot;
import org.hibernate.ogm.util.impl.EmbeddedHelper;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
/**
* @author Davide D'Alto
*/
public class EmbeddedNeo4jTupleAssociationSnapshot implements TupleSnapshot {
private final Map<String, Object> properties;
public EmbeddedNeo4jTupleAssociationSnapshot(Relationship relationship, AssociationKey associationKey, AssociatedEntityKeyMetadata associatedEntityKeyMetadata) {
properties = collectProperties( relationship, associationKey, associatedEntityKeyMetadata );
}
private static Map<String, Object> collectProperties(Relationship relationship, AssociationKey associationKey, AssociatedEntityKeyMetadata associatedEntityKeyMetadata) {
Map<String, Object> properties = new HashMap<String, Object>();
String[] rowKeyColumnNames = associationKey.getMetadata().getRowKeyColumnNames();
Node ownerNode = findOwnerNode( relationship, associationKey );
Node targetNode = findTargetNode( relationship, associationKey, ownerNode );
// Index columns
for ( int i = 0; i < rowKeyColumnNames.length; i++ ) {
if ( relationship.hasProperty( rowKeyColumnNames[i] ) ) {
properties.put( rowKeyColumnNames[i], relationship.getProperty( rowKeyColumnNames[i] ) );
}
}
// Properties stored in the target side of the association
for ( String associationColumn : associatedEntityKeyMetadata.getAssociationKeyColumns() ) {
String targetColumnName = associatedEntityKeyMetadata.getCorrespondingEntityKeyColumn( associationColumn );
if ( isPartOfEmbedded( targetColumnName ) ) {
// Embedded column
String collectionRole = associationKey.getMetadata().getCollectionRole();
if ( targetColumnName.equals( collectionRole ) ) {
// Ex: @ElementCollection List<String> examples
targetColumnName = targetColumnName.substring( targetColumnName.lastIndexOf( "." ) + 1 );
if ( targetNode.hasProperty( targetColumnName ) ) {
properties.put( associationColumn, targetNode.getProperty( targetColumnName ) );
}
}
else if ( targetNode.hasProperty( targetColumnName ) ) {
// Embedded id
properties.put( associationColumn, targetNode.getProperty( targetColumnName ) );
}
else {
// Ex: @ElementCollection List<Embedded> examples
Node embeddedNode = targetNode;
if ( targetColumnName.startsWith( collectionRole ) ) {
targetColumnName = targetColumnName.substring( collectionRole.length() + 1 );
}
String[] split = split( targetColumnName );
boolean found = true;
for ( int i = 0; i < split.length - 1; i++ ) {
Iterator<Relationship> iterator = embeddedNode.getRelationships( Direction.OUTGOING, withName( split[i] ) ).iterator();
if ( iterator.hasNext() ) {
embeddedNode = iterator.next().getEndNode();
}
else {
found = false;
break;
}
}
if ( found ) {
targetColumnName = targetColumnName.substring( targetColumnName.lastIndexOf( "." ) + 1 );
if ( embeddedNode.hasProperty( targetColumnName ) ) {
properties.put( associationColumn, embeddedNode.getProperty( targetColumnName ) );
}
}
}
}
else {
if ( targetNode.hasProperty( targetColumnName ) ) {
properties.put( associationColumn, targetNode.getProperty( targetColumnName ) );
}
}
}
// Property stored in the owner side of the association
for ( int i = 0; i < associationKey.getColumnNames().length; i++ ) {
if ( ownerNode.hasProperty( associationKey.getEntityKey().getColumnNames()[i] ) ) {
properties.put( associationKey.getColumnNames()[i], ownerNode.getProperty( associationKey.getEntityKey().getColumnNames()[i] ) );
}
}
return properties;
}
private static Node findTargetNode(Relationship relationship, AssociationKey associationKey, Node ownerNode) {
if ( isEmbeddedCollection( associationKey ) ) {
return relationship.getEndNode();
}
else {
return relationship.getOtherNode( ownerNode );
}
}
private static Node findOwnerNode(Relationship relationship, AssociationKey associationKey) {
if ( isEmbeddedCollection( associationKey ) ) {
String collectionRole = associationKey.getMetadata().getCollectionRole();
return embeddedAssociationOwner( relationship, collectionRole );
}
else {
return ownerNodeFromAssociation( associationKey, relationship );
}
}
private static boolean isEmbeddedCollection(AssociationKey associationKey) {
return associationKey.getMetadata().getAssociationKind() == AssociationKind.EMBEDDED_COLLECTION;
}
private static Node embeddedAssociationOwner(Relationship relationship, String collectionRole) {
if ( isPartOfEmbedded( collectionRole ) ) {
String[] split = EmbeddedHelper.split( collectionRole );
Node ownerNode = relationship.getStartNode();
for ( int i = 1; i < split.length; i++ ) {
String type = split[split.length - i - 1];
Relationship next = ownerNode.getRelationships( Direction.INCOMING, withName( type ) ).iterator().next();
ownerNode = next.getStartNode();
}
return ownerNode;
}
else {
return relationship.getStartNode();
}
}
private static Node ownerNodeFromAssociation(AssociationKey associationKey, Relationship relationship) {
if ( relationship.getStartNode().hasLabel( label( associationKey.getEntityKey().getTable() ) ) ) {
return relationship.getStartNode();
}
else {
return relationship.getEndNode();
}
}
@Override
public Object get(String column) {
return properties.get( column );
}
@Override
public boolean isEmpty() {
return properties.isEmpty();
}
@Override
public Set<String> getColumnNames() {
return properties.keySet();
}
}