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

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

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

import java.util.HashMap;
import java.util.Map;

import org.hibernate.ogm.grid.AssociationKey;
import org.hibernate.ogm.grid.EntityKey;
import org.hibernate.ogm.grid.Key;
import org.hibernate.ogm.grid.RowKey;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.ResourceIterator;

/**
* Provides a way to create, remove and look for nodes and relationships using the cypher query language.
* <p>
* A node is created using an {@link EntityKey}. A node is labeled with {@link NodeLabel#ENTITY} and with the value
* returned by {@link EntityKey#getTable()}. The properties names and values of the node are the corresponding values of
* {@link EntityKey#getColumnNames()} and {@link EntityKey#getColumnValues()}
* <p>
* A relationship is created using an {@link AssociationKey} and a {@link RowKey}. The type of a new relationship is the
* value returned by {@link AssociationKey#getCollectionRole()}. The names and values of the properties of the
* relationship are the corresponding {@link RowKey#getColumnNames()} and {@link RowKey#getColumnValues()}.
* <p>
* This class must be thread-safe since it's used by the dialect as a reference.
*
* @author Davide D'Alto <davide@hibernate.org>
*/
public class CypherCRUD {

  private final ExecutionEngine engine;

  public CypherCRUD(GraphDatabaseService graphDb) {
    this.engine = new ExecutionEngine( graphDb );
  }

  /**
   * Query example:
   *
   * <pre>
   * MATCH (n) - [r:owners {`owner_id`: {0}} }] -> ()
   * RETURN r</pre>
   *
   * @param associationKey identify the type of the relationship
   * @param rowKey identify the relationship
   * @return the corresponding relationship
   */
  public Relationship findRelationship(AssociationKey associationKey, RowKey rowKey) {
    EntityKey entityKey = associationKey.getEntityKey();
    Map<String, Object> parameters = new HashMap<String, Object>( entityKey.getColumnNames().length + rowKey.getColumnNames().length );
    StringBuilder query = new StringBuilder( "MATCH" );
    appendNodePattern( entityKey, parameters, query, ENTITY );
    query.append( " - " );
    query.append( relationshipCypher( associationKey, rowKey, parameters, entityKey.getColumnNames().length ) );
    query.append( " -> () RETURN r" );
    ExecutionResult result = engine.execute( query.toString(), parameters );
    ResourceIterator<Relationship> column = result.columnAs( "r" );
    Relationship relationship = null;
    if ( column.hasNext() ) {
      relationship = column.next();
    }
    column.close();
    return relationship;
  }

  /**
   * Find the node representing the {@link Key}.
   * <pre>
   * MATCH (n:Table {`id`: {0} })</p>
   *
   * @param key representing the node
   * @return the corresponding {@link Node} or null
   */
  public Node findNode(Key key) {
    return findNode( key, null );
  }

  /**
   * Find the node representing the entity key.
   * <pre>
   * MATCH (n:ENTITY:Table {`id`: {0} })</p>
   *
   * @param key representing the node
   * @return the corresponding {@link Node} or null
   */
  public Node findNode(Key key, NodeLabel label) {
    Map<String, Object> parameters = new HashMap<String, Object>( key.getColumnNames().length );
    StringBuilder query = new StringBuilder( "MATCH" );
    appendNodePattern( key, parameters, query, label );
    query.append( " RETURN n" );
    ExecutionResult result = engine.execute( query.toString(), parameters );
    ResourceIterator<Node> column = result.columnAs( "n" );
    Node node = null;
    if ( column.hasNext() ) {
      node = column.next();
    }
    column.close();
    return node;
  }

  /**
   * Remove the node and all the relationships connected to it.
   * Query example:
   * <pre>
   * MATCH (n:ENTITY:Table {`id`: {0}})
   * OPTIONAL MATCH (n) - [r] - ()
   * DELETE r,n</pre>
   *
   * @param entityKey of the entity to remove
   */
  public void remove(EntityKey entityKey) {
    Map<String, Object> parameters = new HashMap<String, Object>( entityKey.getColumnNames().length );
    StringBuilder query = new StringBuilder( "MATCH" );
    appendNodePattern( entityKey, parameters, query, NodeLabel.ENTITY );
    query.append( " OPTIONAL MATCH (n) - [r] - () DELETE r,n" );
    engine.execute( query.toString(), parameters );
  }

  /**
   * Query example:
   * <pre>
   * MATCH (n:`tableName`) RETURN n</pre>
   *
   * @return the {@link ResourceIterator} with the results
   */
  public ResourceIterator<Node> findNodes(String tableName) {
    String query = "MATCH (n:`" + tableName + "`) RETURN n";
    ExecutionResult result = engine.execute( query.toString() );
    return result.columnAs( "n" );
  }

  /**
   * Find a node or create it if it doesn't exist. Query example:
   *
   * <pre>
   * MERGE (n:ENTITY:Table {`id`: {0} })
   * RETURN n</pre>
   *
   * @param key identify the type of the relationship
   * @return the resulting node
   */
  public Node createNodeUnlessExists(Key key, NodeLabel label) {
    Map<String, Object> parameters = new HashMap<String, Object>( key.getColumnNames().length );
    StringBuilder query = new StringBuilder( "MERGE" );
    appendNodePattern( key, parameters, query, label );
    query.append( " RETURN n" );
    ExecutionResult result = engine.execute( query.toString(), parameters );
    ResourceIterator<Node> column = result.columnAs( "n" );
    Node node = null;
    if ( column.hasNext() ) {
      node = column.next();
    }
    column.close();
    return node;
  }

  /**
   * Appends to a query the pattern that can be used in a Cypher query to identify a node.
   * <p>
   * Pattern example:
   *
   * <pre>(n:ENTITY:Table {`id`: {0} })</pre>
   *
   * @param key identifies the node
   * @param parameters is populated with the place-holders and the value to use when the query is executed
   * @param query where the resulting pattern will be appended
   * @param label a label to be attached
   */
  private void appendNodePattern(Key key, Map<String, Object> parameters, StringBuilder query, NodeLabel label) {
    query.append( "(n:`" );
    query.append( key.getTable() );
    query.append( "`" );
    if ( label != null ) {
      query.append( ":" );
      query.append( label.name() );
    }
    query.append( " {" );
    int columnsLength = key.getColumnNames().length;
    for ( int i = 0; i < columnsLength; i++ ) {
      query.append( "`" );
      query.append( key.getColumnNames()[i] );
      query.append( "`: {" );
      query.append( i );
      query.append( "}" );
      parameters.put( String.valueOf( i ), key.getColumnValues()[i] );
      if ( i < columnsLength - 1 ) {
        query.append( "," );
      }
    }
    query.append( "})" );
  }

  /**
   * Appends to a query the pattern that can be used in a Cypher to identify a relationship.
   * <p>
   * Pattern example:
   * <pre>[r:owners {`owner_id`: (0} }]</pre>
   *
   * @param associationKey
   * @param rowKey
   * @param parameters
   * @param counter
   * @return
   */
  private String relationshipCypher(AssociationKey associationKey, RowKey rowKey, Map<String, Object> parameters, int counter) {
    String[] columnNames = rowKey.getColumnNames();
    Object[] columnValues = rowKey.getColumnValues();
    String table = rowKey.getTable();
    StringBuilder relationshipBuilder = new StringBuilder("[r");
    if (associationKey != null) {
      relationshipBuilder.append( ":" );
      relationshipBuilder.append( relationshipType( associationKey ).name() );
    }
    appendRelationshipProperties( parameters, counter, columnNames, columnValues, table, relationshipBuilder );
    return relationshipBuilder.toString();
  }

  /**
   * Appends to a query the pattern that can be used in a Cypher to identify a relationship.
   * <p>
   * Pattern example:
   *
   * <pre>
   * [r:owners {`owner_id`: {0} }]</pre>
   *
   * @param associationKey identify the type of the relationship
   * @param parameters will be populated with the corresponding value for a place-holder in the query
   * @param counter initial value for the place-holders in the query.
   */
  private String relationshipCypher(AssociationKey associationKey, Map<String, Object> parameters, int counter) {
    String[] columnNames = associationKey.getColumnNames();
    Object[] columnValues = associationKey.getColumnValues();
    String table = associationKey.getTable();
    StringBuilder relationshipBuilder = new StringBuilder( "[r" );
    relationshipBuilder.append( ":" );
    relationshipBuilder.append( relationshipType( associationKey ).name() );
    appendRelationshipProperties( parameters, counter, columnNames, columnValues, table, relationshipBuilder );
    return relationshipBuilder.toString();
  }

  private void appendRelationshipProperties(Map<String, Object> parameters, int counter, String[] columnNames, Object[] columnValues, String table,
      StringBuilder relationshipBuilder) {
    relationshipBuilder.append( " { " );
    for ( int i = 0; i < columnNames.length; i++ ) {
      if ( columnValues[i] != null ) {
        relationshipBuilder.append( "`" );
        relationshipBuilder.append( columnNames[i] );
        relationshipBuilder.append( "`" );
        relationshipBuilder.append( " : {" );
        relationshipBuilder.append( counter );
        relationshipBuilder.append( "}" );
        parameters.put( String.valueOf( counter++ ), columnValues[i] );
        if ( i < columnNames.length - 1 ) {
          relationshipBuilder.append( "," );
        }
      }
    }
    relationshipBuilder.append( "}]" );
  }

  /**
   * Converts the association key into the type of the relationship.
   *
   * @param associationKey the identifier of the association
   * @return the corresponding {@link RelationshipType}
   */
  public static RelationshipType relationshipType(AssociationKey associationKey) {
    return DynamicRelationshipType.withName( associationKey.getCollectionRole() );
  }

  /**
   * Query example:
   * <pre>
   * MATCH (n) - [r:collectionRole { 'associationKey_column_name': {0}}] - ()
   * DELETE r</pre>
   */
  public void remove(AssociationKey associationKey) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    StringBuilder query = new StringBuilder( "MATCH (n) - " );
    query.append( relationshipCypher( associationKey, parameters, 0 ) );
    query.append( " - () DELETE r" );
    engine.execute( query.toString(), parameters );
  }

  /**
   * Query example:
   * <pre>MATCH (n) - [r:collectionRole { 'rowkey_column_name': {0}}] - ()
   * DELETE r
   * </pre>
   */
  public void remove(AssociationKey associationKey, RowKey rowKey) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    StringBuilder query = new StringBuilder( "MATCH (n) - " );
    query.append( relationshipCypher( associationKey, rowKey, parameters, 0 ) );
    query.append( " - () DELETE r" );
    engine.execute( query.toString(), parameters );
  }

  public ExecutionResult executeQuery( String query, Map<String, Object> parameters ) {
    return engine.execute( query, parameters );
  }
}
TOP

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

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.