diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/NeighbourCursor.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/NeighbourCursor.java deleted file mode 100644 index 16b693cfc2421..0000000000000 --- a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/NeighbourCursor.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (c) 2002-2017 "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.internal.kernel.api; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * This is an example of an abstraction on top of {@link RelationshipScanCursor} and {@link RelationshipGroupCursor}. - */ -public abstract class NeighbourCursor implements NodeCursor -{ - public static Builder outgoing( CursorFactory cursors, int label ) - { - return new Builder( cursors ).outgoing( label ); - } - - public static Builder outgoing( CursorFactory cursors, int label, PropertyPredicate relationshipProperties ) - { - return new Builder( cursors ).outgoing( label, relationshipProperties ); - } - - public static Builder incoming( CursorFactory cursors, int label ) - { - return new Builder( cursors ).incoming( label ); - } - - public static Builder incoming( CursorFactory cursors, int label, PropertyPredicate relationshipProperties ) - { - return new Builder( cursors ).incoming( label, relationshipProperties ); - } - - public static Builder any( CursorFactory cursors, int label ) - { - return new Builder( cursors ).any( label ); - } - - public static Builder any( CursorFactory cursors, int label, PropertyPredicate relationshipProperties ) - { - return new Builder( cursors ).any( label, relationshipProperties ); - } - - public static NeighbourCursor outgoing( CursorFactory cursors ) - { - return new AnyLabel( cursors, true, false ); - } - - public static NeighbourCursor outgoing( CursorFactory cursors, PropertyPredicate relationshipProperties ) - { - Objects.requireNonNull( relationshipProperties, "Relationship PropertyPredicate" ); - return new FilteringAnyLabel( cursors, true, false, relationshipProperties ); - } - - public static NeighbourCursor incoming( CursorFactory cursors ) - { - return new AnyLabel( cursors, false, true ); - } - - public static NeighbourCursor incoming( CursorFactory cursors, PropertyPredicate relationshipProperties ) - { - Objects.requireNonNull( relationshipProperties, "Relationship PropertyPredicate" ); - return new FilteringAnyLabel( cursors, false, true, relationshipProperties ); - } - - public static NeighbourCursor any( CursorFactory cursors ) - { - return new AnyLabel( cursors, true, true ); - } - - public static NeighbourCursor any( CursorFactory cursors, PropertyPredicate relationshipProperties ) - { - Objects.requireNonNull( relationshipProperties, "Relationship PropertyPredicate" ); - return new FilteringAnyLabel( cursors, true, true, relationshipProperties ); - } - - public static final class Builder - { - public NeighbourCursor build() - { - Entry[] entries = alternatives.values().toArray( OF_ENTRIES ); - Arrays.sort( entries ); - if ( filterProperties ) - { - return new FilteringExplicitLabels( cursors, entries ); - } - else - { - return new ExplicitLabels( cursors, entries ); - } - } - - public Builder outgoing( int label ) - { - return outgoing( label, NO_FILTER ); - } - - public Builder outgoing( int label, PropertyPredicate relationshipProperties ) - { - if ( Objects.requireNonNull( relationshipProperties, "Relationship PropertyPredicate" ) != NO_FILTER ) - { - filterProperties = true; - } - alternatives.compute( label, ( key, entry ) -> - { - if ( entry == null ) - { - entry = new Entry( key ); - } - entry.outgoing( relationshipProperties ); - return entry; - } ); - return this; - } - - public Builder incoming( int label ) - { - return incoming( label, NO_FILTER ); - } - - public Builder incoming( int label, PropertyPredicate relationshipProperties ) - { - if ( Objects.requireNonNull( relationshipProperties, "Relationship PropertyPredicate" ) != NO_FILTER ) - { - filterProperties = true; - } - alternatives.compute( label, ( key, entry ) -> - { - if ( entry == null ) - { - entry = new Entry( key ); - } - entry.incoming( relationshipProperties ); - return entry; - } ); - return this; - } - - public Builder any( int label ) - { - return any( label, NO_FILTER ); - } - - public Builder any( int label, PropertyPredicate relationshipProperties ) - { - if ( Objects.requireNonNull( relationshipProperties, "Relationship PropertyPredicate" ) != NO_FILTER ) - { - filterProperties = true; - } - alternatives.compute( label, ( key, entry ) -> - { - if ( entry == null ) - { - entry = new Entry( key ); - } - entry.any( relationshipProperties ); - return entry; - } ); - return this; - } - - private static final Entry[] OF_ENTRIES = {}; - private static final PropertyPredicate NO_FILTER = new PropertyPredicate() - { - }; - private final Map alternatives = new HashMap<>(); - private boolean filterProperties; - private final CursorFactory cursors; - - private Builder( CursorFactory cursors ) - { - this.cursors = cursors; - } - - private static final class Entry implements Comparable - { - final int label; - PropertyPredicate incoming, outgoing, any; - - Entry( int label ) - { - this.label = label; - } - - @Override - public int compareTo( Entry that ) - { - return this.label - that.label; - } - - void outgoing( PropertyPredicate predicate ) - { - if ( any != null || outgoing != null ) - { - throw new IllegalStateException( "Already specified" ); - } - if ( incoming == predicate ) - { - any = predicate; - incoming = null; - } - else - { - outgoing = predicate; - } - } - - void incoming( PropertyPredicate predicate ) - { - if ( any != null || incoming != null ) - { - throw new IllegalStateException( "Already specified" ); - } - if ( outgoing == predicate ) - { - any = predicate; - outgoing = null; - } - else - { - incoming = predicate; - } - } - - void any( PropertyPredicate predicate ) - { - if ( any != null || outgoing != null || incoming != null ) - { - throw new IllegalStateException( "Already specified" ); - } - any = predicate; - } - } - } - - private final NodeCursor neighbours; - private final RelationshipGroupCursor group; - private final RelationshipTraversalCursor relationships; - - public NeighbourCursor( CursorFactory cursors ) - { - this.neighbours = cursors.allocateNodeCursor(); - this.group = cursors.allocateRelationshipGroupCursor(); - this.relationships = cursors.allocateRelationshipTraversalCursor(); - } - - /** - * Initialize this cursor to traverse the neighbours of the node currently under the specified cursor. - * - * @param node - * a cursor pointing at the node to get the neighbours for. - */ - public final void of( NodeCursor node ) - { - node.relationships( group ); - // make sure these don't have state from previous use - group.close(); - relationships.close(); - } - - @Override - public final boolean next() - { - while ( !relationships.next() ) - { - if ( !next( group, relationships ) ) - { - return false; - } - } - do - { - relationships.neighbour( neighbours ); - } - while ( relationships.shouldRetry() ); - return neighbours.next(); - } - - protected abstract boolean next( RelationshipGroupCursor group, RelationshipTraversalCursor relationships ); - - @Override - public final boolean shouldRetry() - { - return neighbours.shouldRetry(); - } - - @Override - public final void close() - { - neighbours.close(); - group.close(); - relationships.close(); - } - - @Override - public final long nodeReference() - { - return neighbours.nodeReference(); - } - - @Override - public final LabelSet labels() - { - return neighbours.labels(); - } - - @Override - public final boolean hasProperties() - { - return neighbours.hasProperties(); - } - - @Override - public final void relationships( RelationshipGroupCursor cursor ) - { - neighbours.relationships( cursor ); - } - - @Override - public void outgoingRelationships( RelationshipGroupCursor groups, RelationshipTraversalCursor relationships ) - { - neighbours.outgoingRelationships( groups, relationships ); - } - - @Override - public void incomingRelationships( RelationshipGroupCursor groups, RelationshipTraversalCursor relationships ) - { - neighbours.incomingRelationships( groups, relationships ); - } - - @Override - public void allRelationships( RelationshipGroupCursor groups, RelationshipTraversalCursor relationships ) - { - neighbours.allRelationships( groups, relationships ); - } - - @Override - public final void properties( PropertyCursor cursor ) - { - neighbours.properties( cursor ); - } - - @Override - public final long relationshipGroupReference() - { - return neighbours.relationshipGroupReference(); - } - - @Override - public final long propertiesReference() - { - return neighbours.propertiesReference(); - } - - private static final class AnyLabel extends NeighbourCursor - { - private final boolean outgoing; - private final boolean incoming; - - AnyLabel( CursorFactory cursors, boolean outgoing, boolean incoming ) - { - super( cursors ); - this.outgoing = outgoing; - this.incoming = incoming; - } - - @Override - protected boolean next( RelationshipGroupCursor group, RelationshipTraversalCursor relationships ) - { - throw new UnsupportedOperationException( "not implemented" ); - } - } - - private static final class FilteringAnyLabel extends NeighbourCursor - { - private final PropertyCursor properties; - private final boolean outgoing; - private final boolean incoming; - private final PropertyPredicate predicate; - - FilteringAnyLabel( CursorFactory cursors, boolean outgoing, boolean incoming, PropertyPredicate predicate ) - { - super( cursors ); - this.properties = cursors.allocatePropertyCursor(); - this.outgoing = outgoing; - this.incoming = incoming; - this.predicate = predicate; - } - - @Override - protected boolean next( RelationshipGroupCursor group, RelationshipTraversalCursor relationships ) - { - throw new UnsupportedOperationException( "not implemented" ); - } - } - - private static final class ExplicitLabels extends NeighbourCursor - { - ExplicitLabels( CursorFactory cursors, Builder.Entry[] spec ) - { - super( cursors ); - } - - @Override - protected boolean next( RelationshipGroupCursor group, RelationshipTraversalCursor relationships ) - { - throw new UnsupportedOperationException( "not implemented" ); - } - } - - private static final class FilteringExplicitLabels extends NeighbourCursor - { - private final PropertyCursor properties; - - FilteringExplicitLabels( CursorFactory cursors, Builder.Entry[] spec ) - { - super( cursors ); - this.properties = cursors.allocatePropertyCursor(); - } - - @Override - protected boolean next( RelationshipGroupCursor group, RelationshipTraversalCursor relationships ) - { - throw new UnsupportedOperationException( "not implemented" ); - } - } -} diff --git a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTraversalCursorTestBase.java b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTraversalCursorTestBase.java index 59b33ae1e7fef..abc9f40c2ce89 100644 --- a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTraversalCursorTestBase.java +++ b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTraversalCursorTestBase.java @@ -483,7 +483,10 @@ private void count( RelationshipTraversalCursor relationship, Map value == null ? 1 : value + 1 ); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java index ecb2a9536d39c..a4a700b65cbfe 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java @@ -53,6 +53,7 @@ public class Read implements org.neo4j.internal.kernel.api.Read { + static final long FILTER_MASK = 0x2000_0000_0000_0000L; private final RelationshipGroupStore groupStore; private final PropertyStore propertyStore; private NodeStore nodeStore; @@ -198,6 +199,10 @@ else if ( reference < NO_ID ) // this reference is actually to a group record { ((RelationshipTraversalCursor) cursor).groups( nodeReference, invertReference( reference ) ); } + else if ( needsFiltering( reference ) ) + { + ((RelationshipTraversalCursor) cursor).filtered( nodeReference, removeFilteringFlag( reference ) ); + } else // this is a normal relationship reference { ((RelationshipTraversalCursor) cursor).chain( nodeReference, reference ); @@ -308,14 +313,16 @@ long relationshipHighMark() TextValue string( PropertyCursor cursor, long reference, PageCursor page ) { - ByteBuffer buffer = cursor.buffer = readDynamic( propertyStore.getStringStore(), reference, cursor.buffer, page ); + ByteBuffer buffer = + cursor.buffer = readDynamic( propertyStore.getStringStore(), reference, cursor.buffer, page ); buffer.flip(); return Values.stringValue( UTF8.decode( buffer.array(), 0, buffer.limit() ) ); } ArrayValue array( PropertyCursor cursor, long reference, PageCursor page ) { - ByteBuffer buffer = cursor.buffer = readDynamic( propertyStore.getArrayStore(), reference, cursor.buffer, page ); + ByteBuffer buffer = + cursor.buffer = readDynamic( propertyStore.getArrayStore(), reference, cursor.buffer, page ); buffer.flip(); return PropertyUtil.readArrayFromBuffer( buffer ); } @@ -331,8 +338,7 @@ ArrayValue array( PropertyCursor cursor, long reference, PageCursor page ) *

* This function is its own inverse function. * - * @param reference - * the reference to invert. + * @param reference the reference to invert. * @return the inverted reference. */ static long invertReference( long reference ) @@ -340,7 +346,24 @@ static long invertReference( long reference ) return -2 - reference; } - private static ByteBuffer readDynamic( AbstractDynamicStore store, long reference, ByteBuffer buffer, PageCursor page ) + static long addFilteringFlag( long reference ) + { + // set a high order bit as flag noting that "filtering is required" + return reference | FILTER_MASK; + } + + static long removeFilteringFlag( long reference ) + { + return reference & ~FILTER_MASK; + } + + static boolean needsFiltering( long reference ) + { + return (reference & FILTER_MASK) != 0L; + } + + private static ByteBuffer readDynamic( AbstractDynamicStore store, long reference, ByteBuffer buffer, + PageCursor page ) { if ( buffer == null ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipGroupCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipGroupCursor.java index c7f6f21fb3883..826a50b92452d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipGroupCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipGroupCursor.java @@ -26,7 +26,7 @@ import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; import org.neo4j.kernel.impl.store.record.RelationshipRecord; -import static org.neo4j.kernel.impl.newapi.Read.invertReference; +import static org.neo4j.kernel.impl.newapi.Read.addFilteringFlag; class RelationshipGroupCursor extends RelationshipGroupRecord implements org.neo4j.internal.kernel.api.RelationshipGroupCursor @@ -322,24 +322,18 @@ void loop( RelationshipRecord edge ) long outgoing() { - return flaggedAsRequiringFiltering( firstOut ); + return addFilteringFlag( firstOut ); } long incoming() { - return flaggedAsRequiringFiltering( firstIn ); + return addFilteringFlag( firstIn ); } long loops() { - return flaggedAsRequiringFiltering( firstLoop ); + return addFilteringFlag( firstLoop ); } - private long flaggedAsRequiringFiltering( long reference ) - { - // set a high order bit as flag noting that "filtering is required" - // invert the reference to note that "this reference is special" - return invertReference( reference | 0x2000_0000_0000_0000L ); - } } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipTraversalCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipTraversalCursor.java index 9df7f706495ae..6dd63fa06b28b 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipTraversalCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipTraversalCursor.java @@ -23,15 +23,63 @@ import org.neo4j.io.pagecache.PageCursor; import org.neo4j.kernel.impl.store.record.RelationshipRecord; -import static org.neo4j.kernel.impl.newapi.RelationshipTraversalCursor.GroupState.INCOMING; -import static org.neo4j.kernel.impl.newapi.RelationshipTraversalCursor.GroupState.LOOP; -import static org.neo4j.kernel.impl.newapi.RelationshipTraversalCursor.GroupState.NONE; -import static org.neo4j.kernel.impl.newapi.RelationshipTraversalCursor.GroupState.OUTGOING; class RelationshipTraversalCursor extends RelationshipCursor implements org.neo4j.internal.kernel.api.RelationshipTraversalCursor { - enum GroupState { INCOMING, OUTGOING, LOOP, NONE }; + private enum GroupState + { + INCOMING, + OUTGOING, + LOOP, + NONE + } + + private enum FilterState + { + NOT_INITIALIZED + { + @Override + boolean check( long source, long target, long origin ) + { + throw new IllegalStateException( "Cannot call check on uninitialized filter" ); + } + }, + INCOMING + { + @Override + boolean check( long source, long target, long origin ) + { + return origin == target; + } + }, + OUTGOING + { + @Override + boolean check( long source, long target, long origin ) + { + return origin == source; + } + }, + LOOP + { + @Override + boolean check( long source, long target, long origin ) + { + return source == target; + } + }, + NONE + { + @Override + boolean check( long source, long target, long origin ) + { + return true; + } + }; + + abstract boolean check( long source, long target, long origin ); + } private long originNodeReference; private long next; @@ -39,7 +87,8 @@ enum GroupState { INCOMING, OUTGOING, LOOP, NONE }; private PageCursor pageCursor; private final RelationshipGroupCursor group; private GroupState groupState; - + private FilterState filterState; + private int filterType = NO_ID; RelationshipTraversalCursor( Read read ) { @@ -55,6 +104,9 @@ void buffered( long nodeReference, Record record ) { this.originNodeReference = nodeReference; this.buffer = Record.initialize( record ); + this.groupState = GroupState.NONE; + this.filterState = FilterState.NONE; + this.filterType = NO_ID; } /* @@ -67,7 +119,9 @@ void chain( long nodeReference, long reference ) pageCursor = read.relationshipPage( reference ); } setId( NO_ID ); - groupState = NONE; + groupState = GroupState.NONE; + filterState = FilterState.NONE; + filterType = NO_ID; originNodeReference = nodeReference; next = reference; } @@ -79,16 +133,24 @@ void groups( long nodeReference, long groupReference ) { setId( NO_ID ); next = NO_ID; - groupState = INCOMING; + groupState = GroupState.INCOMING; + filterState = FilterState.NONE; + filterType = NO_ID; originNodeReference = nodeReference; read.relationshipGroups( nodeReference, groupReference, group ); } void filtered( long nodeReference, long reference ) { - // TODO: read the first record and use the type of it for filtering the chain - // - only include records with that type - throw new UnsupportedOperationException( "not implemented" ); + if ( pageCursor == null ) + { + pageCursor = read.relationshipPage( reference ); + } + setId( NO_ID ); + groupState = GroupState.NONE; + filterState = FilterState.NOT_INITIALIZED; + originNodeReference = nodeReference; + next = reference; } @Override @@ -136,33 +198,35 @@ public long originNodeReference() @Override public boolean next() { - /* - Node(dense=true) - | - v - - Group(:HOLDS) -incoming-> Rel(id=2) -> Rel(id=3) - -outgoing-> Rel(id=5) -> Rel(id=10) -> Rel(id=3) - -loop-> Rel(id=9) - | - v - - Group(:USES) -incoming-> Rel(id=14) - -outgoing-> Rel(id=55) -> Rel(id=51) -> ... - -loop-> Rel(id=21) -> Rel(id=11) - - | - v - ... - - */ if ( traversingDenseNode() ) { + while ( next == NO_ID ) { - /* - Defines a small state machine, we start in state INCOMING. + /* + Dense nodes looks something like: + + Node(dense=true) + + | + v + + Group(:HOLDS) -incoming-> Rel(id=2) -> Rel(id=3) + -outgoing-> Rel(id=5) -> Rel(id=10) -> Rel(id=3) + -loop-> Rel(id=9) + | + v + + Group(:USES) -incoming-> Rel(id=14) + -outgoing-> Rel(id=55) -> Rel(id=51) -> ... + -loop-> Rel(id=21) -> Rel(id=11) + + | + v + ... + + We iterate over dense nodes using a small state machine staring in state INCOMING. 1) fetch next group, if no more group stop. 2) set next to group.incomingReference, switch state to OUTGOING 3) Iterate relationship chain until we reach the end @@ -186,17 +250,17 @@ public boolean next() { pageCursor = read.relationshipPage( Math.max( next, 0L ) ); } - groupState = OUTGOING; + groupState = GroupState.OUTGOING; break; case OUTGOING: next = group.outgoingReference(); - groupState = LOOP; + groupState = GroupState.LOOP; break; case LOOP: next = group.loopsReference(); - groupState = INCOMING; + groupState = GroupState.INCOMING; break; default: @@ -216,11 +280,7 @@ public boolean next() else { // Copy buffer data to self - this.setId( buffer.id ); - this.setType( buffer.type ); - this.setNextProp( buffer.nextProp ); - this.setFirstNode( buffer.firstNode ); - this.setSecondNode( buffer.secondNode ); + copyFromBuffer(); return true; } } @@ -231,7 +291,66 @@ public boolean next() reset(); return false; } + + if ( filteringTraversal() ) + { + if ( filterState == FilterState.NOT_INITIALIZED ) + { + //Initialize filtering: + // - Read first record + // - Check type and direction + // - Subsequent records need to have same type and direction + read.relationship( this, next, pageCursor ); + filterType = getType(); + final long source = sourceNodeReference(), target = targetNodeReference(); + if ( source == target ) + { + next = getFirstNextRel(); + filterState = FilterState.LOOP; + } + else if ( source == originNodeReference ) + { + next = getFirstNextRel(); + filterState = FilterState.OUTGOING; + } + else if ( target == originNodeReference ) + { + next = getSecondNextRel(); + filterState = FilterState.INCOMING; + } + return true; + } + else + { + //Iterate until we stop on a valid record, + //i.e. one with the same type and direction. + while ( true ) + { + read.relationship( this, next, pageCursor ); + computeNext(); + if ( predicate() ) + { + return true; + } + if ( next == NO_ID ) + { + reset(); + return false; + } + + } + } + } + + //Not a group, nothing buffered, no filtering. + //Just a plain old traversal. read.relationship( this, next, pageCursor ); + computeNext(); + return true; + } + + private void computeNext() + { final long source = sourceNodeReference(), target = targetNodeReference(); if ( source == originNodeReference ) { @@ -245,12 +364,31 @@ else if ( target == originNodeReference ) { throw new IllegalStateException( "NOT PART OF CHAIN" ); } - return true; + } + + private boolean predicate() + { + return filterType == getType() && + filterState.check( sourceNodeReference(), targetNodeReference(), originNodeReference ); + } + + private void copyFromBuffer() + { + this.setId( buffer.id ); + this.setType( buffer.type ); + this.setNextProp( buffer.nextProp ); + this.setFirstNode( buffer.firstNode ); + this.setSecondNode( buffer.secondNode ); } private boolean traversingDenseNode() { - return groupState != NONE; + return groupState != GroupState.NONE; + } + + private boolean filteringTraversal() + { + return filterState != FilterState.NONE; } @Override @@ -273,7 +411,9 @@ public void close() private void reset() { setId( next = NO_ID ); - groupState = NONE; + groupState = GroupState.NONE; + filterState = FilterState.NONE; + filterType = NO_ID; buffer = null; } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/NodeRecord.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/NodeRecord.java index 093c05f1a772d..308ea5685877c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/NodeRecord.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/NodeRecord.java @@ -134,7 +134,6 @@ public Iterable getUnusedDynamicLabelRecords() return filter( notInUseFilter(), dynamicLabelRecords ); } - @Override public boolean isDense() { return dense; diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/ReadTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/ReadTest.java new file mode 100644 index 0000000000000..7f211b864d411 --- /dev/null +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/ReadTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2002-2017 "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.kernel.impl.newapi; + +import org.junit.Test; + +import java.util.concurrent.ThreadLocalRandom; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.neo4j.kernel.impl.newapi.Read.FILTER_MASK; +import static org.neo4j.kernel.impl.newapi.Read.addFilteringFlag; +import static org.neo4j.kernel.impl.newapi.Read.invertReference; +import static org.neo4j.kernel.impl.newapi.Read.needsFiltering; +import static org.neo4j.kernel.impl.newapi.Read.removeFilteringFlag; +import static org.neo4j.kernel.impl.store.record.AbstractBaseRecord.NO_ID; + +public class ReadTest +{ + + @Test + public void shouldPreserveNoId() + { + assertThat( invertReference( NO_ID ), equalTo( (long) NO_ID ) ); + } + + @Test + public void shouldInvertTheInvert() + { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for ( int i = 0; i < 1000; i++ ) + { + //TODO what is the real upper limit + long reference = random.nextLong( FILTER_MASK ); + assertThat( invertReference( invertReference( reference ) ), equalTo( reference ) ); + } + } + + @Test + public void shouldSetFilterFlag() + { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for ( int i = 0; i < 1000; i++ ) + { + //TODO what is the real upper limit + long reference = random.nextLong( FILTER_MASK ); + assertFalse( reference + " shouldn't need to be filtered " + FILTER_MASK, needsFiltering( reference ) ); + long needsFiltering = addFilteringFlag( reference ); + assertTrue( needsFiltering( needsFiltering ) ); + assertThat( removeFilteringFlag( needsFiltering ), equalTo( reference ) ); + } + } + + @Test + public void shouldOnlyFilterRealReferences() + { + assertThat( addFilteringFlag( NO_ID ), equalTo( (long) NO_ID ) ); + assertThat( addFilteringFlag( -1337L ), equalTo( -1337L ) ); + } + + +}