Package org.neo4j.collections.timeline

Source Code of org.neo4j.collections.timeline.Timeline

/**
* Copyright (c) 2002-2013 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.collections.timeline;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.neo4j.collections.btree.BTree;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.ReturnableEvaluator;
import org.neo4j.graphdb.StopEvaluator;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.TraversalPosition;
import org.neo4j.graphdb.Traverser;
import org.neo4j.graphdb.Traverser.Order;
import org.neo4j.kernel.AbstractGraphDatabase;

import javax.transaction.TransactionManager;


/**
* An implementation of {@link TimelineIndex} on top of Neo4j, using
* {@link BTree} for indexing. Note: this implementation is not thread-safe
* (yet).
*
* Nodes added to a timeline will get a {@link Relationship} created to it so if
* you delete such a node later on you'll have to remove it from the timeline
* first (or in the same transaction at least).
*/
public class Timeline implements TimelineIndex
{
    static enum RelTypes implements RelationshipType
    {
        TIMELINE_INSTANCE,
        TIMELINE_NEXT_ENTRY,
    }

    private static final String TIMESTAMP = "timestamp";
    private static final String TIMELINE_NAME = "timeline_name";
    private static final String TIMELINE_IS_INDEXED = "timeline_indexed";
    private static final String INDEX_COUNT = "index_count";
    private static int INDEX_TRIGGER_COUNT = 1000;

    private final Node underlyingNode;
    private final boolean indexed;
    private BTree indexBTree;
    private final String name;
    private final GraphDatabaseService graphDb;

    // lazy init cache holders for first and last
    private Node firstNode;
    private Node lastNode;

    /**
     * Creates/loads a timeline. The <CODE>underlyingNode</CODE> can either be a
     * new (just created) node or a node that already represents a previously
     * timeline.
     *
     * @param name The unique name of the timeline or <CODE>null</CODE> if
     *            timeline already exist
     * @param underlyingNode The underlying node representing the timeline
     * @param indexed Set to <CODE>true</CODE> if this timeline is indexed
     * @param graphDb the {@link GraphDatabaseService}
     */
    public Timeline( String name, Node underlyingNode, boolean indexed,
            GraphDatabaseService graphDb )
    {
        if ( underlyingNode == null || graphDb == null )
        {
            throw new IllegalArgumentException(
                    "Null parameter underlyingNode=" + underlyingNode
                            + " graphDb=" + graphDb );
        }
        this.underlyingNode = underlyingNode;
        this.graphDb = graphDb;
        Transaction tx = graphDb.beginTx();
        try
        {
            assertPropertyIsSame( TIMELINE_NAME, name );
            this.name = name;
            assertPropertyIsSame( TIMELINE_IS_INDEXED, indexed );
            this.indexed = indexed;
            if ( indexed )
            {
                Relationship bTreeRel = underlyingNode.getSingleRelationship(
                        BTree.RelTypes.TREE_ROOT, Direction.OUTGOING );
                if ( bTreeRel == null )
                {
                    Node bTreeNode = graphDb.createNode();
                    bTreeRel = underlyingNode.createRelationshipTo( bTreeNode,
                            BTree.RelTypes.TREE_ROOT );
                }
                indexBTree = new BTree( graphDb, bTreeRel.getEndNode() );
            }
            tx.success();
        }
        finally
        {
            tx.finish();
        }
    }
   
    public Timeline( String name, Node underlyingNode, boolean indexed, int indexTriggerCount,
            GraphDatabaseService graphDb )
    {
        this(name, underlyingNode, indexed, graphDb);
        INDEX_TRIGGER_COUNT = indexTriggerCount;
    }

    private void assertPropertyIsSame( String key, Object value )
    {
        Object storedValue = underlyingNode.getProperty( key, null );
        if ( storedValue != null )
        {
            if ( !storedValue.equals( value ) )
            {
                throw new IllegalArgumentException( "Timeline("
                                                    + underlyingNode
                                                    + ") property '" + key
                                                    + "' is " + storedValue
                                                    + ", passed in " + value );
            }
        }
        else
        {
            underlyingNode.setProperty( key, value );
        }
    }

    /**
     * Creates/loads an indexed timeline. The <CODE>underlyingNode</CODE> can
     * either be a new (just created) node or a node that already represents a
     * previously timeline.
     *
     * @param name The unique name of the timeline or <CODE>null</CODE> if
     *            timeline already exist
     * @param underlyingNode The underlying node representing the timeline
     * @param graphDb The {@link GraphDatabaseService}.
     */
    public Timeline( String name, Node underlyingNode,
            GraphDatabaseService graphDb )
    {
        this( name, underlyingNode, true, graphDb );
    }

    /**
     * Returns the underlying node representing this timeline.
     *
     * @return The underlying node representing this timeline
     */
    public Node getUnderlyingNode()
    {
        return underlyingNode;
    }

    public Node getLastNode()
    {
        if ( lastNode != null )
        {
            return lastNode;
        }
            Relationship rel = underlyingNode.getSingleRelationship(
                    RelTypes.TIMELINE_NEXT_ENTRY, Direction.INCOMING );
            if ( rel == null )
            {
                return null;
            }
            lastNode = rel.getStartNode().getRelationships(
                    RelTypes.TIMELINE_INSTANCE, Direction.OUTGOING ).iterator().next().getEndNode();
            return lastNode;
    }

    public Node getFirstNode()
    {
        if ( firstNode != null )
        {
            return firstNode;
        }
            Relationship rel = underlyingNode.getSingleRelationship(
                    RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING );
            if ( rel == null )
            {
                return null;
            }
            firstNode = rel.getEndNode().getRelationships(
                    RelTypes.TIMELINE_INSTANCE, Direction.OUTGOING ).iterator().next().getEndNode();
            return firstNode;
    }

    public void addNode( Node nodeToAdd, long timestamp )
    {
        if ( nodeToAdd == null )
        {
            throw new IllegalArgumentException( "Null node" );
        }
        Transaction tx = graphDb.beginTx();
        try
        {
            for ( Relationship rel : nodeToAdd.getRelationships( RelTypes.TIMELINE_INSTANCE ) )
            {
                if ( rel.getProperty( TIMELINE_NAME, "" ).equals( name ) )
                {
                    throw new IllegalArgumentException(
                            "Node[" + nodeToAdd.getId()
                                    + "] already connected to Timeline[" + name
                                    + "]" );
                }
            }
            Relationship rel = underlyingNode.getSingleRelationship(
                    RelTypes.TIMELINE_NEXT_ENTRY, Direction.INCOMING );
            if ( rel == null )
            {
                // timeline was empty
                Node node = createNewTimeNode( timestamp, nodeToAdd );
                underlyingNode.createRelationshipTo( node,
                        RelTypes.TIMELINE_NEXT_ENTRY );
                node.createRelationshipTo( underlyingNode,
                        RelTypes.TIMELINE_NEXT_ENTRY );
                firstNode = nodeToAdd;
                lastNode = nodeToAdd;
                updateNodeAdded( timestamp );
            }
            else
            {
                Node previousLast = rel.getStartNode();
                long previousTime = (Long) previousLast.getProperty( TIMESTAMP );
                if ( timestamp > previousTime )
                {
                    // add it last in chain
                    Node node = createNewTimeNode( timestamp, nodeToAdd );
                    rel.delete();
                    previousLast.createRelationshipTo( node,
                            RelTypes.TIMELINE_NEXT_ENTRY );
                    node.createRelationshipTo( underlyingNode,
                            RelTypes.TIMELINE_NEXT_ENTRY );
                    lastNode = nodeToAdd;
                    updateNodeAdded( timestamp );
                }
                else if ( timestamp == previousTime )
                {
                  Relationship instanceRel = previousLast.createRelationshipTo( nodeToAdd,
                            RelTypes.TIMELINE_INSTANCE );
                  instanceRel.setProperty( TIMELINE_NAME, name );
                }
                else
                {
                    // find where to insert
                    Iterator<Node> itr = getAllTimeNodesAfter( timestamp ).iterator();
                    assert itr.hasNext();
                    Node next = itr.next();
                    rel = next.getSingleRelationship(
                            RelTypes.TIMELINE_NEXT_ENTRY, Direction.INCOMING );
                    assert rel != null;
                    Node previous = rel.getStartNode();
                    long previousTimestamp = Long.MIN_VALUE;
                    if ( !previous.equals( underlyingNode ) )
                    {
                        previousTimestamp = (Long) previous.getProperty( TIMESTAMP );
                    }
                    if ( previousTimestamp == timestamp )
                    {
                        // just connect previous with node to add
                      Relationship instanceRel = previous.createRelationshipTo( nodeToAdd,
                                RelTypes.TIMELINE_INSTANCE );
                      instanceRel.setProperty( TIMELINE_NAME, name );
                        return;
                    }
                    long nextTimestamp = (Long) next.getProperty( TIMESTAMP );
                    if ( nextTimestamp == timestamp )
                    {
                        // just connect next with node to add
                      Relationship instanceRel = next.createRelationshipTo( nodeToAdd,
                                RelTypes.TIMELINE_INSTANCE );
                      instanceRel.setProperty( TIMELINE_NAME, name );
                        return;
                    }

                    assert previousTimestamp < timestamp;
                    assert nextTimestamp > timestamp;

                    Node node = createNewTimeNode( timestamp, nodeToAdd );
                    rel.delete();
                    previous.createRelationshipTo( node,
                            RelTypes.TIMELINE_NEXT_ENTRY );
                    node.createRelationshipTo( next,
                            RelTypes.TIMELINE_NEXT_ENTRY );
                    if ( previous.equals( underlyingNode ) )
                    {
                        firstNode = nodeToAdd;
                    }
                    updateNodeAdded( timestamp );
                }
            }
            tx.success();
        }
        finally
        {
            tx.finish();
        }
    }

    private Node createNewTimeNode( long timestamp, Node nodeToAdd )
    {
        Node node = graphDb.createNode();
        node.setProperty( TIMESTAMP, timestamp );
        Relationship instanceRel = node.createRelationshipTo( nodeToAdd,
                RelTypes.TIMELINE_INSTANCE );
        instanceRel.setProperty( TIMELINE_NAME, name );
        return node;
    }

    public long getTimestampForNode( Node node )
    {
        Transaction tx = graphDb.beginTx();
        try
        {
            Traverser traverser = node.traverse( Traverser.Order.DEPTH_FIRST,
                    StopEvaluator.END_OF_GRAPH, new ReturnableEvaluator()
                    {
                        public boolean isReturnableNode(
                                TraversalPosition position )
                        {
                            Node currentNode = position.currentNode();
                            return currentNode != null
                                   && !currentNode.hasRelationship(
                                           RelTypes.TIMELINE_INSTANCE,
                                           Direction.INCOMING );
                        }
                    }, RelTypes.TIMELINE_INSTANCE, Direction.INCOMING );

            Iterator<Node> hits = traverser.iterator();
            Long result = null;
            if ( hits.hasNext() )
            {
                Node hit = hits.next();
                result = (Long) hit.getProperty( TIMESTAMP );
            }
            else
            {
                throw new RuntimeException(
                        "No timpestamp found for '" + node
                                + "' maybe it's not in the timeline?" );
            }
            tx.success();
            return result;
        }
        finally
        {
            tx.finish();
        }
    }

    private synchronized void updateNodeAdded( final long timestamp )
    {
        if ( !indexed )
        {
            return;
        }
        Long nodeId = (Long) indexBTree.getClosestHigherEntry( timestamp );
        if ( nodeId == null )
        {
            // no indexing yet, check if time to add index
            int indexCount = (Integer) underlyingNode.getProperty( INDEX_COUNT,
                    0 );
            indexCount++;
            if ( indexCount >= INDEX_TRIGGER_COUNT )
            {
                indexCount = createIndex( underlyingNode, indexCount );
            }
            underlyingNode.setProperty( INDEX_COUNT, indexCount );
        }
        else
        {
            Node indexedNode = graphDb.getNodeById( nodeId );
            int indexCount = (Integer) indexedNode.getProperty( INDEX_COUNT );
            indexCount++;
            if ( indexCount >= INDEX_TRIGGER_COUNT )
            {
                indexCount = createIndex( indexedNode, indexCount );
            }
            indexedNode.setProperty( INDEX_COUNT, indexCount );
        }
    }

    // returns new count to set on next higher index and
    // creates the new indexing relationship
    private int createIndex( Node startIndexNode, int currentCount )
    {
        assert indexed;
        int newCount = 0;
        // use 0.33f beacuse most timelines are not random timestamp
        // insertion, instead they just grow at the end, so 0.33 (instead of
        // 0.5) results in less balancing of tree (and tree depth at start)
        int timesToTraverse = (int) ( INDEX_TRIGGER_COUNT * 0.33f );
        assert timesToTraverse > 0;
        Node newIndexedNode = startIndexNode;
        for ( int i = 0; i < timesToTraverse; i++ )
        {
            newIndexedNode = newIndexedNode.getSingleRelationship(
                    RelTypes.TIMELINE_NEXT_ENTRY, Direction.INCOMING ).getStartNode();
           
            newCount++;
            assert !newIndexedNode.hasProperty( INDEX_COUNT );
        }
        long timestamp = (Long) newIndexedNode.getProperty( TIMESTAMP );
        indexBTree.addEntry( timestamp, newIndexedNode.getId() );
        newIndexedNode.setProperty( INDEX_COUNT, currentCount - timesToTraverse );
        return newCount;
    }
   
    public void removeNode(Node nodeToRemove, boolean transactional)
    {
        if ( nodeToRemove == null )
        {
            throw new IllegalArgumentException( "Null parameter." );
        }
        if ( nodeToRemove.equals( underlyingNode ) )
        {
            throw new IllegalArgumentException( "Cannot remove underlying node" );
        }
        Transaction tx = graphDb.beginTx();
        try
        {
            Relationship instanceRel = null;
            for ( Relationship rel : nodeToRemove.getRelationships( RelTypes.TIMELINE_INSTANCE ) )
            {
                if ( rel.getProperty( TIMELINE_NAME, "" ).equals( name ) )
                {
                    assert instanceRel == null;
                    instanceRel = rel;
                }
            }
            if ( instanceRel == null )
            {
                throw new IllegalArgumentException(
                        "Node[" + nodeToRemove.getId()
                                + "] not added to Timeline[" + name + "]" );
            }
            Node node = instanceRel.getStartNode();
            instanceRel.delete();
            if ( firstNode != null && firstNode.equals( nodeToRemove ) )
            {
                firstNode = null;
            }
            if ( lastNode != null && lastNode.equals( nodeToRemove ) )
            {
                lastNode = null;
            }
            if ( node.getRelationships( RelTypes.TIMELINE_INSTANCE ).iterator().hasNext() )
            {
                // still have instances connected to this time
                return;
            }
            Relationship incoming = node.getSingleRelationship(
                    RelTypes.TIMELINE_NEXT_ENTRY, Direction.INCOMING );
            if ( incoming == null )
            {
                throw new RuntimeException( "No incoming relationship of "
                                            + RelTypes.TIMELINE_NEXT_ENTRY
                                            + " found" );
            }
            Relationship outgoing = node.getSingleRelationship(
                    RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING );
            if ( outgoing == null )
            {
                throw new RuntimeException( "No outgoing relationship of "
                                            + RelTypes.TIMELINE_NEXT_ENTRY
                                            + " found" );
            }
            Node previous = incoming.getStartNode();
            Node next = outgoing.getEndNode();
            incoming.delete();
            outgoing.delete();
            // TODO: this needs proper synchronization
            if ( node.hasProperty( INDEX_COUNT ) )
            {
                long nodeId = (Long) indexBTree.removeEntry( (Long) node.getProperty( TIMESTAMP ) );
                assert nodeId == node.getId();
                int count = (Integer) node.getProperty( INDEX_COUNT );
                count--;
                if ( !previous.equals( underlyingNode )
                     && !previous.hasProperty( INDEX_COUNT ) )
                {
                    previous.setProperty( INDEX_COUNT, count );
                    indexBTree.addEntry(
                            (Long) previous.getProperty( TIMESTAMP ),
                            previous.getId() );
                }
            }
            else
            {
                long timestamp = (Long) node.getProperty( TIMESTAMP );
                if ( indexed )
                {
                    Long nodeId = (Long) indexBTree.getClosestHigherEntry( timestamp );
                    if ( nodeId != null )
                    {
                        Node indexedNode = graphDb.getNodeById( nodeId );
                        int count = (Integer) indexedNode.getProperty( INDEX_COUNT );
                        count--;
                        indexedNode.setProperty( INDEX_COUNT, count );
                    }
                    else
                    {
                        if ( underlyingNode.hasProperty( INDEX_COUNT ) )
                        {
                            int count = (Integer) underlyingNode.getProperty( INDEX_COUNT );
                            count--;
                            underlyingNode.setProperty( INDEX_COUNT, count );
                        }
                    }
                }
            }
            node.delete();
            if ( !previous.equals( next ) )
            {
                previous.createRelationshipTo( next,
                        RelTypes.TIMELINE_NEXT_ENTRY );
            }
            tx.success();
        }
        finally
        {
            if(transactional)
            {
                tx.finish();
            }
        }
    }

    public void removeNode( Node nodeToRemove )
    {
        removeNode( nodeToRemove, true );
    }

    public Iterable<Node> getAllNodes( Long afterTimestampOrNull,
            Long beforeTimestampOrNull )
    {
        Iterable<Node> result = null;
        if ( afterTimestampOrNull == null && beforeTimestampOrNull == null )
        {
            result = getAllNodes();
        }
        else if ( afterTimestampOrNull == null )
        {
            result = getAllNodesBefore( beforeTimestampOrNull );
        }
        else if ( beforeTimestampOrNull == null )
        {
            result = getAllNodesAfter( afterTimestampOrNull );
        }
        else
        {
            result = getAllNodesBetween( afterTimestampOrNull,
                    beforeTimestampOrNull );
        }
        return result;
    }

    public Iterable<Node> getAllNodes()
    {
        return underlyingNode.traverse(
                Order.BREADTH_FIRST,
                StopEvaluator.END_OF_GRAPH,
                new ReturnableEvaluator()
                {
                    public boolean isReturnableNode( TraversalPosition position )
                    {
                        Relationship last = position.lastRelationshipTraversed();
                        if ( last != null
                             && last.isType(
                                     RelTypes.TIMELINE_INSTANCE ) )
                        {
                            return true;
                        }
                        return false;
                    }
                }, RelTypes.TIMELINE_INSTANCE, Direction.OUTGOING,
                RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING );
    }

    Iterable<Node> getAllTimeNodes()
    {
        return underlyingNode.traverse( Order.DEPTH_FIRST,
                StopEvaluator.END_OF_GRAPH, new ReturnableEvaluator()
                {
                    public boolean isReturnableNode( TraversalPosition position )
                    {
                        return position.depth() > 0;
                    }
                }, RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING );
    }

    // from closest lower indexed start relationship
    private Node getIndexedStartNode( long timestamp )
    {
        if ( indexed )
        {
            Node startNode = underlyingNode;
            Long nodeId = (Long) indexBTree.getClosestLowerEntry( timestamp );
            if ( nodeId != null )
            {
                startNode = graphDb.getNodeById( nodeId );
            }
            return startNode;
        }
        return underlyingNode;
    }

    public Iterable<Node> getNodes( long timestamp )
    {
        Node currentNode = getIndexedStartNode( timestamp );
        List<Node> nodeList = new ArrayList<Node>();
        if ( currentNode.equals( underlyingNode ) )
        {
            if ( !currentNode.hasRelationship( RelTypes.TIMELINE_NEXT_ENTRY,
                    Direction.OUTGOING ) )
            {
                // empty timeline
                return nodeList;
            }
            // no index or best start node is underlying node
            currentNode = currentNode.getSingleRelationship(
                    RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING ).getEndNode();
        }
        do
        {
            long currentTime = (Long) currentNode.getProperty( TIMESTAMP );
            if ( currentTime == timestamp )
            {
                for ( Relationship instanceRel : currentNode.getRelationships(
                        RelTypes.TIMELINE_INSTANCE, Direction.OUTGOING ) )
                {
                    nodeList.add( instanceRel.getEndNode() );
                }
                break;
            }
            if ( currentTime > timestamp )
            {
                break;
            }
            Relationship rel = currentNode.getSingleRelationship(
                    RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING );
            currentNode = rel.getEndNode();
        }
        while ( !currentNode.equals( underlyingNode ) );
        return nodeList;
    }

    public Iterable<Node> getAllNodesAfter( final long timestamp )
    {
        Node startNode = getIndexedStartNode( timestamp );
        return startNode.traverse( Order.DEPTH_FIRST, new StopEvaluator()
        {
            public boolean isStopNode( TraversalPosition position )
            {
                if ( position.lastRelationshipTraversed() != null
                     && position.currentNode().equals( underlyingNode ) )
                {
                    return true;
                }
                return false;
            }
        }, new ReturnableEvaluator()
        {
            private boolean timeOk = false;

            public boolean isReturnableNode( TraversalPosition position )
            {
                if ( position.currentNode().equals( underlyingNode ) )
                {
                    return false;
                }
                Relationship last = position.lastRelationshipTraversed();
                if ( !timeOk && last != null
                     && last.isType( RelTypes.TIMELINE_NEXT_ENTRY ) )
                {
                    Node node = position.currentNode();
                    long currentTime = (Long) node.getProperty( TIMESTAMP );
                    timeOk = currentTime > timestamp;
                    return false;
                }
                if ( timeOk
                     && last.isType( RelTypes.TIMELINE_INSTANCE ) )
                {
                    return true;
                }
                return false;
            }
        }, RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING,
                RelTypes.TIMELINE_INSTANCE, Direction.OUTGOING );
    }

    Iterable<Node> getAllTimeNodesAfter( final long timestamp )
    {
        Node startNode = getIndexedStartNode( timestamp );
        return startNode.traverse( Order.DEPTH_FIRST, new StopEvaluator()
        {
            public boolean isStopNode( TraversalPosition position )
            {
                if ( position.lastRelationshipTraversed() != null
                     && position.currentNode().equals( underlyingNode ) )
                {
                    return true;
                }
                return false;
            }
        }, new ReturnableEvaluator()
        {
            private boolean timeOk = false;

            public boolean isReturnableNode( TraversalPosition position )
            {
                if ( position.currentNode().equals( underlyingNode ) )
                {
                    return false;
                }
                Relationship last = position.lastRelationshipTraversed();
                if ( !timeOk && last != null
                     && last.isType( RelTypes.TIMELINE_NEXT_ENTRY ) )
                {
                    Node node = position.currentNode();
                    long currentTime = (Long) node.getProperty( TIMESTAMP );
                    timeOk = currentTime > timestamp;
                }
                return timeOk;
            }
        }, RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING );
    }

    public Iterable<Node> getAllNodesBefore( final long timestamp )
    {
        return underlyingNode.traverse( Order.DEPTH_FIRST, new StopEvaluator()
        {
            public boolean isStopNode( TraversalPosition position )
            {
                Relationship last = position.lastRelationshipTraversed();
                if ( last != null
                     && last.isType( RelTypes.TIMELINE_NEXT_ENTRY ) )
                {
                    Node node = position.currentNode();
                    long currentTime = (Long) node.getProperty( TIMESTAMP );
                    return currentTime >= timestamp;
                }
                return false;
            }
        }, new ReturnableEvaluator()
        {
            public boolean isReturnableNode( TraversalPosition position )
            {
                Relationship last = position.lastRelationshipTraversed();
                if ( last != null
                     && last.isType( RelTypes.TIMELINE_INSTANCE) )
                {
                    return true;
                }
                return false;
            }
        }, RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING,
                RelTypes.TIMELINE_INSTANCE, Direction.OUTGOING );
    }

    public Iterable<Node> getAllNodesBetween( final long startTime,
            final long endTime )
    {
        if ( startTime >= endTime )
        {
            throw new IllegalArgumentException(
                    "Start time greater or equal to end time" );
        }
        Node startNode = getIndexedStartNode( startTime );
        return startNode.traverse( Order.DEPTH_FIRST, new StopEvaluator()
        {
            public boolean isStopNode( TraversalPosition position )
            {
                Relationship last = position.lastRelationshipTraversed();
                if ( last != null
                     && position.currentNode().equals( underlyingNode ) )
                {
                    return true;
                }
                if ( last != null
                     && last.isType( RelTypes.TIMELINE_NEXT_ENTRY ) )
                {
                    Node node = position.currentNode();
                    long currentTime = (Long) node.getProperty( TIMESTAMP );
                    return currentTime >= endTime;
                }
                return false;
            }
        }, new ReturnableEvaluator()
        {
            private boolean timeOk = false;

            public boolean isReturnableNode( TraversalPosition position )
            {
                if ( position.currentNode().equals( underlyingNode ) )
                {
                    return false;
                }
                Relationship last = position.lastRelationshipTraversed();
                if ( !timeOk && last != null
                     && last.isType( RelTypes.TIMELINE_NEXT_ENTRY ) )
                {
                    Node node = position.currentNode();
                    long currentTime = (Long) node.getProperty( TIMESTAMP );
                    timeOk = currentTime > startTime;
                    return false;
                }
                if ( timeOk
                     && last.isType( RelTypes.TIMELINE_INSTANCE ) )
                {
                    return true;
                }
                return false;
            }
        }, RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING,
                RelTypes.TIMELINE_INSTANCE, Direction.OUTGOING );
    }

    public void delete()
    {
        if ( indexed )
        {
            indexBTree.delete();
        }
        Relationship rel = underlyingNode.getSingleRelationship(
                RelTypes.TIMELINE_NEXT_ENTRY, Direction.OUTGOING );
        while ( rel != null )
        {
            Node node = rel.getEndNode();
            if ( !node.equals( underlyingNode ) )
            {
                for ( Relationship instance : node.getRelationships( RelTypes.TIMELINE_INSTANCE ) )
                {
                    instance.delete();
                }
                rel.delete();
                rel = node.getSingleRelationship( RelTypes.TIMELINE_NEXT_ENTRY,
                        Direction.OUTGOING );
                node.delete();
            }
            else
            {
                rel.delete();
                rel = null;
            }
        }
    }
   
    public void delete(int commitInterval)
    {
        int count = 0;
        while(this.getLastNode()!=null) {
           
            this.removeNode( this.getLastNode() );
            count++;
            if ( count > commitInterval )
            {
                System.out.print(".");
                restartTx();
                count = 0;
            }
        }
        if ( indexed )
        {
            indexBTree.delete(commitInterval);
        }
    }
   
    private void restartTx() {
            try
            {
                TransactionManager transactionManager = ((AbstractGraphDatabase) graphDb).getDependencyResolver().resolveDependency(TransactionManager.class);
                javax.transaction.Transaction tx = transactionManager.getTransaction();
                if ( tx != null )
                {
                    tx.commit();
                }
            }
            catch ( Exception e )
            {
                throw new RuntimeException( e );
            }
            graphDb.beginTx();
        }
}
TOP

Related Classes of org.neo4j.collections.timeline.Timeline

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.