/* * Copyright (c) 2002-2016 "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 . */ package org.neo4j.index.timeline; import static java.util.Collections.sort; import static org.junit.Assert.assertEquals; import static org.neo4j.helpers.collection.Iterators.asCollection; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; 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.graphdb.index.Index; import org.neo4j.graphdb.index.RelationshipIndex; import org.neo4j.helpers.collection.Pair; import org.neo4j.index.lucene.LuceneTimeline; import org.neo4j.index.lucene.TimelineIndex; import org.neo4j.test.TestGraphDatabaseFactory; public class TestTimeline { private GraphDatabaseService db; @Before public void before() throws Exception { db = new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder().newGraphDatabase(); } @After public void after() { db.shutdown(); } private interface EntityCreator { T create(); } private EntityCreator nodeCreator = new EntityCreator() { @Override public Node create() { return db.createNode(); } }; private EntityCreator relationshipCreator = new EntityCreator() { private final RelationshipType type = RelationshipType.withName( "whatever" ); @Override public Relationship create() { return db.createNode().createRelationshipTo( db.createNode(), type ); } }; private TimelineIndex nodeTimeline() { try( Transaction tx = db.beginTx() ) { Index nodeIndex = db.index().forNodes( "timeline" ); tx.success(); return new LuceneTimeline( db, nodeIndex ); } } private TimelineIndex relationshipTimeline() { try( Transaction tx = db.beginTx() ) { RelationshipIndex relationshipIndex = db.index().forRelationships( "timeline" ); tx.success(); return new LuceneTimeline( db, relationshipIndex ); } } private LinkedList> createTimestamps( EntityCreator creator, TimelineIndex timeline, long... timestamps ) { try( Transaction tx = db.beginTx() ) { LinkedList> result = new LinkedList<>(); for ( long timestamp : timestamps ) { result.add( createTimestampedEntity( creator, timeline, timestamp ) ); } tx.success(); return result; } } private Pair createTimestampedEntity( EntityCreator creator, TimelineIndex timeline, long timestamp ) { T entity = creator.create(); timeline.add( entity, timestamp ); return Pair.of( entity, timestamp ); } private List sortedEntities( LinkedList> timestamps, final boolean reversed ) { List> sorted = new ArrayList<>( timestamps ); sort( sorted, new Comparator>() { @Override public int compare( Pair o1, Pair o2 ) { return !reversed ? o1.other().compareTo( o2.other() ) : o2.other().compareTo( o1.other() ); } } ); List result = new ArrayList<>(); for ( Pair 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 creator, TimelineIndex timeline ) throws Exception { LinkedList> timestamps = createTimestamps( creator, timeline, 223456, 12345, 432234 ); try( Transaction tx = db.beginTx() ) { assertEquals( timestamps.get( 1 ).first(), timeline.getFirst() ); assertEquals( timestamps.getLast().first(), timeline.getLast() ); tx.success(); } } private void makeSureRangesAreReturnedInCorrectOrder( EntityCreator creator, TimelineIndex timeline ) throws Exception { LinkedList> timestamps = createTimestamps( creator, timeline, 300000, 200000, 400000, 100000, 500000, 600000, 900000, 800000 ); try( Transaction tx = db.beginTx() ) { assertEquals( sortedEntities( timestamps, false ), asCollection( timeline.getBetween( null, null ).iterator() ) ); tx.success(); } } private void makeSureRangesAreReturnedInCorrectReversedOrder( EntityCreator creator, TimelineIndex timeline ) throws Exception { LinkedList> timestamps = createTimestamps( creator, timeline, 300000, 200000, 199999, 400000, 100000, 500000, 600000, 900000, 800000 ); try( Transaction tx = db.beginTx() ) { assertEquals( sortedEntities( timestamps, true ), asCollection( timeline.getBetween( null, null, true ).iterator() ) ); tx.success(); } } private void makeSureWeCanQueryLowerDefaultThan1970( EntityCreator creator, TimelineIndex timeline ) throws Exception { LinkedList> timestamps = createTimestamps( creator, timeline, -10000, 0, 10000 ); try( Transaction tx = db.beginTx() ) { assertEquals( sortedEntities( timestamps, true ), asCollection( timeline.getBetween( null, 10000L, true ).iterator() ) ); tx.success(); } } private void makeSureUncommittedChangesAreSortedCorrectly( EntityCreator creator, TimelineIndex timeline ) throws Exception { LinkedList> timestamps = createTimestamps( creator, timeline, 300000, 100000, 500000, 900000, 800000 ); try( Transaction tx = db.beginTx() ) { timestamps.addAll( createTimestamps( creator, timeline, 40000, 70000, 20000 ) ); assertEquals( sortedEntities( timestamps, false ), asCollection( timeline.getBetween( null, null ).iterator() ) ); tx.success(); } try( Transaction ignore = db.beginTx() ) { assertEquals( sortedEntities( timestamps, false ), asCollection( timeline.getBetween( null, null ).iterator() ) ); } } // ======== 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() ); } @Test public void makeSureWeCanQueryLowerDefaultThan1970Node() throws Exception { makeSureWeCanQueryLowerDefaultThan1970( nodeCreator, nodeTimeline() ); } @Test public void makeSureWeCanQueryLowerDefaultThan1970Relationship() throws Exception { makeSureWeCanQueryLowerDefaultThan1970( relationshipCreator, relationshipTimeline() ); } }