/*
* 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.split;
import static org.neo4j.graphdb.DynamicRelationshipType.withName;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.hibernate.ogm.datastore.neo4j.EmbeddedNeo4jDialect;
import org.hibernate.ogm.model.key.spi.AssociatedEntityKeyMetadata;
import org.hibernate.ogm.model.key.spi.EntityKeyMetadata;
import org.hibernate.ogm.model.spi.TupleSnapshot;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
/**
* Represents the Tuple snapshot as loaded by the Neo4j datastore.
* <p>
* The columns of the tuple are mapped as properties of the node.
*
* @author Davide D'Alto <davide@hibernate.org>
*/
public final class EmbeddedNeo4jTupleSnapshot implements TupleSnapshot {
private Node node;
private final EntityKeyMetadata entityKeyMetadata;
private final Map<String, AssociatedEntityKeyMetadata> associatedEntityKeyMetadata;
private final Map<String, String> rolesByColumn;
private EmbeddedNeo4jTupleSnapshot(Node node, Map<String, AssociatedEntityKeyMetadata> associatedEntityKeyMetadata, Map<String, String> rolesByColumn, EntityKeyMetadata entityKeyMetadata) {
this.node = node;
this.associatedEntityKeyMetadata = associatedEntityKeyMetadata;
this.rolesByColumn = rolesByColumn;
this.entityKeyMetadata = entityKeyMetadata;
}
public static EmbeddedNeo4jTupleSnapshot emptySnapshot(EntityKeyMetadata entityKeyMetadata) {
return new EmbeddedNeo4jTupleSnapshot( null, Collections.<String, AssociatedEntityKeyMetadata>emptyMap(), Collections.<String, String>emptyMap(),
entityKeyMetadata );
}
public static EmbeddedNeo4jTupleSnapshot fromNode(Node node, Map<String, AssociatedEntityKeyMetadata> associatedEntityKeyMetadata, Map<String, String> rolesByColumn, EntityKeyMetadata entityKeyMetadata) {
return new EmbeddedNeo4jTupleSnapshot( node, associatedEntityKeyMetadata, rolesByColumn, entityKeyMetadata );
}
@Override
public Object get(String column) {
if ( isNew() ) {
return null;
}
else if ( associatedEntityKeyMetadata.containsKey( column ) ) {
return readPropertyOnOtherNode( column );
}
else if ( EmbeddedNeo4jDialect.isPartOfRegularEmbedded( entityKeyMetadata.getColumnNames(), column ) ) {
return readEmbeddedProperty( column );
}
else {
return readProperty( node, column );
}
}
private Object readPropertyOnOtherNode(String column) {
Iterator<Relationship> relationships = node.getRelationships( Direction.OUTGOING, withName( rolesByColumn.get( column ) ) ).iterator();
if ( relationships.hasNext() ) {
Node otherNode = relationships.next().getEndNode();
return readProperty( otherNode, associatedEntityKeyMetadata.get( column ).getCorrespondingEntityKeyColumn( column ) );
}
return null;
}
// TODO: We should create a query to read this value
private Object readEmbeddedProperty(String column) {
String[] split = split( column );
Node embeddedNode = node;
for ( int i = 0; i < split.length - 1; i++ ) {
String relType = split[i];
Iterator<Relationship> rels = embeddedNode.getRelationships( Direction.OUTGOING, withName( relType ) ).iterator();
if ( rels.hasNext() ) {
embeddedNode = rels.next().getEndNode();
}
else {
return null;
}
}
return readProperty( embeddedNode, split[split.length - 1] );
}
private Object readProperty(Node otherNode, String targetColumnName) {
Object value = null;
if ( otherNode.hasProperty( targetColumnName ) ) {
value = otherNode.getProperty( targetColumnName );
}
return value;
}
@Override
public boolean isEmpty() {
return isNew() ? true : !node.getPropertyKeys().iterator().hasNext();
}
@Override
public Set<String> getColumnNames() {
if ( isNew() ) {
return Collections.emptySet();
}
Set<String> names = new HashSet<String>();
for ( String string : node.getPropertyKeys() ) {
names.add( string );
}
return names;
}
public Node getNode() {
return node;
}
public void setNode(Node node) {
this.node = node;
}
/**
* Whether this snapshot has been newly created (meaning it doesn't have an actual {@link Node} yet) or not. A node
* will be in the "new" state between the {@code createTuple()} call and the next {@code insertOrUpdateTuple()}
* call.
*/
public boolean isNew() {
return node == null;
}
}