package org.neo4j.kernel.impl.traversal; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.graphdb.traversal.Traverser; import org.neo4j.helpers.Predicate; import org.neo4j.kernel.Traversal; import org.neo4j.kernel.Uniqueness; public class DepthPitfallGraphTest extends AbstractTestBase { /* Layout: * _(2)--__ * / \ * (1)-(3)-_ (6) * |\_ \ / * | (4)__ \/ * \_______(5) */ private static final String[] THE_WORLD_AS_WE_KNOW_IT = new String[] { "1 TO 2", "1 TO 3", "1 TO 4", "5 TO 3", "1 TO 5", "4 TO 5", "2 TO 6", "5 TO 6" }; private static final String[] NODE_UNIQUE_PATHS = new String[] { "1", "1,2", "1,2,6", "1,2,6,5", "1,2,6,5,3", "1,2,6,5,4", "1,3", "1,3,5", "1,3,5,4", "1,3,5,6", "1,3,5,6,2", "1,4", "1,4,5", "1,4,5,3", "1,4,5,6", "1,4,5,6,2", "1,5", "1,5,3", "1,5,4", "1,5,6", "1,5,6,2" }; private static final String[] RELATIONSHIP_UNIQUE_EXTRA_PATHS = new String[] { "1,2,6,5,1", "1,2,6,5,1,3", "1,2,6,5,1,3,5", "1,2,6,5,1,3,5,4", "1,2,6,5,1,3,5,4,1", "1,2,6,5,1,4", "1,2,6,5,1,4,5", "1,2,6,5,1,4,5,3", "1,2,6,5,1,4,5,3,1", "1,2,6,5,3,1", "1,2,6,5,3,1,4", "1,2,6,5,3,1,4,5", "1,2,6,5,3,1,4,5,1", "1,2,6,5,3,1,5", "1,2,6,5,3,1,5,4", "1,2,6,5,3,1,5,4,1", "1,2,6,5,4,1", "1,2,6,5,4,1,3", "1,2,6,5,4,1,3,5", "1,2,6,5,4,1,3,5,1", "1,2,6,5,4,1,5", "1,2,6,5,4,1,5,3", "1,2,6,5,4,1,5,3,1", "1,3,5,1", "1,3,5,1,2", "1,3,5,1,2,6", "1,3,5,1,2,6,5", "1,3,5,1,2,6,5,4", "1,3,5,1,2,6,5,4,1", "1,3,5,1,4", "1,3,5,1,4,5", "1,3,5,1,4,5,6", "1,3,5,1,4,5,6,2", "1,3,5,1,4,5,6,2,1", "1,3,5,4,1", "1,3,5,4,1,2", "1,3,5,4,1,2,6", "1,3,5,4,1,2,6,5", "1,3,5,4,1,2,6,5,1", "1,3,5,4,1,5", "1,3,5,4,1,5,6", "1,3,5,4,1,5,6,2", "1,3,5,4,1,5,6,2,1", "1,3,5,6,2,1", "1,3,5,6,2,1,4", "1,3,5,6,2,1,4,5", "1,3,5,6,2,1,4,5,1", "1,3,5,6,2,1,5", "1,3,5,6,2,1,5,4", "1,3,5,6,2,1,5,4,1", "1,4,5,1", "1,4,5,1,2", "1,4,5,1,2,6", "1,4,5,1,2,6,5", "1,4,5,1,2,6,5,3", "1,4,5,1,2,6,5,3,1", "1,4,5,1,3", "1,4,5,1,3,5", "1,4,5,1,3,5,6", "1,4,5,1,3,5,6,2", "1,4,5,1,3,5,6,2,1", "1,4,5,3,1", "1,4,5,3,1,2", "1,4,5,3,1,2,6", "1,4,5,3,1,2,6,5", "1,4,5,3,1,2,6,5,1", "1,4,5,3,1,5", "1,4,5,3,1,5,6", "1,4,5,3,1,5,6,2", "1,4,5,3,1,5,6,2,1", "1,4,5,6,2,1", "1,4,5,6,2,1,3", "1,4,5,6,2,1,3,5", "1,4,5,6,2,1,3,5,1", "1,4,5,6,2,1,5", "1,4,5,6,2,1,5,3", "1,4,5,6,2,1,5,3,1", "1,5,3,1", "1,5,3,1,2", "1,5,3,1,2,6", "1,5,3,1,2,6,5", "1,5,3,1,2,6,5,4", "1,5,3,1,2,6,5,4,1", "1,5,3,1,4", "1,5,3,1,4,5", "1,5,3,1,4,5,6", "1,5,3,1,4,5,6,2", "1,5,3,1,4,5,6,2,1", "1,5,4,1", "1,5,4,1,2", "1,5,4,1,2,6", "1,5,4,1,2,6,5", "1,5,4,1,2,6,5,3", "1,5,4,1,2,6,5,3,1", "1,5,4,1,3", "1,5,4,1,3,5", "1,5,4,1,3,5,6", "1,5,4,1,3,5,6,2", "1,5,4,1,3,5,6,2,1", "1,5,6,2,1", "1,5,6,2,1,3", "1,5,6,2,1,3,5", "1,5,6,2,1,3,5,4", "1,5,6,2,1,3,5,4,1", "1,5,6,2,1,4", "1,5,6,2,1,4,5", "1,5,6,2,1,4,5,3", "1,5,6,2,1,4,5,3,1" }; @BeforeClass public static void setup() { createGraph( THE_WORLD_AS_WE_KNOW_IT ); } @Test public void testSmallestPossibleInit() throws Exception { Traverser traversal = Traversal.description().traverse( referenceNode() ); int count = 0; for ( Path position : traversal ) { count++; assertNotNull( position ); assertNotNull( position.endNode() ); if ( position.length() > 0 ) { assertNotNull( position.lastRelationship() ); } assertNotNull( position.length() ); } assertFalse( "empty traversal", count == 0 ); } @Test public void testAllNodesAreReturnedOnceDepthFirst() throws Exception { testAllNodesAreReturnedOnce( Traversal.description().depthFirst() ); } @Test public void testAllNodesAreReturnedOnceBreadthFirst() throws Exception { testAllNodesAreReturnedOnce( Traversal.description().breadthFirst() ); } private void testAllNodesAreReturnedOnce( TraversalDescription traversal ) { Traverser traverser = traversal.uniqueness( Uniqueness.NODE_GLOBAL ).traverse( referenceNode() ); expectNodes( traverser, "1", "2", "3", "4", "5", "6" ); } @Test public void testNodesAreReturnedOnceWhenSufficientRecentlyUniqueDepthFirst() throws Exception { testNodesAreReturnedOnceWhenSufficientRecentlyUnique( Traversal.description().depthFirst() ); } @Test public void testNodesAreReturnedOnceWhenSufficientRecentlyUniqueBreadthFirst() throws Exception { testNodesAreReturnedOnceWhenSufficientRecentlyUnique( Traversal.description().breadthFirst() ); } private void testNodesAreReturnedOnceWhenSufficientRecentlyUnique( TraversalDescription description ) { Traverser traverser = description.uniqueness( Uniqueness.NODE_RECENT, 6 ).traverse( referenceNode() ); expectNodes( traverser, "1", "2", "3", "4", "5", "6" ); } @Test public void testAllRelationshipsAreReturnedOnceDepthFirst() throws Exception { testAllRelationshipsAreReturnedOnce( Traversal.description().depthFirst() ); } @Test public void testAllRelationshipsAreReturnedOnceBreadthFirst() throws Exception { testAllRelationshipsAreReturnedOnce( Traversal.description().breadthFirst() ); } private void testAllRelationshipsAreReturnedOnce( TraversalDescription description ) throws Exception { Traverser traverser = Traversal.description().uniqueness( Uniqueness.RELATIONSHIP_GLOBAL ).traverse( referenceNode() ); expectRelationships( traverser, THE_WORLD_AS_WE_KNOW_IT ); } @Test public void testRelationshipsAreReturnedOnceWhenSufficientRecentlyUniqueDepthFirst() throws Exception { testRelationshipsAreReturnedOnceWhenSufficientRecentlyUnique( Traversal.description().depthFirst() ); } @Test public void testRelationshipsAreReturnedOnceWhenSufficientRecentlyUniqueBreadthFirst() throws Exception { testRelationshipsAreReturnedOnceWhenSufficientRecentlyUnique( Traversal.description().breadthFirst() ); } private void testRelationshipsAreReturnedOnceWhenSufficientRecentlyUnique( TraversalDescription description ) throws Exception { Traverser traverser = description.uniqueness( Uniqueness.RELATIONSHIP_RECENT, THE_WORLD_AS_WE_KNOW_IT.length ).traverse( referenceNode() ); expectRelationships( traverser, THE_WORLD_AS_WE_KNOW_IT ); } @Test public void testAllUniqueNodePathsAreReturnedDepthFirst() throws Exception { testAllUniqueNodePathsAreReturned( Traversal.description().depthFirst() ); } @Test public void testAllUniqueNodePathsAreReturnedBreadthFirst() throws Exception { testAllUniqueNodePathsAreReturned( Traversal.description().breadthFirst() ); } private void testAllUniqueNodePathsAreReturned( TraversalDescription description ) throws Exception { Traverser traverser = description.uniqueness( Uniqueness.NODE_PATH ).traverse( referenceNode() ); expectPaths( traverser, NODE_UNIQUE_PATHS ); } @Test public void testAllUniqueRelationshipPathsAreReturnedDepthFirst() throws Exception { testAllUniqueRelationshipPathsAreReturned( Traversal.description().depthFirst() ); } @Test public void testAllUniqueRelationshipPathsAreReturnedBreadthFirst() throws Exception { testAllUniqueRelationshipPathsAreReturned( Traversal.description().breadthFirst() ); } private void testAllUniqueRelationshipPathsAreReturned( TraversalDescription description ) throws Exception { Set<String> expected = new HashSet<String>( Arrays.asList( NODE_UNIQUE_PATHS ) ); expected.addAll( Arrays.asList( RELATIONSHIP_UNIQUE_EXTRA_PATHS ) ); Traverser traverser = description.uniqueness( Uniqueness.RELATIONSHIP_PATH ).traverse( referenceNode() ); expectPaths( traverser, expected ); } @Test public void canPruneTraversalAtSpecificDepthDepthFirst() { canPruneTraversalAtSpecificDepth( Traversal.description().depthFirst() ); } @Test public void canPruneTraversalAtSpecificDepthBreadthFirst() { canPruneTraversalAtSpecificDepth( Traversal.description().breadthFirst() ); } private void canPruneTraversalAtSpecificDepth( TraversalDescription description ) { Traverser traverser = description.uniqueness( Uniqueness.NONE ).prune( Traversal.pruneAfterDepth( 1 ) ).traverse( referenceNode() ); expectNodes( traverser, "1", "2", "3", "4", "5" ); } @Test public void canPreFilterNodesDepthFirst() { canPreFilterNodes( Traversal.description().depthFirst() ); } @Test public void canPreFilterNodesBreadthFirst() { canPreFilterNodes( Traversal.description().breadthFirst() ); } private void canPreFilterNodes( TraversalDescription description ) { Traverser traverser = description.uniqueness( Uniqueness.NONE ).prune( Traversal.pruneAfterDepth( 2 ) ).filter( new Predicate<Path>() { public boolean accept( Path position ) { return position.length() == 2; } } ).traverse( referenceNode() ); expectPaths( traverser, "1,2,6", "1,3,5", "1,4,5", "1,5,3", "1,5,4", "1,5,6" ); } }