private List<Long> asList( NodeValueIndexCursor cursor ) { List<Long> list = new ArrayList<>(); while ( cursor.next() ) { list.add( cursor.nodeReference() ); } return list; }
@Test public void shouldHoldSharedIndexLockIfNodeIsExists() throws Exception { // given NodeValueIndexCursor cursor = mock( NodeValueIndexCursor.class ); when( cursor.next() ).thenReturn( true ); when( cursor.nodeReference() ).thenReturn( 42L ); // when long nodeId = LockingNodeUniqueIndexSeek.apply( locks, LockTracer.NONE, () -> cursor, uniqueNodeIndexSeeker, read, index, predicate ); // then assertEquals( 42L, nodeId ); verify( locks ).acquireShared( LockTracer.NONE, INDEX_ENTRY, resourceId ); verifyNoMoreInteractions( locks ); verify( cursor ).close(); }
@Test public void shouldHoldExclusiveIndexLockIfNodeDoesNotExist() throws Exception { // given NodeValueIndexCursor cursor = mock( NodeValueIndexCursor.class ); when( cursor.next() ).thenReturn( false, false ); when( cursor.nodeReference() ).thenReturn( -1L ); // when long nodeId = LockingNodeUniqueIndexSeek.apply( locks, LockTracer.NONE, () -> cursor, uniqueNodeIndexSeeker, read, index, predicate ); // then assertEquals( -1L, nodeId ); order.verify( locks ).acquireShared( LockTracer.NONE, INDEX_ENTRY, resourceId ); order.verify( locks ).releaseShared( INDEX_ENTRY, resourceId ); order.verify( locks ).acquireExclusive( LockTracer.NONE, INDEX_ENTRY, resourceId ); verifyNoMoreInteractions( locks ); verify( cursor ).close(); } }
@Test public void shouldHoldSharedIndexLockIfNodeIsConcurrentlyCreated() throws Exception { // given NodeValueIndexCursor cursor = mock( NodeValueIndexCursor.class ); when( cursor.next() ).thenReturn( false, true ); when( cursor.nodeReference() ).thenReturn( 42L ); // when long nodeId = LockingNodeUniqueIndexSeek.apply( locks, LockTracer.NONE, () -> cursor, uniqueNodeIndexSeeker, read, index, predicate ); // then assertEquals( 42L, nodeId ); order.verify( locks ).acquireShared( LockTracer.NONE, INDEX_ENTRY, resourceId ); order.verify( locks ).releaseShared( INDEX_ENTRY, resourceId ); order.verify( locks ).acquireExclusive( LockTracer.NONE, INDEX_ENTRY, resourceId ); order.verify( locks ).acquireShared( LockTracer.NONE, INDEX_ENTRY, resourceId ); order.verify( locks ).releaseExclusive( INDEX_ENTRY, resourceId ); verifyNoMoreInteractions( locks ); verify( cursor ).close(); }
@Test public void shouldSeeAllNodesAddedInTransaction() throws Exception { if ( index.type() != UNIQUE ) // this test does not make any sense for UNIQUE indexes { try ( Transaction ignore = graphDatabaseAPI.beginTx() ) { long nodeID1 = createNode(); long nodeID2 = createNode(); long nodeID3 = createNode(); KernelTransaction ktx = ktx(); Set<Long> result = new HashSet<>( ); try ( NodeValueIndexCursor cursor = seek( ktx ) ) { while ( cursor.next() ) { result.add( cursor.nodeReference() ); } } assertThat( result, containsInAnyOrder( nodeID1, nodeID2, nodeID3 ) ); } } }
@Test public void shouldSeeAllNodesAddedBeforeTransaction() throws Exception { if ( index.type() != UNIQUE ) // this test does not make any sense for UNIQUE indexes { long nodeID1 = createNode(); long nodeID2 = createNode(); long nodeID3 = createNode(); try ( Transaction ignore = graphDatabaseAPI.beginTx() ) { KernelTransaction ktx = ktx(); Set<Long> result = new HashSet<>( ); try ( NodeValueIndexCursor cursor = seek( ktx ) ) { while ( cursor.next() ) { result.add( cursor.nodeReference() ); } } assertThat( result, containsInAnyOrder( nodeID1, nodeID2, nodeID3 ) ); } } }
private void assertResultsInOrder( List<Pair<Long,Value>> expected, NodeValueIndexCursor cursor ) { Comparator<Pair<Long,Value>> comparator = indexOrder == IndexOrder.ASCENDING ? ( a, b ) -> Values.COMPARATOR.compare( a.other(), b.other() ) : ( a, b ) -> Values.COMPARATOR.compare( b.other(), a.other() ); expected.sort( comparator ); Iterator<Pair<Long,Value>> expectedRows = expected.iterator(); while ( cursor.next() && expectedRows.hasNext() ) { Pair<Long, Value> expectedRow = expectedRows.next(); assertThat( cursor.nodeReference(), equalTo( expectedRow.first() ) ); for ( int i = 0; i < cursor.numberOfProperties(); i++ ) { Value value = cursor.propertyValue( i ); assertThat( value, equalTo( expectedRow.other() ) ); } } assertFalse( expectedRows.hasNext() ); assertFalse( cursor.next() ); }
@Test public void multipleIteratorsNestedInnerNewExists() throws Exception { try ( Transaction tx = db.beginTx() ) { // when KernelTransaction ktx = db.transaction(); try ( NodeValueIndexCursor cursor1 = indexCoordinator.queryExists( ktx ) ) { List<Long> actual1 = new ArrayList<>(); while ( cursor1.next() ) { actual1.add( cursor1.nodeReference() ); try ( NodeValueIndexCursor cursor2 = indexCoordinator.queryExists( ktx ) ) { List<Long> actual2 = asList( cursor2 ); indexCoordinator.assertExistsResult( actual2 ); } } // then indexCoordinator.assertExistsResult( actual1 ); } tx.success(); } }
@Test public void multipleIteratorsNestedInnerNewExact() throws Exception { try ( Transaction tx = db.beginTx() ) { // when KernelTransaction ktx = db.transaction(); try ( NodeValueIndexCursor cursor1 = indexCoordinator.queryExact( ktx ) ) { List<Long> actual1 = new ArrayList<>(); while ( cursor1.next() ) { actual1.add( cursor1.nodeReference() ); try ( NodeValueIndexCursor cursor2 = indexCoordinator.queryExact( ktx ) ) { List<Long> actual2 = asList( cursor2 ); indexCoordinator.assertExactResult( actual2 ); } } // then indexCoordinator.assertExactResult( actual1 ); } tx.success(); } }
@Test public void multipleIteratorsNestedInnerNewRange() throws Exception { Assume.assumeTrue( indexCoordinator.supportRangeQuery() ); try ( Transaction tx = db.beginTx() ) { // when KernelTransaction ktx = db.transaction(); try ( NodeValueIndexCursor cursor1 = indexCoordinator.queryRange( ktx ) ) { List<Long> actual1 = new ArrayList<>(); while ( cursor1.next() ) { actual1.add( cursor1.nodeReference() ); try ( NodeValueIndexCursor cursor2 = indexCoordinator.queryRange( ktx ) ) { List<Long> actual2 = asList( cursor2 ); indexCoordinator.assertRangeResult( actual2 ); } } // then indexCoordinator.assertRangeResult( actual1 ); } tx.success(); } }
private void assertFoundNodesAndNoValue( NodeValueIndexCursor node, int nodes, MutableLongSet uniqueIds ) { uniqueIds.clear(); for ( int i = 0; i < nodes; i++ ) { assertTrue( "at least " + nodes + " nodes, was " + uniqueIds.size(), node.next() ); long nodeReference = node.nodeReference(); assertTrue( "all nodes are unique", uniqueIds.add( nodeReference ) ); // We can't quite assert !node.hasValue() because even tho pure SpatialIndexReader is guaranteed to not return any values, // where null could be used, the generic native index, especially when having composite keys including spatial values it's // more of a gray area and some keys may be spatial, some not and therefore a proper Value[] will be extracted // potentially containing some NO_VALUE values. } assertFalse( "no more than " + nodes + " nodes", node.next() ); }
@Test public void shouldSeeNodeAddedByPropertyToIndexInTranslation() throws Exception { try ( Transaction ignore = graphDatabaseAPI.beginTx() ) { KernelTransaction ktx = ktx(); Write write = ktx.dataWrite(); long nodeID = write.nodeCreate(); write.nodeAddLabel( nodeID, LABEL_ID ); for ( int propID : index.schema().getPropertyIds() ) { write.nodeSetProperty( nodeID, propID, Values.intValue( propID ) ); } try ( NodeValueIndexCursor cursor = seek( ktx ) ) { assertTrue( cursor.next() ); assertThat( cursor.nodeReference(), equalTo( nodeID ) ); assertFalse( cursor.next() ); } } }
@Test public void shouldSeeNodeAddedToByLabelIndexInTransaction() throws Exception { try ( Transaction ignore = graphDatabaseAPI.beginTx() ) { KernelTransaction ktx = ktx(); Write write = ktx.dataWrite(); long nodeID = write.nodeCreate(); for ( int propID : index.schema().getPropertyIds() ) { write.nodeSetProperty( nodeID, propID, Values.intValue( propID ) ); } write.nodeAddLabel( nodeID, LABEL_ID ); try ( NodeValueIndexCursor cursor = seek( ktx ) ) { assertTrue( cursor.next() ); assertThat( cursor.nodeReference(), equalTo( nodeID ) ); assertFalse( cursor.next() ); } } }
@Test public void shouldFindUpdatedNodeInIndexSeek() throws Exception { // Given boolean needsValues = false; int label = token.nodeLabel( "Node" ); int prop = token.propertyKey( "prop" ); IndexReference index = schemaRead.index( label, prop ); try ( org.neo4j.internal.kernel.api.Transaction tx = beginTransaction(); NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() ) { // when tx.dataWrite().nodeSetProperty( strOne, prop, stringValue( "ett" ) ); tx.dataRead().nodeIndexSeek( index, node, IndexOrder.NONE, needsValues, IndexQuery.exact( prop, "ett" ) ); // then assertTrue( node.next() ); assertEquals( strOne, node.nodeReference() ); } }
@Test public void shouldFindSwappedNodeInIndexSeek() throws Exception { // Given boolean needsValues = false; int label = token.nodeLabel( "Node" ); int prop = token.propertyKey( "prop" ); IndexReference index = schemaRead.index( label, prop ); try ( org.neo4j.internal.kernel.api.Transaction tx = beginTransaction(); NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() ) { // when tx.dataWrite().nodeRemoveLabel( strOne, label ); tx.dataWrite().nodeAddLabel( strOneNoLabel, label ); tx.dataRead().nodeIndexSeek( index, node, IndexOrder.NONE, needsValues, IndexQuery.exact( prop, "one" ) ); // then assertTrue( node.next() ); assertEquals( strOneNoLabel, node.nodeReference() ); } }
@Test public void shouldFindUpdatedNodeInRangeSearch() throws Exception { // Given boolean needsValues = indexProvidesStringValues(); int label = token.nodeLabel( "Node" ); int prop = token.propertyKey( "prop" ); IndexReference index = schemaRead.index( label, prop ); try ( org.neo4j.internal.kernel.api.Transaction tx = beginTransaction(); NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() ) { // when tx.dataWrite().nodeSetProperty( strOne, prop, stringValue( "ett" ) ); tx.dataRead().nodeIndexSeek( index, node, IndexOrder.NONE, needsValues, IndexQuery.range( prop, "ett", true, "tre", true ) ); // then assertTrue( node.next() ); assertEquals( strOne, node.nodeReference() ); } }
@Test public void shouldFindSwappedNodeInPrefixSearch() throws Exception { // Given boolean needsValues = indexProvidesStringValues(); int label = token.nodeLabel( "Node" ); int prop = token.propertyKey( "prop" ); IndexReference index = schemaRead.index( label, prop ); try ( org.neo4j.internal.kernel.api.Transaction tx = beginTransaction(); NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() ) { // when tx.dataWrite().nodeRemoveLabel( strOne, label ); tx.dataWrite().nodeAddLabel( strOneNoLabel, label ); tx.dataRead().nodeIndexSeek( index, node, IndexOrder.NONE, needsValues, IndexQuery.stringPrefix( prop, stringValue( "on" )) ); // then assertTrue( node.next() ); assertEquals( strOneNoLabel, node.nodeReference() ); } }
@Test public void shouldFindUpdatedNodeInPrefixSearch() throws Exception { // Given boolean needsValues = indexProvidesStringValues(); int label = token.nodeLabel( "Node" ); int prop = token.propertyKey( "prop" ); IndexReference index = schemaRead.index( label, prop ); try ( org.neo4j.internal.kernel.api.Transaction tx = beginTransaction(); NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() ) { // when tx.dataWrite().nodeSetProperty( strOne, prop, stringValue( "ett" ) ); tx.dataRead().nodeIndexSeek( index, node, IndexOrder.NONE, needsValues, IndexQuery.stringPrefix( prop, stringValue( "et" ) ) ); // then assertTrue( node.next() ); assertEquals( strOne, node.nodeReference() ); } }
@Test public void shouldFindSwappedNodeInRangeSearch() throws Exception { // Given boolean needsValues = indexProvidesStringValues(); int label = token.nodeLabel( "Node" ); int prop = token.propertyKey( "prop" ); IndexReference index = schemaRead.index( label, prop ); try ( org.neo4j.internal.kernel.api.Transaction tx = beginTransaction(); NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() ) { // when tx.dataWrite().nodeRemoveLabel( strOne, label ); tx.dataWrite().nodeAddLabel( strOneNoLabel, label ); tx.dataRead().nodeIndexSeek( index, node, IndexOrder.NONE, needsValues, IndexQuery.range( prop, "one", true, "ones", true ) ); // then assertTrue( node.next() ); assertEquals( strOneNoLabel, node.nodeReference() ); assertFalse( node.next() ); } }
@Test public void shouldFindUpdatedNodeInCompositeIndex() throws Exception { // Given boolean needsValues = false; int label = token.nodeLabel( "Person" ); int firstName = token.propertyKey( "firstname" ); int surname = token.propertyKey( "surname" ); IndexReference index = schemaRead.index( label, firstName, surname ); try ( org.neo4j.internal.kernel.api.Transaction tx = beginTransaction(); NodeValueIndexCursor node = cursors.allocateNodeValueIndexCursor() ) { // when tx.dataWrite().nodeSetProperty( jackDalton, firstName, stringValue( "Jesse" ) ); tx.dataWrite().nodeSetProperty( jackDalton, surname, stringValue( "James" ) ); tx.dataRead().nodeIndexSeek( index, node, IndexOrder.NONE, needsValues, IndexQuery.exact( firstName, "Jesse" ), IndexQuery.exact( surname, "James" ) ); // then assertTrue( node.next() ); assertEquals( jackDalton, node.nodeReference() ); } }