diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/txstate/TransactionCountingStateVisitor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/txstate/TransactionCountingStateVisitor.java index 2838cd0f0615..232a4c4710ea 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/txstate/TransactionCountingStateVisitor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/txstate/TransactionCountingStateVisitor.java @@ -79,8 +79,8 @@ private void decrementCountForLabelsAndRelationships( NodeItem node ) return false; } ); - storeLayer.degrees( statement, node, ALWAYS_TRUE_INT, - ( type, out, in, loop ) -> updateRelationshipsCountsFromDegrees( labelIds, type, -out, -in, -loop ) ); + storeLayer.degrees( statement, node, + ( type, out, in ) -> updateRelationshipsCountsFromDegrees( labelIds, type, -out, -in ) ); } @Override @@ -125,40 +125,37 @@ public void visitNodeLabelChanges( long id, final Set added, final Set< // get the relationship counts from *before* this transaction, // the relationship changes will compensate for what happens during the transaction storeLayer.nodeCursor( statement, id, null ) - .forAll( node -> storeLayer.degrees( statement, node, ALWAYS_TRUE_INT, ( type, out, in, loop ) -> + .forAll( node -> storeLayer.degrees( statement, node, ( type, out, in ) -> { - added.forEach( label -> updateRelationshipsCountsFromDegrees( type, label, out, in, loop ) ); + added.forEach( label -> updateRelationshipsCountsFromDegrees( type, label, out, in ) ); removed.forEach( - label -> updateRelationshipsCountsFromDegrees( type, label, -out, -in, -loop ) ); - return true; + label -> updateRelationshipsCountsFromDegrees( type, label, -out, -in ) ); } ) ); } super.visitNodeLabelChanges( id, added, removed ); } - private boolean updateRelationshipsCountsFromDegrees( PrimitiveIntCollection labels, int type, long outgoing, - long incoming, long loop ) + private void updateRelationshipsCountsFromDegrees( PrimitiveIntCollection labels, int type, long out, long in ) { - labels.visitKeys( label -> updateRelationshipsCountsFromDegrees( type, label, outgoing, incoming, loop ) ); - return true; + labels.visitKeys( label -> updateRelationshipsCountsFromDegrees( type, label, out, in ) ); } - private boolean updateRelationshipsCountsFromDegrees( int type, int label, long out, long in, long loop ) + private boolean updateRelationshipsCountsFromDegrees( int type, int label, long out, long in ) { // untyped - counts.incrementRelationshipCount( label, ANY_RELATIONSHIP_TYPE, ANY_LABEL, out + loop ); - counts.incrementRelationshipCount( ANY_LABEL, ANY_RELATIONSHIP_TYPE, label, in + loop ); + counts.incrementRelationshipCount( label, ANY_RELATIONSHIP_TYPE, ANY_LABEL, out ); + counts.incrementRelationshipCount( ANY_LABEL, ANY_RELATIONSHIP_TYPE, label, in ); // typed - counts.incrementRelationshipCount( label, type, ANY_LABEL, out + loop ); - counts.incrementRelationshipCount( ANY_LABEL, type, label, in + loop ); + counts.incrementRelationshipCount( label, type, ANY_LABEL, out ); + counts.incrementRelationshipCount( ANY_LABEL, type, label, in ); return false; } private void updateRelationshipCount( long startNode, int type, long endNode, int delta ) { - updateRelationshipsCountsFromDegrees( type, ANY_LABEL, delta, 0, 0 ); - visitLabels( startNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, delta, 0, 0 ) ); - visitLabels( endNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, 0, delta, 0 ) ); + updateRelationshipsCountsFromDegrees( type, ANY_LABEL, delta, 0 ); + visitLabels( startNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, delta, 0) ); + visitLabels( endNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, 0, delta ) ); } private void visitLabels( long nodeId, PrimitiveIntVisitor visitor ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/CountingDegreeVisitor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/CountingDegreeVisitor.java deleted file mode 100644 index b8902768f0fc..000000000000 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/CountingDegreeVisitor.java +++ /dev/null @@ -1,77 +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.kernel.impl.api; - -import java.util.function.IntPredicate; - -import org.neo4j.storageengine.api.Direction; - -public class CountingDegreeVisitor implements DegreeVisitor, IntPredicate -{ - private final Direction direction; - private final int relType; - private final boolean stopOnFirstMatch; - - private int count; - - public CountingDegreeVisitor( Direction direction, boolean stopOnFirstMatch ) - { - this( direction, -1, stopOnFirstMatch ); - } - - public CountingDegreeVisitor( Direction direction, int relType, boolean stopOnFirstMatch ) - { - this.direction = direction; - this.relType = relType; - this.stopOnFirstMatch = stopOnFirstMatch; - } - - @Override - public boolean visitDegree( int type, long outgoing, long incoming, long loop ) - { - switch ( direction ) - { - case OUTGOING: - count += outgoing + loop; - break; - case INCOMING: - count += incoming + loop; - break; - case BOTH: - count += outgoing + incoming + loop; - break; - default: - throw new IllegalStateException( "Unknown direction: " + direction ); - } - - return !stopOnFirstMatch; - } - - public int count() - { - return count; - } - - @Override - public boolean test( int value ) - { - return relType == -1 || relType == value; - } -} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DegreeVisitor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DegreeVisitor.java index e6e059568894..850c746a2e7c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DegreeVisitor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DegreeVisitor.java @@ -22,13 +22,5 @@ @FunctionalInterface public interface DegreeVisitor { - interface Visitable extends AutoCloseable - { - void accept( DegreeVisitor visitor ); - - @Override - void close(); - } - - boolean visitDegree( int type, long outgoing, long incoming, long loop ); + void visitDegree( int type, long outgoing, long incoming ); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java index 9195c2e5e100..c42bd52cc2fb 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java @@ -1650,30 +1650,14 @@ public PrimitiveIntSet relationshipTypes( KernelStatement statement, NodeItem no @Override public int degree( KernelStatement statement, NodeItem node, Direction direction ) { - int degree = statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) - ? 0 - : countDegrees( statement, node, new CountingDegreeVisitor( direction, false ) ); - - return statement.hasTxStateWithChanges() - ? statement.txState().getNodeState( node.id() ).augmentDegree( direction, degree ) - : degree; + ReadableTransactionState state = statement.hasTxStateWithChanges() ? statement.txState() : null; + return storeLayer.countDegrees( statement.getStoreStatement(), node, direction, state ); } @Override public int degree( KernelStatement statement, NodeItem node, Direction direction, int relType ) { - int degree = statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) - ? 0 - : countDegrees( statement, node, new CountingDegreeVisitor( direction, relType, node.isDense() ) ); - - return statement.hasTxStateWithChanges() - ? statement.txState().getNodeState( node.id() ).augmentDegree( direction, degree, relType ) - : degree; - } - - private int countDegrees( KernelStatement statement, NodeItem node, CountingDegreeVisitor countingDegreeVisitor ) - { - storeLayer.degrees( statement.getStoreStatement(), node, countingDegreeVisitor, countingDegreeVisitor ); - return countingDegreeVisitor.count(); + ReadableTransactionState state = statement.hasTxStateWithChanges() ? statement.txState() : null; + return storeLayer.countDegrees( statement.getStoreStatement(), node, direction, relType, state ); } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DegreeVisitable.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DenseNodeDegreeCounter.java similarity index 57% rename from community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DegreeVisitable.java rename to community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DenseNodeDegreeCounter.java index 2dc3c6502c72..45ba1fe84788 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DegreeVisitable.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DenseNodeDegreeCounter.java @@ -20,23 +20,21 @@ package org.neo4j.kernel.impl.api.store; import java.util.function.Consumer; -import java.util.function.IntPredicate; -import org.neo4j.function.Disposable; import org.neo4j.io.pagecache.PageCursor; import org.neo4j.kernel.api.StatementConstants; import org.neo4j.kernel.impl.api.DegreeVisitor; import org.neo4j.kernel.impl.store.InvalidRecordException; import org.neo4j.kernel.impl.store.RelationshipGroupStore; import org.neo4j.kernel.impl.store.RelationshipStore; -import org.neo4j.kernel.impl.store.record.Record; import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; import org.neo4j.kernel.impl.store.record.RelationshipRecord; +import org.neo4j.storageengine.api.Direction; import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_RELATIONSHIP; import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE; -public class DegreeVisitable implements DegreeVisitor.Visitable, Disposable +public class DenseNodeDegreeCounter implements NodeDegreeCounter { private final RelationshipStore relationshipStore; private final RelationshipRecord relationshipRecord; @@ -44,14 +42,13 @@ public class DegreeVisitable implements DegreeVisitor.Visitable, Disposable private final RelationshipGroupStore groupStore; private final RelationshipGroupRecord groupRecord; private final PageCursor groupCursor; - private final Consumer cache; + private final Consumer cache; private long nodeId; private long groupId; - private IntPredicate types; - DegreeVisitable( RelationshipStore relationshipStore, RelationshipGroupStore groupStore, - Consumer cache ) + DenseNodeDegreeCounter( RelationshipStore relationshipStore, RelationshipGroupStore groupStore, + Consumer cache ) { this.relationshipStore = relationshipStore; this.relationshipRecord = relationshipStore.newRecord(); @@ -62,39 +59,45 @@ public class DegreeVisitable implements DegreeVisitor.Visitable, Disposable this.cache = cache; } - public DegreeVisitable init( long nodeId, long firstGroupId, IntPredicate types ) + public DenseNodeDegreeCounter init( long nodeId, long firstGroupId ) { this.nodeId = nodeId; this.groupId = firstGroupId; - this.types = types; return this; } @Override public void accept( DegreeVisitor visitor ) { - boolean keepGoing = true; - while ( keepGoing && groupId != NO_NEXT_RELATIONSHIP.longValue() ) + while ( !NO_NEXT_RELATIONSHIP.is( groupId ) ) { RelationshipGroupRecord record = groupStore.readRecord( groupId, groupRecord, FORCE, groupCursor ); if ( record.inUse() ) { int type = record.getType(); - if ( types.test( type ) ) - { - long loop = countByFirstPrevPointer( record.getFirstLoop() ); - long outgoing = countByFirstPrevPointer( record.getFirstOut() ); - long incoming = countByFirstPrevPointer( record.getFirstIn() ); - keepGoing = visitor.visitDegree( type, outgoing, incoming, loop ); - } + long loop = countByFirstPrevPointer( record.getFirstLoop() ); + long outgoing = countByFirstPrevPointer( record.getFirstOut() ) + loop; + long incoming = countByFirstPrevPointer( record.getFirstIn() ) + loop; + visitor.visitDegree( type, outgoing, incoming ); } groupId = record.getNext(); } } + @Override + public int count( Direction direction ) + { + return countRelationshipsInGroup( direction, -1 ); + } + + public int count( Direction direction, int relType ) + { + return countRelationshipsInGroup( direction, relType ); + } + private long countByFirstPrevPointer( long relationshipId ) { - if ( Record.NO_NEXT_RELATIONSHIP.is( relationshipId ) ) + if ( NO_NEXT_RELATIONSHIP.is( relationshipId ) ) { return 0; } @@ -111,12 +114,59 @@ private long countByFirstPrevPointer( long relationshipId ) throw new InvalidRecordException( "Node " + nodeId + " neither start nor end node of " + record ); } + private int countRelationshipsInGroup( Direction direction, int type ) + { + int count = 0; + while ( !NO_NEXT_RELATIONSHIP.is( groupId ) ) + { + RelationshipGroupRecord record = groupStore.readRecord( groupId, groupRecord, FORCE, groupCursor ); + if ( record.inUse() && ( type == -1 || groupRecord.getType() == type ) ) + { + count += nodeDegreeByDirection( direction ); + if ( type != -1 ) + { + // we have read the only type we were interested on, so break the look + break; + } + } + groupId = groupRecord.getNext(); + } + return count; + } + + private long nodeDegreeByDirection( Direction direction ) + { + long firstLoop = groupRecord.getFirstLoop(); + long loopCount = countByFirstPrevPointer( firstLoop ); + switch ( direction ) + { + case OUTGOING: + { + long firstOut = groupRecord.getFirstOut(); + return countByFirstPrevPointer( firstOut ) + loopCount; + } + case INCOMING: + { + long firstIn = groupRecord.getFirstIn(); + return countByFirstPrevPointer( firstIn ) + loopCount; + } + case BOTH: + { + long firstOut = groupRecord.getFirstOut(); + long firstIn = groupRecord.getFirstIn(); + return countByFirstPrevPointer( firstOut ) + + countByFirstPrevPointer( firstIn ) + loopCount; + } + default: + throw new IllegalArgumentException( direction.name() ); + } + } + @Override public void close() { nodeId = StatementConstants.NO_SUCH_NODE; groupId = StatementConstants.NO_SUCH_RELATIONSHIP; - types = null; cache.accept( this ); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/NodeDegreeCounter.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/NodeDegreeCounter.java new file mode 100644 index 000000000000..013fe4920cc1 --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/NodeDegreeCounter.java @@ -0,0 +1,37 @@ +/* + * 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.api.store; + +import org.neo4j.function.Disposable; +import org.neo4j.kernel.impl.api.DegreeVisitor; +import org.neo4j.storageengine.api.Direction; + +public interface NodeDegreeCounter extends AutoCloseable, Disposable +{ + void accept( DegreeVisitor visitor ); + + int count( Direction direction ); + + int count( Direction direction, int relType ); + + @Override + void close(); + +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java index 216de17433d3..afb00a75fe0d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java @@ -21,7 +21,6 @@ import java.util.Iterator; import java.util.function.Function; -import java.util.function.IntPredicate; import java.util.function.Supplier; import org.neo4j.collection.primitive.PrimitiveIntIterator; @@ -78,6 +77,7 @@ import org.neo4j.storageengine.api.txstate.ReadableTransactionState; import static org.neo4j.collection.primitive.Primitive.intSet; +import static org.neo4j.kernel.impl.util.Cursors.count; import static org.neo4j.register.Registers.newDoubleLongRegister; import static org.neo4j.storageengine.api.Direction.BOTH; import static org.neo4j.storageengine.api.Direction.INCOMING; @@ -584,47 +584,99 @@ public PrimitiveIntSet relationshipTypes( StorageStatement statement, NodeItem n } @Override - public void degrees( StorageStatement statement, NodeItem node, IntPredicate types, DegreeVisitor visitor ) + public void degrees( StorageStatement statement, NodeItem node, DegreeVisitor visitor ) { if ( node.isDense() ) { - try ( DegreeVisitor.Visitable degreeCounter = statement - .acquireDenseNodeDegreeCounter( node.id(), node.nextGroupId(), types ) ) + try ( NodeDegreeCounter degreeCounter = statement + .acquireNodeDegreeCounter( node.id(), node.nextGroupId() ) ) { degreeCounter.accept( visitor ); } } else { - visitNode( statement, node, types, visitor ); + visitNode( statement, node, visitor ); } } - private void visitNode( StorageStatement statement, NodeItem node, IntPredicate types, DegreeVisitor visitor ) + @Override + public int countDegrees( StorageStatement statement, NodeItem node, Direction direction, + ReadableTransactionState state ) + { + int count; + if ( state != null && state.nodeIsAddedInThisTx( node.id() ) ) + { + count = 0; + } + else + { + if ( node.isDense() ) + { + try ( NodeDegreeCounter degreeCounter = statement + .acquireNodeDegreeCounter( node.id(), node.nextGroupId() ) ) + { + count = degreeCounter.count( direction ); + } + } + else + { + count = count( nodeGetRelationships( statement, node, direction, null ) ); + } + } + + return state == null ? count : state.getNodeState( node.id() ).augmentDegree( direction, count ); + } + + @Override + public int countDegrees( StorageStatement statement, NodeItem node, Direction direction, int relType, + ReadableTransactionState state ) + { + int count; + if ( state != null && state.nodeIsAddedInThisTx( node.id() ) ) + { + count = 0; + } + else + { + if ( node.isDense() ) + { + try ( NodeDegreeCounter degreeCounter = statement + .acquireNodeDegreeCounter( node.id(), node.nextGroupId() ) ) + { + count = degreeCounter.count( direction, relType ); + } + } + else + { + count = count( nodeGetRelationships( statement, node, direction, new int[]{relType}, null ) ); + } + } + + return state == null ? count : state.getNodeState( node.id() ).augmentDegree( direction, count, relType ); + } + + private void visitNode( StorageStatement statement, NodeItem node, DegreeVisitor visitor ) { try ( Cursor relationships = nodeGetRelationships( statement, node, BOTH, null ) ) { - boolean keepGoing = true; - while ( keepGoing && relationships.next() ) + while ( relationships.next() ) { RelationshipItem rel = relationships.get(); int type = rel.type(); - if ( types.test( type ) ) + switch ( directionOf( node.id(), rel.id(), rel.startNode(), rel.endNode() ) ) { - switch ( directionOf( node.id(), rel.id(), rel.startNode(), rel.endNode() ) ) - { - case OUTGOING: - keepGoing = visitor.visitDegree( type, 1, 0, 0 ); - break; - case INCOMING: - keepGoing = visitor.visitDegree( type, 0, 1, 0 ); - break; - case BOTH: - keepGoing = visitor.visitDegree( type, 0, 0, 1 ); - break; - default: - throw new IllegalStateException( "You found the missing direction!" ); - } + case OUTGOING: + visitor.visitDegree( type, 1, 0 ); + break; + case INCOMING: + visitor.visitDegree( type, 0, 1 ); + break; + case BOTH: + visitor.visitDegree( type, 1, 1 ); + break; + default: + throw new IllegalStateException( "You found the missing direction!" ); } } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreStatement.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreStatement.java index 2a420b16df48..4dcb14f31a5f 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreStatement.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreStatement.java @@ -19,13 +19,11 @@ */ package org.neo4j.kernel.impl.api.store; -import java.util.function.IntPredicate; import java.util.function.Supplier; import org.neo4j.cursor.Cursor; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.schema.index.IndexDescriptor; -import org.neo4j.kernel.impl.api.DegreeVisitor; import org.neo4j.kernel.impl.api.IndexReaderFactory; import org.neo4j.kernel.impl.locking.Lock; import org.neo4j.kernel.impl.locking.LockService; @@ -58,7 +56,7 @@ public class StoreStatement implements StorageStatement private final InstanceCache propertyCursorCache; private final InstanceCache singlePropertyCursorCache; private final InstanceCache relationshipGroupCursorCache; - private final InstanceCache degreeVisitableCache; + private final InstanceCache degreeVisitableCache; private final NeoStores neoStores; private final Supplier indexReaderFactorySupplier; private final Supplier labelScanStore; @@ -125,12 +123,12 @@ protected StoreSinglePropertyCursor create() return new StoreSinglePropertyCursor( neoStores.getPropertyStore(), this ); } }; - degreeVisitableCache = new InstanceCache() + degreeVisitableCache = new InstanceCache() { @Override - protected DegreeVisitable create() + protected DenseNodeDegreeCounter create() { - return new DegreeVisitable( neoStores.getRelationshipStore(), neoStores.getRelationshipGroupStore(), + return new DenseNodeDegreeCounter( neoStores.getRelationshipStore(), neoStores.getRelationshipGroupStore(), this ); } }; @@ -210,10 +208,9 @@ public Cursor acquireRelationshipGroupCursor( long relati } @Override - public DegreeVisitor.Visitable acquireDenseNodeDegreeCounter( long nodeId, long relationshipGroupId, - IntPredicate types ) + public NodeDegreeCounter acquireNodeDegreeCounter( long nodeId, long relationshipGroupId ) { - return degreeVisitableCache.get().init( nodeId, relationshipGroupId, types ); + return degreeVisitableCache.get().init( nodeId, relationshipGroupId ); } @Override diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java index 6aed9d51eb05..147691c05773 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java @@ -19,12 +19,10 @@ */ package org.neo4j.storageengine.api; -import java.util.function.IntPredicate; - import org.neo4j.cursor.Cursor; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.schema.index.IndexDescriptor; -import org.neo4j.kernel.impl.api.DegreeVisitor; +import org.neo4j.kernel.impl.api.store.NodeDegreeCounter; import org.neo4j.kernel.impl.locking.Lock; import org.neo4j.storageengine.api.schema.IndexReader; import org.neo4j.storageengine.api.schema.LabelScanReader; @@ -126,7 +124,7 @@ Cursor acquireNodeRelationshipCursor( boolean isDense, long no Cursor acquireRelationshipGroupCursor( long relationshipGroupId ); - DegreeVisitor.Visitable acquireDenseNodeDegreeCounter( long nodeId, long relationshipGroupId, IntPredicate types ); + NodeDegreeCounter acquireNodeDegreeCounter( long nodeId, long relationshipGroupId ); /** * @return {@link LabelScanReader} capable of reading nodes for specific label ids. diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/StoreReadLayer.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/StoreReadLayer.java index 58fb444b9120..cef6d295631c 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/StoreReadLayer.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/StoreReadLayer.java @@ -40,6 +40,7 @@ import org.neo4j.kernel.api.schema.index.IndexDescriptor; import org.neo4j.kernel.api.txstate.TransactionState; import org.neo4j.kernel.impl.api.DegreeVisitor; +import org.neo4j.kernel.impl.api.KernelStatement; import org.neo4j.kernel.impl.api.store.RelationshipIterator; import org.neo4j.register.Register.DoubleLongRegister; import org.neo4j.storageengine.api.schema.IndexReader; @@ -386,7 +387,13 @@ DoubleLongRegister indexSample( LabelSchemaDescriptor descriptor, DoubleLongRegi PrimitiveIntSet relationshipTypes( StorageStatement statement, NodeItem node ); - void degrees( StorageStatement statement, NodeItem nodeItem, IntPredicate types, DegreeVisitor visitor ); + void degrees( StorageStatement statement, NodeItem nodeItem, DegreeVisitor visitor ); + + int countDegrees( StorageStatement statement, NodeItem node, Direction direction, ReadableTransactionState state ); + + int countDegrees( StorageStatement statement, NodeItem node, Direction direction, int relType, + ReadableTransactionState state ); T getOrCreateSchemaDependantState( Class type, Function factory ); + } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerRelTypesAndDegreeTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerRelTypesAndDegreeTest.java index 25b93470f0eb..197ff0e73cc1 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerRelTypesAndDegreeTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerRelTypesAndDegreeTest.java @@ -32,7 +32,6 @@ import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.kernel.impl.api.CountingDegreeVisitor; import org.neo4j.kernel.impl.core.RelationshipTypeTokenHolder; import org.neo4j.kernel.impl.core.TokenNotFoundException; import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; @@ -55,7 +54,6 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.neo4j.collection.primitive.PrimitiveIntCollections.mapToSet; -import static org.neo4j.function.Predicates.ALWAYS_TRUE_INT; import static org.neo4j.helpers.collection.Iterators.asSet; import static org.neo4j.kernel.impl.api.store.TestRelType.IN; import static org.neo4j.kernel.impl.api.store.TestRelType.LOOP; @@ -242,16 +240,12 @@ private void testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelChains( boo private int degreeForDirection( NodeCursor node, Direction direction ) { - CountingDegreeVisitor countingDegreeVisitor = new CountingDegreeVisitor( direction, false ); - disk.degrees( disk.newStatement(), node, countingDegreeVisitor, countingDegreeVisitor ); - return countingDegreeVisitor.count(); + return disk.countDegrees( disk.newStatement(), node, direction, null ); } private int degreeForDirectionAndType( NodeCursor node, Direction direction, int relType ) { - CountingDegreeVisitor countingDegreeVisitor = new CountingDegreeVisitor( direction, relType, node.isDense() ); - disk.degrees( disk.newStatement(), node, countingDegreeVisitor, countingDegreeVisitor ); - return countingDegreeVisitor.count(); + return disk.countDegrees( disk.newStatement(), node, direction, relType, null ); } private void testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( @@ -415,15 +409,15 @@ private void testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelTy Set expectedDegrees = new HashSet<>(); if ( outRelCount != 0 ) { - expectedDegrees.add( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0, 0 ) ); + expectedDegrees.add( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0 ) ); } if ( inRelCount != 0 ) { - expectedDegrees.add( new TestDegreeItem( relTypeId( IN ), 0, inRelCount, 0 ) ); + expectedDegrees.add( new TestDegreeItem( relTypeId( IN ), 0, inRelCount ) ); } if ( loopRelCount != 0 ) { - expectedDegrees.add( new TestDegreeItem( relTypeId( LOOP ), 0, 0, loopRelCount ) ); + expectedDegrees.add( new TestDegreeItem( relTypeId( LOOP ), loopRelCount, loopRelCount ) ); } Set actualDegrees = degrees( cursor ); @@ -455,9 +449,9 @@ private void testDegreesForDenseNodeWithPartiallyDeletedRelChains( boolean modif } Set expectedDegrees = new HashSet<>( - asList( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0, 0 ), - new TestDegreeItem( relTypeId( IN ), 0, inRelCount, 0 ), - new TestDegreeItem( relTypeId( LOOP ), 0, 0, loopRelCount ) ) ); + asList( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0 ), + new TestDegreeItem( relTypeId( IN ), 0, inRelCount ), + new TestDegreeItem( relTypeId( LOOP ), loopRelCount, loopRelCount ) ) ); Set actualDegrees = degrees( cursor.get() ); @@ -467,8 +461,8 @@ private void testDegreesForDenseNodeWithPartiallyDeletedRelChains( boolean modif private Set degrees( NodeItem nodeItem ) { Set degrees = new HashSet<>(); - disk.degrees( disk.newStatement(), nodeItem, ALWAYS_TRUE_INT, ( type, outgoing, incoming, loop ) -> degrees - .add( new TestDegreeItem( type, outgoing, incoming, loop ) ) ); + disk.degrees( disk.newStatement(), nodeItem, ( type, outgoing, incoming ) -> degrees + .add( new TestDegreeItem( type, outgoing, incoming ) ) ); return degrees; } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/TestDegreeItem.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/TestDegreeItem.java index 6fafc8c7e3d7..0084e7c4492f 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/TestDegreeItem.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/TestDegreeItem.java @@ -26,14 +26,12 @@ public class TestDegreeItem private final int type; private final long outgoing; private final long incoming; - private final long loop; - TestDegreeItem( int type, long outgoing, long incoming, long loop ) + TestDegreeItem( int type, long outgoing, long incoming ) { this.type = type; this.outgoing = outgoing; this.incoming = incoming; - this.loop = loop; } public int type() @@ -51,11 +49,6 @@ public long incoming() return incoming; } - public long loop() - { - return loop; - } - @Override public boolean equals( Object o ) { @@ -68,19 +61,18 @@ public boolean equals( Object o ) return false; } TestDegreeItem that = (TestDegreeItem) o; - return type == that.type && outgoing == that.outgoing && incoming == that.incoming && loop == that.loop; + return type == that.type && outgoing == that.outgoing && incoming == that.incoming; } @Override public int hashCode() { - return Objects.hash( type, outgoing, incoming, loop ); + return Objects.hash( type, outgoing, incoming ); } @Override public String toString() { - return "TestDegreeItem{" + "type=" + type + ", outgoing=" + outgoing + ", incoming=" + incoming + ", loop=" + - loop + '}'; + return "TestDegreeItem{" + "type=" + type + ", outgoing=" + outgoing + ", incoming=" + incoming + '}'; } }