Package org.hibernate.ogm.datastore.neo4j.dialect.impl

Source Code of org.hibernate.ogm.datastore.neo4j.dialect.impl.Neo4jAssociationQueries

/*
* 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.dialect.impl;

import static org.hibernate.ogm.datastore.neo4j.dialect.impl.NodeLabel.EMBEDDED;
import static org.hibernate.ogm.datastore.neo4j.dialect.impl.NodeLabel.ENTITY;
import static org.hibernate.ogm.datastore.neo4j.query.parsing.cypherdsl.impl.CypherDSL.escapeIdentifier;

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.AssociationKind;
import org.hibernate.ogm.util.impl.ArrayHelper;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.Relationship;

/**
* Container for the queries related to one association family in Neo4j. Unfortunately, we cannot use the same queries
* for all associations, as Neo4j does not allow to parameterize on node labels which would be required, as the
* association table is stored as a label.
*
* @author Davide D'Alto
*/
public class Neo4jAssociationQueries extends QueriesBase {

  private final String findRelationshipQuery;
  private final String removeAssociationQuery;
  private final String removeAssociationRowQuery;

  public Neo4jAssociationQueries(EntityKeyMetadata ownerEntityKeyMetadata, AssociationKeyMetadata associationKeyMetadata) {
    this.removeAssociationQuery = initRemoveAssociationQuery( ownerEntityKeyMetadata, associationKeyMetadata );
    this.removeAssociationRowQuery = initRemoveAssociationRowQuery( ownerEntityKeyMetadata, associationKeyMetadata );
    this.findRelationshipQuery = initFindRelationshipQuery( ownerEntityKeyMetadata, associationKeyMetadata );
  }

  /*
   * Example with target node:
   *
   * MATCH (n:ENTITY:table {id: {0}}) -[r:role] -  (t {id: {1}})
   * RETURN r
   *
   * Example with relationship indexes:
   *
   * MATCH (n:ENTITY:table {id: {0}}) -[r:role {index: {1}}] -  (t)
   * RETURN r
   */
  private static String initFindRelationshipQuery(EntityKeyMetadata ownerEntityKeyMetadata, AssociationKeyMetadata associationKeyMetadata) {
    int offset = 0;
    StringBuilder queryBuilder = new StringBuilder("MATCH ");
    queryBuilder.append( "(n:" );
    queryBuilder.append( ENTITY );
    queryBuilder.append( ":" );
    appendLabel( ownerEntityKeyMetadata, queryBuilder );
    appendProperties( ownerEntityKeyMetadata, queryBuilder );
    queryBuilder.append( ") - " );
    queryBuilder.append( "[r" );
    queryBuilder.append( ":" );
    appendRelationshipType( queryBuilder, associationKeyMetadata );
    offset = ownerEntityKeyMetadata.getColumnNames().length;
    if ( associationKeyMetadata.getRowKeyIndexColumnNames().length > 0 ) {
      appendProperties( queryBuilder, associationKeyMetadata.getRowKeyIndexColumnNames(), offset );
      queryBuilder.append( "] - (t" );
    }
    else {
      queryBuilder.append( "] - (t" );
      appendProperties( queryBuilder, associationKeyMetadata.getAssociatedEntityKeyMetadata().getEntityKeyMetadata().getColumnNames(), offset );
    }
    queryBuilder.append( ")" );
    queryBuilder.append( " RETURN r" );
    return queryBuilder.toString();
  }

  /*
   * Example with association:
   *
   * MATCH (n:ENTITY:table {id: {0}}) -[r:role] - ()
   * DELETE r
   *
   * Example with embedded:
   *
   * MATCH (n:ENTITY:table {id: {0}}) -[r:role] - (e:EMBEDDED)
   * DELETE r, e
   */
  private static String initRemoveAssociationQuery(EntityKeyMetadata ownerEntityKeyMetadata, AssociationKeyMetadata associationKeyMetadata) {
    StringBuilder queryBuilder = new StringBuilder("MATCH ");
    queryBuilder.append( "(n:" );
    queryBuilder.append( ENTITY );
    queryBuilder.append( ":" );
    appendLabel( ownerEntityKeyMetadata, queryBuilder );
    appendProperties( ownerEntityKeyMetadata, queryBuilder );
    queryBuilder.append( ") - " );
    queryBuilder.append( "[r" );
    queryBuilder.append( ":" );
    appendRelationshipType( queryBuilder, associationKeyMetadata );
    queryBuilder.append( "]" );
    if ( associationKeyMetadata.getAssociationKind() == AssociationKind.EMBEDDED_COLLECTION ) {
      queryBuilder.append( " - (e:" );
      queryBuilder.append( EMBEDDED );
      queryBuilder.append( ") DELETE r, e" );
    }
    else {
      queryBuilder.append( " - () DELETE r" );
    }
    return queryBuilder.toString();
  }

  /*
   * Example with association:
   *
   * MATCH (n:ENTITY:table {id: {0}}) -[r:role] - (e)
   * DELETE r
   *
   * Example with embedded collection:
   *
   * MATCH (n:ENTITY:table {id: {0}}) -[r:role] - (e:EMBEDDED)
   * DELETE r, e
   *
   * Example with indexes:
   *
   * MATCH (n:ENTITY:table {id: {0}}) -[r:role {index: {1}}] - (e)
   * DELETE r
   */
  private static String initRemoveAssociationRowQuery(EntityKeyMetadata ownerEntityKeyMetadata, AssociationKeyMetadata associationKeyMetadata) {
    StringBuilder queryBuilder = new StringBuilder("MATCH ");
    queryBuilder.append( "(n:" );
    queryBuilder.append( ENTITY );
    queryBuilder.append( ":" );
    appendLabel( ownerEntityKeyMetadata, queryBuilder );
    appendProperties( ownerEntityKeyMetadata, queryBuilder );
    queryBuilder.append( ") - " );
    queryBuilder.append( "[r" );
    queryBuilder.append( ":" );
    appendRelationshipType( queryBuilder, associationKeyMetadata );
    int offset = ownerEntityKeyMetadata.getColumnNames().length;
    boolean hasIndexColumns = associationKeyMetadata.getRowKeyIndexColumnNames().length > 0;
    if ( hasIndexColumns ) {
      appendProperties( queryBuilder, associationKeyMetadata.getRowKeyIndexColumnNames(), offset );
    }
    queryBuilder.append( "] - (e" );
    if ( associationKeyMetadata.getAssociationKind() == AssociationKind.EMBEDDED_COLLECTION ) {
      queryBuilder.append( ":" );
      queryBuilder.append( EMBEDDED );
    }
    if ( !hasIndexColumns ) {
      appendProperties( queryBuilder, associationKeyMetadata.getAssociatedEntityKeyMetadata().getEntityKeyMetadata().getColumnNames(), offset );
    }
    queryBuilder.append( ")" );
    queryBuilder.append( " DELETE r" );
    if ( associationKeyMetadata.getAssociationKind() == AssociationKind.EMBEDDED_COLLECTION ) {
      queryBuilder.append( ", e" );
    }
    return queryBuilder.toString();
  }

  /**
   * Removes the relationship(s) representing the given association. If the association refers to an embedded entity
   * (collection), the referenced entities are removed as well.
   *
   * @param executionEngine the {@link ExecutionEngine} used to run the query
   * @param associationKey represents the association
   */
  public void removeAssociation(ExecutionEngine executionEngine, AssociationKey associationKey) {
    executionEngine.execute( removeAssociationQuery, params( associationKey.getEntityKey().getColumnValues() ) );
  }

  /**
   * Returns the relationship corresponding to the {@link AssociationKey} and {@link RowKey}.
   *
   * @param executionEngine the {@link ExecutionEngine} used to run the query
   * @param associationKey represents the association
   * @param rowKey represents a row in an association
   * @return the corresponding relationship
   */
  public Relationship findRelationship(ExecutionEngine executionEngine, AssociationKey associationKey, RowKey rowKey) {
    Object[] relationshipValues;
    if ( associationKey.getMetadata().getRowKeyIndexColumnNames().length > 0 ) {
      int length = associationKey.getMetadata().getRowKeyIndexColumnNames().length;
      relationshipValues = new Object[length];
      String[] indexColumnNames = associationKey.getMetadata().getRowKeyIndexColumnNames();
      for ( int i = 0; i < indexColumnNames.length; i++ ) {
        for ( int j = 0; j < rowKey.getColumnNames().length; j++ ) {
          if ( indexColumnNames[i].equals( rowKey.getColumnNames()[j] ) ) {
            relationshipValues[i] = rowKey.getColumnValues()[j];
          }
        }
      }
    }
    else {
      relationshipValues = getEntityKey( associationKey, rowKey ).getColumnValues();
    }
    Object[] queryValues = ArrayHelper.concat( associationKey.getEntityKey().getColumnValues(), relationshipValues );
    ExecutionResult result = executionEngine.execute( findRelationshipQuery, params( queryValues ) );
    return singleResult( result );
  }

  /**
   * Remove an association row
   *
   * @param executionEngine the {@link ExecutionEngine} used to run the query
   * @param associationKey represents the association
   * @param rowKey represents a row in an association
   */
  public void removeAssociationRow(ExecutionEngine executionEngine, AssociationKey associationKey, RowKey rowKey) {
    Object[] relationshipValues;
    if ( associationKey.getMetadata().getRowKeyIndexColumnNames().length > 0 ) {
      int length = associationKey.getMetadata().getRowKeyIndexColumnNames().length;
      relationshipValues = new Object[length];
      String[] indexColumnNames = associationKey.getMetadata().getRowKeyIndexColumnNames();
      for ( int i = 0; i < indexColumnNames.length; i++ ) {
        for ( int j = 0; j < rowKey.getColumnNames().length; j++ ) {
          if ( indexColumnNames[i].equals( rowKey.getColumnNames()[j] ) ) {
            relationshipValues[i] = rowKey.getColumnValues()[j];
          }
        }
      }
    }
    else {
      relationshipValues = getEntityKey( associationKey, rowKey ).getColumnValues();
    }
    Object[] queryValues = ArrayHelper.concat( associationKey.getEntityKey().getColumnValues(), relationshipValues );
    executionEngine.execute( removeAssociationRowQuery, params( queryValues ) );
  }

  /**
   * Returns the entity key on the other side of association row represented by the given row key.
   * <p>
   * <b>Note:</b> May only be invoked if the row key actually contains all the columns making up that entity key.
   * Specifically, it may <b>not</b> be invoked if the association has index columns (maps, ordered collections), as
   * the entity key columns will not be part of the row key in this case.
   */
  private EntityKey getEntityKey(AssociationKey associationKey, RowKey rowKey) {
    String[] associationKeyColumns = associationKey.getMetadata().getAssociatedEntityKeyMetadata().getAssociationKeyColumns();
    Object[] columnValues = new Object[associationKeyColumns.length];
    int i = 0;

    for ( String associationKeyColumn : associationKeyColumns ) {
      columnValues[i] = rowKey.getColumnValue( associationKeyColumn );
      i++;
    }

    EntityKeyMetadata entityKeyMetadata = associationKey.getMetadata().getAssociatedEntityKeyMetadata().getEntityKeyMetadata();
    return new EntityKey( entityKeyMetadata, columnValues );
  }

  private static void appendRelationshipType(StringBuilder queryBuilder, AssociationKeyMetadata associationKeyMetadata) {
    escapeIdentifier( queryBuilder, associationKeyMetadata.getCollectionRole() );
  }
}
TOP

Related Classes of org.hibernate.ogm.datastore.neo4j.dialect.impl.Neo4jAssociationQueries

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.