/**
* Copyright (c) 2002-2011 "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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.index.timeline;
import static java.util.Collections.sort;
import static org.junit.Assert.assertEquals;
import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.helpers.Pair;
import org.neo4j.index.timeline.LuceneTimeline;
import org.neo4j.index.timeline.TimelineIndex;
import org.neo4j.kernel.ImpermanentGraphDatabase;
public class TestTimeline
{
private GraphDatabaseService db;
private Transaction tx;
@Before
public void before() throws Exception
{
db = new ImpermanentGraphDatabase();
}
private void beginTx()
{
tx = db.beginTx();
}
private void commitTx()
{
tx.success();
tx.finish();
}
private interface EntityCreator<T extends PropertyContainer>
{
T create();
}
private EntityCreator<PropertyContainer> nodeCreator = new EntityCreator<PropertyContainer>()
{
@Override
public Node create()
{
return db.createNode();
}
};
private EntityCreator<PropertyContainer> relationshipCreator = new EntityCreator<PropertyContainer>()
{
private final RelationshipType type = DynamicRelationshipType.withName( "whatever" );
@Override
public Relationship create()
{
return db.createNode().createRelationshipTo( db.createNode(), type );
}
};
private TimelineIndex<PropertyContainer> nodeTimeline()
{
return new LuceneTimeline( db, db.index().forNodes( "timeline" ) );
}
private TimelineIndex<PropertyContainer> relationshipTimeline()
{
return new LuceneTimeline( db, db.index().forRelationships( "timeline" ) );
}
private LinkedList<Pair<PropertyContainer, Long>> createTimestamps( EntityCreator<PropertyContainer> creator,
TimelineIndex<PropertyContainer> timeline, long... timestamps )
{
beginTx();
LinkedList<Pair<PropertyContainer, Long>> result = new LinkedList<Pair<PropertyContainer,Long>>();
for ( long timestamp : timestamps )
{
result.add( createTimestampedEntity( creator, timeline, timestamp ) );
}
commitTx();
return result;
}
private Pair<PropertyContainer, Long> createTimestampedEntity( EntityCreator<PropertyContainer> creator,
TimelineIndex<PropertyContainer> timeline, long timestamp )
{
PropertyContainer entity = creator.create();
timeline.add( entity, timestamp );
return Pair.of( entity, timestamp );
}
private List<PropertyContainer> sortedEntities( LinkedList<Pair<PropertyContainer, Long>> timestamps, final boolean reversed )
{
List<Pair<PropertyContainer, Long>> sorted = new ArrayList<Pair<PropertyContainer,Long>>( timestamps );
sort( sorted, new Comparator<Pair<PropertyContainer, Long>>()
{
@Override
public int compare( Pair<PropertyContainer, Long> o1, Pair<PropertyContainer, Long> o2 )
{
return !reversed ? o1.other().compareTo( o2.other() ) : o2.other().compareTo( o1.other() );
}
} );
List<PropertyContainer> result = new ArrayList<PropertyContainer>();
for ( Pair<PropertyContainer, Long> timestamp : sorted )
{
result.add( timestamp.first() );
}
return result;
}
// ======== Tests, although private so that we can create two versions of each,
// ======== one for nodes and one for relationships
private void makeSureFirstAndLastAreReturnedCorrectly( EntityCreator<PropertyContainer> creator,
TimelineIndex<PropertyContainer> timeline ) throws Exception
{
LinkedList<Pair<PropertyContainer, Long>> timestamps = createTimestamps( creator, timeline, 223456, 12345, 432234 );
assertEquals( timestamps.get( 1 ).first(), timeline.getFirst() );
assertEquals( timestamps.getLast().first(), timeline.getLast() );
}
private void makeSureRangesAreReturnedInCorrectOrder( EntityCreator<PropertyContainer> creator,
TimelineIndex<PropertyContainer> timeline ) throws Exception
{
LinkedList<Pair<PropertyContainer, Long>> timestamps = createTimestamps( creator, timeline,
300000, 200000, 400000, 100000, 500000, 600000, 900000, 800000 );
assertEquals( sortedEntities( timestamps, false ), asCollection( timeline.getBetween( null, null ) ) );
}
private void makeSureRangesAreReturnedInCorrectReversedOrder( EntityCreator<PropertyContainer> creator,
TimelineIndex<PropertyContainer> timeline ) throws Exception
{
LinkedList<Pair<PropertyContainer, Long>> timestamps = createTimestamps( creator, timeline,
300000, 200000, 199999, 400000, 100000, 500000, 600000, 900000, 800000 );
assertEquals( sortedEntities( timestamps, true ), asCollection( timeline.getBetween( null, null, true ) ) );
}
private void makeSureUncommittedChangesAreSortedCorrectly( EntityCreator<PropertyContainer> creator,
TimelineIndex<PropertyContainer> timeline ) throws Exception
{
LinkedList<Pair<PropertyContainer, Long>> timestamps = createTimestamps( creator, timeline,
300000, 100000, 500000, 900000, 800000 );
beginTx();
timestamps.addAll( createTimestamps( creator, timeline, 40000, 70000, 20000 ) );
assertEquals( sortedEntities( timestamps, false ),
asCollection( timeline.getBetween( null, null ) ) );
commitTx();
assertEquals( sortedEntities( timestamps, false ),
asCollection( timeline.getBetween( null, null ) ) );
}
// ======== The tests
@Test
public void makeSureFirstAndLastAreReturnedCorrectlyNode() throws Exception
{
makeSureFirstAndLastAreReturnedCorrectly( nodeCreator, nodeTimeline() );
}
@Test
public void makeSureFirstAndLastAreReturnedCorrectlyRelationship() throws Exception
{
makeSureFirstAndLastAreReturnedCorrectly( relationshipCreator, relationshipTimeline() );
}
@Test
public void makeSureRangesAreReturnedInCorrectOrderNode() throws Exception
{
makeSureRangesAreReturnedInCorrectOrder( nodeCreator, nodeTimeline() );
}
@Test
public void makeSureRangesAreReturnedInCorrectOrderRelationship() throws Exception
{
makeSureRangesAreReturnedInCorrectOrder( relationshipCreator, relationshipTimeline() );
}
@Test
public void makeSureRangesAreReturnedInCorrectReversedOrderNode() throws Exception
{
makeSureRangesAreReturnedInCorrectReversedOrder( nodeCreator, nodeTimeline() );
}
@Test
public void makeSureRangesAreReturnedInCorrectReversedOrderRelationship() throws Exception
{
makeSureRangesAreReturnedInCorrectReversedOrder( relationshipCreator, relationshipTimeline() );
}
@Test
public void makeSureUncommittedChangesAreSortedCorrectlyNode() throws Exception
{
makeSureUncommittedChangesAreSortedCorrectly( nodeCreator, nodeTimeline() );
}
@Test
public void makeSureUncommittedChangesAreSortedCorrectlyRelationship() throws Exception
{
makeSureUncommittedChangesAreSortedCorrectly( relationshipCreator, relationshipTimeline() );
}
}