/**
   * Fetch relationships for the given source node.
   *
   * @param sourceNode
   * @param relType can be null
   * @param dir
   * @return a list of relationships
   * @throws FrameworkException
   */
  public List<AbstractRelationship> execute(
      AbstractNode sourceNode, RelationshipType relType, Direction dir) throws FrameworkException {

    RelationshipFactory factory = new RelationshipFactory(securityContext);
    List<AbstractRelationship> result = new LinkedList<AbstractRelationship>();
    Node node = sourceNode.getNode();
    Iterable<Relationship> rels;

    if (node == null) {

      return Collections.EMPTY_LIST;
    }

    if (relType != null) {

      rels = node.getRelationships(relType, dir);

    } else {

      rels = node.getRelationships(dir);
    }

    try {

      for (Relationship r : rels) {

        result.add(factory.instantiate(r));
      }

    } catch (RuntimeException e) {

      logger.log(Level.WARNING, "Exception occured: ", e.getMessage());

      /**
       * ********* FIXME
       *
       * <p>Here an exception occurs:
       *
       * <p>org.neo4j.kernel.impl.nioneo.store.InvalidRecordException: Node[5] is neither
       * firstNode[37781] nor secondNode[37782] for Relationship[188125] at
       * org.neo4j.kernel.impl.nioneo.xa.ReadTransaction.getMoreRelationships(ReadTransaction.java:131)
       * at
       * org.neo4j.kernel.impl.nioneo.xa.NioNeoDbPersistenceSource$ReadOnlyResourceConnection.getMoreRelationships(NioNeoDbPersistenceSource.java:280)
       * at
       * org.neo4j.kernel.impl.persistence.PersistenceManager.getMoreRelationships(PersistenceManager.java:100)
       * at org.neo4j.kernel.impl.core.NodeManager.getMoreRelationships(NodeManager.java:585) at
       * org.neo4j.kernel.impl.core.NodeImpl.getMoreRelationships(NodeImpl.java:358) at
       * org.neo4j.kernel.impl.core.IntArrayIterator.hasNext(IntArrayIterator.java:115)
       */
    }

    return result;
  }
  @Override
  public void execute(Map<String, Object> attributes) throws FrameworkException {

    final String entityType = (String) attributes.get("type");
    final String relType = (String) attributes.get("relType");
    final GraphDatabaseService graphDb = (GraphDatabaseService) arguments.get("graphDb");
    final SecurityContext superUserContext = SecurityContext.getSuperUserInstance();
    final NodeFactory nodeFactory = new NodeFactory(superUserContext);
    final RelationshipFactory relFactory = new RelationshipFactory(superUserContext);

    if (entityType != null) {

      final Class type = EntityContext.getEntityClassForRawType(entityType);

      if (type != null) {

        // final Result<AbstractNode> result = Services.command(securityContext,
        // SearchNodeCommand.class).execute(true, false, Search.andExactType(type.getSimpleName()));
        final Result<AbstractNode> result =
            nodeFactory.instantiateAll(GlobalGraphOperations.at(graphDb).getAllNodes());
        final List<AbstractNode> nodes = new ArrayList<AbstractNode>();

        for (AbstractNode node : result.getResults()) {

          if (node.getClass().equals(type)) {

            nodes.add(node);
          }
        }

        logger.log(
            Level.INFO,
            "Start (re-)indexing all nodes of type {0}",
            new Object[] {type.getSimpleName()});

        long count =
            bulkGraphOperation(
                securityContext,
                nodes,
                1000,
                "RebuildIndex",
                new BulkGraphOperation<AbstractNode>() {

                  @Override
                  public void handleGraphObject(
                      SecurityContext securityContext, AbstractNode node) {

                    node.updateInIndex();
                  }

                  @Override
                  public void handleThrowable(
                      SecurityContext securityContext, Throwable t, AbstractNode node) {

                    logger.log(
                        Level.WARNING,
                        "Unable to index node {0}: {1}",
                        new Object[] {node, t.getMessage()});
                  }

                  @Override
                  public void handleTransactionFailure(
                      SecurityContext securityContext, Throwable t) {

                    logger.log(Level.WARNING, "Unable to index node: {0}", t.getMessage());
                  }
                });

        logger.log(Level.INFO, "Done with (re-)indexing {0} nodes", count);

        return;
      }

    } else if (relType != null) {

      // final Result<AbstractNode> result = Services.command(securityContext,
      // SearchNodeCommand.class).execute(true, false, Search.andExactType(type.getSimpleName()));
      final List<AbstractRelationship> unfilteredRels =
          relFactory.instantiate(GlobalGraphOperations.at(graphDb).getAllRelationships());
      final List<AbstractRelationship> rels = new ArrayList<AbstractRelationship>();

      for (AbstractRelationship rel : unfilteredRels) {

        if (!rel.getType().equals(relType)) {

          rels.add(rel);
        }
      }

      logger.log(Level.INFO, "Start setting UUID on all rels of type {0}", new Object[] {relType});

      long count =
          bulkGraphOperation(
              securityContext,
              rels,
              1000,
              "SetRelationshipUuid",
              new BulkGraphOperation<AbstractRelationship>() {

                @Override
                public void handleGraphObject(
                    SecurityContext securityContext, AbstractRelationship rel) {

                  rel.updateInIndex();
                }

                @Override
                public void handleThrowable(
                    SecurityContext securityContext, Throwable t, AbstractRelationship rel) {

                  logger.log(
                      Level.WARNING,
                      "Unable to index relationship {0}: {1}",
                      new Object[] {rel, t.getMessage()});
                }

                @Override
                public void handleTransactionFailure(SecurityContext securityContext, Throwable t) {

                  logger.log(Level.WARNING, "Unable to index relationship: {0}", t.getMessage());
                }
              });

      logger.log(Level.INFO, "Done with (re-)indexing {0} relationships", count);

      return;
    }

    logger.log(Level.INFO, "Unable to determine entity type to re-index.");
  }
  private synchronized <
          A extends NodeInterface, B extends NodeInterface, R extends Relation<A, B, ?, ?>>
      R createRelationship(
          final A fromNode, final B toNode, final Class<R> relType, final PropertyMap properties)
          throws FrameworkException {

    final RelationshipFactory<R> factory = new RelationshipFactory(securityContext);
    final R template = instantiate(relType);
    final Node startNode = fromNode.getNode();
    final Node endNode = toNode.getNode();
    final Relationship rel = startNode.createRelationshipTo(endNode, template);
    final R newRel = factory.instantiate(rel);
    final Date now = new Date();

    // logger.log(Level.INFO, "CREATING relationship {0}-[{1}]->{2}", new Object[] {
    // fromNode.getType(), newRel.getRelType(), toNode.getType() } );

    if (newRel != null) {

      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(GraphObject.type, relType.getSimpleName());

      // set UUID
      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(GraphObject.id, getNextUuid());

      // set created date
      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(AbstractRelationship.createdDate, now);

      // set last modified date
      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(AbstractRelationship.lastModifiedDate, now);

      // Try to get the cascading delete flag from the domain specific relationship type
      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(
          AbstractRelationship.cascadeDelete, factory.instantiate(rel).getCascadingDeleteFlag());

      // notify transaction handler
      TransactionCommand.relationshipCreated(newRel);

      if (properties != null) {

        for (Entry<PropertyKey, Object> entry : properties.entrySet()) {

          PropertyKey key = entry.getKey();

          // on creation, writing of read-only properties should be possible
          if (key.isReadOnly() || key.isWriteOnce()) {
            newRel.unlockReadOnlyPropertiesOnce();
          }

          newRel.setProperty(entry.getKey(), entry.getValue());
        }
      }

      // notify relationship of its creation
      newRel.onRelationshipCreation();

      // iterate post creation transformations
      for (Transformation<GraphObject> transformation :
          StructrApp.getConfiguration().getEntityCreationTransformations(newRel.getClass())) {

        transformation.apply(securityContext, newRel);
      }
    }

    return newRel;
  }