From 68bb78b339ba7c3a07f569c1ad9d298b8cebe860 Mon Sep 17 00:00:00 2001 From: Davide Grohmann Date: Fri, 17 Mar 2017 13:46:01 +0100 Subject: [PATCH] Simplify degree counting code by reusing degree visiting code --- .../TransactionCountingStateVisitor.java | 32 +++--- .../impl/api/CountingDegreeVisitor.java | 73 ++++++++++++ .../neo4j/kernel/impl/api/DegreeVisitor.java | 3 +- .../api/StateHandlingStatementOperations.java | 23 +--- .../kernel/impl/api/store/DegreeCounter.java | 107 ------------------ .../kernel/impl/api/store/StorageLayer.java | 74 ++++++------ .../neo4j/storageengine/api/DegreeItem.java | 41 ------- .../storageengine/api/StoreReadLayer.java | 3 - .../StorageLayerRelTypesAndDegreeTest.java | 32 +++--- .../kernel/impl/api/store/TestDegreeItem.java | 32 +++--- 10 files changed, 168 insertions(+), 252 deletions(-) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/CountingDegreeVisitor.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DegreeCounter.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/DegreeItem.java 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 6f458e67b504..557fc5bf4ab1 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 @@ -80,7 +80,7 @@ private void decrementCountForLabelsAndRelationships( NodeItem node ) } ); storeLayer.degrees( statement, node, - ( type, out, in ) -> updateRelationshipsCountsFromDegrees( labelIds, type, -out, -in ) ); + ( type, out, in, loop ) -> updateRelationshipsCountsFromDegrees( labelIds, type, -out, -in, -loop ) ); } @Override @@ -124,37 +124,39 @@ 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, ( type, out, in ) -> + .forAll( node -> storeLayer.degrees( statement, node, ( type, out, in, loop ) -> { - added.forEach( label -> updateRelationshipsCountsFromDegrees( type, label, out, in ) ); - removed.forEach( label -> updateRelationshipsCountsFromDegrees( type, label, -out, -in ) ); + added.forEach( label -> updateRelationshipsCountsFromDegrees( type, label, out, in, loop ) ); + removed.forEach( label -> updateRelationshipsCountsFromDegrees( type, label, -out, -in, -loop ) ); + return true; } ) ); } super.visitNodeLabelChanges( id, added, removed ); } - private void updateRelationshipsCountsFromDegrees( PrimitiveIntCollection labels, int type, long outgoing, - long incoming ) + private boolean updateRelationshipsCountsFromDegrees( PrimitiveIntCollection labels, int type, long outgoing, + long incoming, long loop ) { - labels.visitKeys( ( label ) -> updateRelationshipsCountsFromDegrees( type, label, outgoing, incoming ) ); + labels.visitKeys( label -> updateRelationshipsCountsFromDegrees( type, label, outgoing, incoming, loop ) ); + return true; } - private boolean updateRelationshipsCountsFromDegrees( int type, int label, long outgoing, long incoming ) + private boolean updateRelationshipsCountsFromDegrees( int type, int label, long out, long in, long loop ) { // untyped - counts.incrementRelationshipCount( label, ANY_RELATIONSHIP_TYPE, ANY_LABEL, outgoing ); - counts.incrementRelationshipCount( ANY_LABEL, ANY_RELATIONSHIP_TYPE, label, incoming ); + counts.incrementRelationshipCount( label, ANY_RELATIONSHIP_TYPE, ANY_LABEL, out + loop ); + counts.incrementRelationshipCount( ANY_LABEL, ANY_RELATIONSHIP_TYPE, label, in + loop ); // typed - counts.incrementRelationshipCount( label, type, ANY_LABEL, outgoing ); - counts.incrementRelationshipCount( ANY_LABEL, type, label, incoming ); + counts.incrementRelationshipCount( label, type, ANY_LABEL, out + loop ); + counts.incrementRelationshipCount( ANY_LABEL, type, label, in + loop ); return false; } private void updateRelationshipCount( long startNode, int type, long endNode, int delta ) { - updateRelationshipsCountsFromDegrees( type, ANY_LABEL, delta, 0 ); - visitLabels( startNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, delta, 0 ) ); - visitLabels( endNode, ( labelId ) -> updateRelationshipsCountsFromDegrees( type, labelId, 0, 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 ) ); } 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 new file mode 100644 index 000000000000..1106565d340e --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/CountingDegreeVisitor.java @@ -0,0 +1,73 @@ +/* + * 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 org.neo4j.storageengine.api.Direction; + +public class CountingDegreeVisitor implements DegreeVisitor +{ + private final Direction direction; + private final int relType; + private final boolean dense; + + private int count; + + public CountingDegreeVisitor( Direction direction, boolean dense ) + { + this( direction, -1, dense ); + } + + public CountingDegreeVisitor( Direction direction, int relType, boolean dense ) + { + this.direction = direction; + this.relType = relType; + this.dense = dense; + } + + @Override + public boolean visitDegree( int type, long outgoing, long incoming, long loop ) + { + if ( relType == -1 || type == relType ) + { + 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 ); + } + // continue only if we are counting all types or the node is not dense (i.e., we visit one rel at the time) + return relType == -1 || !dense; + } + return true; + } + + public int count() + { + return count; + } +} 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 a7e184742504..72baabc1485d 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 @@ -19,7 +19,8 @@ */ package org.neo4j.kernel.impl.api; +@FunctionalInterface public interface DegreeVisitor { - void visitDegree( int type, long outgoing, long incoming ); + boolean visitDegree( int type, long outgoing, long incoming, long loop ); } 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 02dda7ce4e9d..604b57d76072 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 @@ -97,7 +97,6 @@ import org.neo4j.storageengine.api.PropertyItem; import org.neo4j.storageengine.api.RelationshipItem; import org.neo4j.storageengine.api.StorageProperty; -import org.neo4j.storageengine.api.StorageStatement; import org.neo4j.storageengine.api.StoreReadLayer; import org.neo4j.storageengine.api.Token; import org.neo4j.storageengine.api.schema.IndexReader; @@ -118,7 +117,6 @@ import static org.neo4j.kernel.api.properties.DefinedProperty.NO_SUCH_PROPERTY; import static org.neo4j.kernel.impl.api.state.IndexTxStateUpdater.LabelChangeType.ADDED_LABEL; import static org.neo4j.kernel.impl.api.state.IndexTxStateUpdater.LabelChangeType.REMOVED_LABEL; -import static org.neo4j.kernel.impl.util.Cursors.count; import static org.neo4j.register.Registers.newDoubleLongRegister; import static org.neo4j.storageengine.api.txstate.TxStateVisitor.EMPTY; @@ -1654,7 +1652,7 @@ public int degree( KernelStatement statement, NodeItem node, Direction direction { int degree = statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) ? 0 - : computeDegree( statement, node, direction, null ); + : visitDegrees( statement, node, new CountingDegreeVisitor( direction, node.isDense() ) ).count(); return statement.hasTxStateWithChanges() ? statement.txState().getNodeState( node.id() ).augmentDegree( direction, degree ) @@ -1666,27 +1664,16 @@ public int degree( KernelStatement statement, NodeItem node, Direction direction { int degree = statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) ? 0 - : computeDegree( statement, node, direction, relType ); + : visitDegrees( statement, node, new CountingDegreeVisitor( direction, relType, node.isDense() ) ).count(); return statement.hasTxStateWithChanges() ? statement.txState().getNodeState( node.id() ).augmentDegree( direction, degree, relType ) : degree; } - private int computeDegree( KernelStatement statement, NodeItem node, Direction direction, Integer relType ) + private T visitDegrees( KernelStatement statement, NodeItem node, T visitor ) { - StorageStatement storeStatement = statement.getStoreStatement(); - if ( node.isDense() ) - { - return storeLayer - .degreeRelationshipsInGroup( storeStatement, node.id(), node.nextGroupId(), direction, relType ); - } - else - { - return count( relType == null - ? storeLayer.nodeGetRelationships( storeStatement, node, direction, null ) - : storeLayer.nodeGetRelationships( storeStatement, node, direction, new int[]{relType},null ) - ); - } + storeLayer.degrees( statement.getStoreStatement(), node, visitor ); + return visitor; } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DegreeCounter.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DegreeCounter.java deleted file mode 100644 index ea9aa0cb12f3..000000000000 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/DegreeCounter.java +++ /dev/null @@ -1,107 +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.store; - -import org.neo4j.kernel.impl.store.InvalidRecordException; -import org.neo4j.kernel.impl.store.RecordCursor; -import org.neo4j.kernel.impl.store.RecordCursors; -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.RecordLoad.FORCE; - -class DegreeCounter -{ - private DegreeCounter() - { - } - - static long countByFirstPrevPointer( long relationshipId, RecordCursor cursor, - long nodeId, RelationshipRecord relationshipRecord ) - { - if ( relationshipId == Record.NO_NEXT_RELATIONSHIP.longValue() ) - { - return 0; - } - cursor.next( relationshipId, relationshipRecord, FORCE ); - if ( relationshipRecord.getFirstNode() == nodeId ) - { - return relationshipRecord.getFirstPrevRel(); - } - if ( relationshipRecord.getSecondNode() == nodeId ) - { - return relationshipRecord.getSecondPrevRel(); - } - throw new InvalidRecordException( "Node " + nodeId + " neither start nor end node of " + relationshipRecord ); - } - - static int countRelationshipsInGroup( long groupId, Direction direction, Integer type, long nodeId, - RelationshipRecord relationshipRecord, RelationshipGroupRecord groupRecord, RecordCursors cursors ) - { - int count = 0; - while ( groupId != Record.NO_NEXT_RELATIONSHIP.longValue() ) - { - boolean groupRecordInUse = cursors.relationshipGroup().next( groupId, groupRecord, FORCE ); - if ( groupRecordInUse && ( type == null || groupRecord.getType() == type ) ) - { - count += nodeDegreeByDirection( direction, nodeId, relationshipRecord, groupRecord, cursors ); - if ( type != null ) - { - // we have read the only type we were interested on, so break the look - break; - } - } - groupId = groupRecord.getNext(); - } - return count; - } - - private static long nodeDegreeByDirection( Direction direction, long nodeId, - RelationshipRecord relationshipRecord, RelationshipGroupRecord groupRecord, RecordCursors cursors ) - { - long firstLoop = groupRecord.getFirstLoop(); - RecordCursor cursor = cursors.relationship(); - long loopCount = countByFirstPrevPointer( firstLoop, cursor, nodeId, relationshipRecord ); - switch ( direction ) - { - case OUTGOING: - { - long firstOut = groupRecord.getFirstOut(); - return countByFirstPrevPointer( firstOut, cursor, nodeId, relationshipRecord ) + loopCount; - } - case INCOMING: - { - long firstIn = groupRecord.getFirstIn(); - return countByFirstPrevPointer( firstIn, cursor, nodeId, relationshipRecord ) + loopCount; - } - case BOTH: - { - long firstOut = groupRecord.getFirstOut(); - long firstIn = groupRecord.getFirstIn(); - return countByFirstPrevPointer( firstOut, cursor, nodeId, relationshipRecord ) + - countByFirstPrevPointer( firstIn, cursor, nodeId, relationshipRecord ) + loopCount; - } - default: - throw new IllegalArgumentException( direction.name() ); - } - } -} 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 289b644f1367..df78e391e06f 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 @@ -63,6 +63,7 @@ import org.neo4j.kernel.impl.store.UnderlyingStorageException; import org.neo4j.kernel.impl.store.counts.CountsTracker; import org.neo4j.kernel.impl.store.record.IndexRule; +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.kernel.impl.transaction.state.PropertyLoader; @@ -84,8 +85,6 @@ import org.neo4j.storageengine.api.txstate.ReadableTransactionState; import static org.neo4j.collection.primitive.Primitive.intSet; -import static org.neo4j.kernel.impl.api.store.DegreeCounter.countByFirstPrevPointer; -import static org.neo4j.kernel.impl.api.store.DegreeCounter.countRelationshipsInGroup; import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_RELATIONSHIP; import static org.neo4j.kernel.impl.store.record.RecordLoad.CHECK; import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE; @@ -612,15 +611,15 @@ public PrimitiveIntSet relationshipTypes( StorageStatement statement, NodeItem n } @Override - public void degrees( StorageStatement statement, NodeItem nodeItem, DegreeVisitor visitor ) + public void degrees( StorageStatement statement, NodeItem node, DegreeVisitor visitor ) { - if ( nodeItem.isDense() ) + if ( node.isDense() ) { - visitDenseNode( statement, nodeItem, visitor ); + visitDenseNode( statement, node, visitor ); } else { - visitNode( statement, nodeItem, visitor ); + visitNode( statement, node, visitor ); } } @@ -637,40 +636,31 @@ private IndexRule indexRule( IndexDescriptor index ) return schemaStorage.indexGetForSchema( index ); } - @Override - public int degreeRelationshipsInGroup( StorageStatement storeStatement, long nodeId, long groupId, - Direction direction, Integer relType ) - { - RelationshipRecord relationshipRecord = relationshipStore.newRecord(); - RelationshipGroupRecord relationshipGroupRecord = relationshipGroupStore.newRecord(); - return countRelationshipsInGroup( groupId, direction, relType, nodeId, relationshipRecord, - relationshipGroupRecord, storeStatement.recordCursors() ); - } - @Override public T getOrCreateSchemaDependantState( Class type, Function factory ) { return schemaCache.getOrCreateDependantState( type, factory, this ); } - private void visitNode( StorageStatement statement, NodeItem nodeItem, DegreeVisitor visitor ) + private void visitNode( StorageStatement statement, NodeItem node, DegreeVisitor visitor ) { - try ( Cursor relationships = nodeGetRelationships( statement, nodeItem, BOTH, null ) ) + try ( Cursor relationships = nodeGetRelationships( statement, node, BOTH, null ) ) { - while ( relationships.next() ) + boolean keepGoing = true; + while ( keepGoing && relationships.next() ) { RelationshipItem rel = relationships.get(); int type = rel.type(); - switch ( directionOf( nodeItem.id(), rel.id(), rel.startNode(), rel.endNode() ) ) + switch ( directionOf( node.id(), rel.id(), rel.startNode(), rel.endNode() ) ) { case OUTGOING: - visitor.visitDegree( type, 1, 0 ); + keepGoing = visitor.visitDegree( type, 1, 0, 0 ); break; case INCOMING: - visitor.visitDegree( type, 0, 1 ); + keepGoing = visitor.visitDegree( type, 0, 1, 0 ); break; case BOTH: - visitor.visitDegree( type, 1, 1 ); + keepGoing = visitor.visitDegree( type, 0, 0, 1 ); break; default: throw new IllegalStateException( "You found the missing direction!" ); @@ -679,15 +669,16 @@ private void visitNode( StorageStatement statement, NodeItem nodeItem, DegreeVis } } - private void visitDenseNode( StorageStatement statement, NodeItem nodeItem, DegreeVisitor visitor ) + private void visitDenseNode( StorageStatement statement, NodeItem node, DegreeVisitor visitor ) { RelationshipGroupRecord relationshipGroupRecord = relationshipGroupStore.newRecord(); RecordCursor relationshipGroupCursor = statement.recordCursors().relationshipGroup(); RelationshipRecord relationshipRecord = relationshipStore.newRecord(); RecordCursor relationshipCursor = statement.recordCursors().relationship(); - long groupId = nodeItem.nextGroupId(); - while ( groupId != NO_NEXT_RELATIONSHIP.longValue() ) + boolean keepGoing = true; + long groupId = node.nextGroupId(); + while ( keepGoing && groupId != NO_NEXT_RELATIONSHIP.longValue() ) { relationshipGroupCursor.next( groupId, relationshipGroupRecord, FORCE ); if ( relationshipGroupRecord.inUse() ) @@ -698,14 +689,10 @@ private void visitDenseNode( StorageStatement statement, NodeItem nodeItem, Degr long firstOut = relationshipGroupRecord.getFirstOut(); long firstIn = relationshipGroupRecord.getFirstIn(); - long loop = countByFirstPrevPointer( firstLoop, relationshipCursor, nodeItem.id(), relationshipRecord ); - long outgoing = - countByFirstPrevPointer( firstOut, relationshipCursor, nodeItem.id(), relationshipRecord ) + - loop; - long incoming = - countByFirstPrevPointer( firstIn, relationshipCursor, nodeItem.id(), relationshipRecord ) + - loop; - visitor.visitDegree( type, outgoing, incoming ); + long loop = countByFirstPrevPointer( firstLoop, relationshipCursor, node.id(), relationshipRecord ); + long outgoing = countByFirstPrevPointer( firstOut, relationshipCursor, node.id(), relationshipRecord ); + long incoming = countByFirstPrevPointer( firstIn, relationshipCursor, node.id(), relationshipRecord ); + keepGoing = visitor.visitDegree( type, outgoing, incoming, loop ); } groupId = relationshipGroupRecord.getNext(); } @@ -726,6 +713,25 @@ private Direction directionOf( long nodeId, long relationshipId, long startNode, " with startNode:" + startNode + " and endNode:" + endNode ); } + private static long countByFirstPrevPointer( long relationshipId, RecordCursor cursor, + long nodeId, RelationshipRecord relationshipRecord ) + { + if ( relationshipId == Record.NO_NEXT_RELATIONSHIP.longValue() ) + { + return 0; + } + cursor.next( relationshipId, relationshipRecord, FORCE ); + if ( relationshipRecord.getFirstNode() == nodeId ) + { + return relationshipRecord.getFirstPrevRel(); + } + if ( relationshipRecord.getSecondNode() == nodeId ) + { + return relationshipRecord.getSecondPrevRel(); + } + throw new InvalidRecordException( "Node " + nodeId + " neither start nor end node of " + relationshipRecord ); + } + private static Iterator toIndexDescriptors( Iterable rules ) { return Iterators.map( IndexRule::getIndexDescriptor, rules.iterator() ); diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/DegreeItem.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/DegreeItem.java deleted file mode 100644 index 46f48d48c413..000000000000 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/DegreeItem.java +++ /dev/null @@ -1,41 +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.storageengine.api; - -/** - * Single degree item from a node cursor. - */ -public interface DegreeItem -{ - /** - * @return relationship type token id for this degree. - */ - int type(); - - /** - * @return number of outgoing relationships for the {@link #type()}. Loops are included. - */ - long outgoing(); - - /** - * @return number of incoming relationships for the {@link #type()}. Loops are included. - */ - long incoming(); -} 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 8c1a8fb3a8ab..5bf5b75544bd 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 @@ -399,8 +399,5 @@ DoubleLongRegister indexSample( LabelSchemaDescriptor descriptor, DoubleLongRegi void degrees( StorageStatement statement, NodeItem nodeItem, DegreeVisitor visitor ); - int degreeRelationshipsInGroup( StorageStatement storeStatement, long id, long groupId, Direction direction, - Integer relType ); - 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 5eea2e630493..31eaeb98dcd9 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,11 +32,11 @@ 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; import org.neo4j.kernel.impl.store.NeoStores; -import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.kernel.impl.store.RecordStore; import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; import org.neo4j.kernel.impl.store.record.NodeRecord; @@ -239,16 +239,18 @@ private void testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelChains( boo assertEquals( inRelCount + outRelCount + loopRelCount, degreeForDirection( cursor, BOTH ) ); } - private int degreeForDirection( NodeCursor cursor, Direction direction ) + private int degreeForDirection( NodeCursor node, Direction direction ) { - return disk - .degreeRelationshipsInGroup( disk.newStatement(), cursor.id(), cursor.nextGroupId(), direction, null ); + CountingDegreeVisitor visitor = new CountingDegreeVisitor( direction, node.isDense() ); + disk.degrees( disk.newStatement(), node, visitor ); + return visitor.count(); } - private int degreeForDirectionAndType( NodeCursor cursor, Direction direction, int relType ) + private int degreeForDirectionAndType( NodeCursor node, Direction direction, Integer relType ) { - return disk.degreeRelationshipsInGroup( disk.newStatement(), cursor.id(), cursor.nextGroupId(), direction, - relType ); + CountingDegreeVisitor visitor = new CountingDegreeVisitor( direction, relType, node.isDense() ); + disk.degrees( disk.newStatement(), node, visitor ); + return visitor.count(); } private void testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( @@ -412,15 +414,15 @@ private void testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelTy Set expectedDegrees = new HashSet<>(); if ( outRelCount != 0 ) { - expectedDegrees.add( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0 ) ); + expectedDegrees.add( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0, 0 ) ); } if ( inRelCount != 0 ) { - expectedDegrees.add( new TestDegreeItem( relTypeId( IN ), 0, inRelCount ) ); + expectedDegrees.add( new TestDegreeItem( relTypeId( IN ), 0, inRelCount, 0 ) ); } if ( loopRelCount != 0 ) { - expectedDegrees.add( new TestDegreeItem( relTypeId( LOOP ), loopRelCount, loopRelCount ) ); + expectedDegrees.add( new TestDegreeItem( relTypeId( LOOP ), 0, 0, loopRelCount ) ); } Set actualDegrees = degrees( cursor ); @@ -452,9 +454,9 @@ private void testDegreesForDenseNodeWithPartiallyDeletedRelChains( boolean modif } Set expectedDegrees = new HashSet<>( - asList( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0 ), - new TestDegreeItem( relTypeId( IN ), 0, inRelCount ), - new TestDegreeItem( relTypeId( LOOP ), loopRelCount, loopRelCount ) ) ); + asList( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0, 0 ), + new TestDegreeItem( relTypeId( IN ), 0, inRelCount, 0 ), + new TestDegreeItem( relTypeId( LOOP ), 0, 0, loopRelCount ) ) ); Set actualDegrees = degrees( cursor.get() ); @@ -464,8 +466,8 @@ private void testDegreesForDenseNodeWithPartiallyDeletedRelChains( boolean modif private Set degrees( NodeItem nodeItem ) { Set degrees = new HashSet<>(); - disk.degrees( disk.newStatement(), nodeItem, - ( type, outgoing, incoming ) -> degrees.add( new TestDegreeItem( type, outgoing, incoming ) ) ); + disk.degrees( disk.newStatement(), nodeItem, ( type, outgoing, incoming, loop ) -> degrees + .add( new TestDegreeItem( type, outgoing, incoming, loop ) ) ); 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 ee9afc6bf354..6fafc8c7e3d7 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 @@ -19,44 +19,43 @@ */ package org.neo4j.kernel.impl.api.store; -import org.neo4j.storageengine.api.DegreeItem; +import java.util.Objects; -public class TestDegreeItem implements DegreeItem +public class TestDegreeItem { private final int type; private final long outgoing; private final long incoming; + private final long loop; - public TestDegreeItem( DegreeItem item ) - { - this( item.type(), item.outgoing(), item.incoming() ); - } - - public TestDegreeItem( int type, long outgoing, long incoming ) + TestDegreeItem( int type, long outgoing, long incoming, long loop ) { this.type = type; this.outgoing = outgoing; this.incoming = incoming; + this.loop = loop; } - @Override public int type() { return type; } - @Override public long outgoing() { return outgoing; } - @Override public long incoming() { return incoming; } + public long loop() + { + return loop; + } + @Override public boolean equals( Object o ) { @@ -69,22 +68,19 @@ public boolean equals( Object o ) return false; } TestDegreeItem that = (TestDegreeItem) o; - return type == that.type && outgoing == that.outgoing && incoming == that.incoming; + return type == that.type && outgoing == that.outgoing && incoming == that.incoming && loop == that.loop; } @Override public int hashCode() { - return 31 * (31 * type + (int) (outgoing ^ (outgoing >>> 32))) + (int) (incoming ^ (incoming >>> 32)); + return Objects.hash( type, outgoing, incoming, loop ); } @Override public String toString() { - return "TestDegreeItem{" + - "type=" + type + - ", outgoing=" + outgoing + - ", incoming=" + incoming + - '}'; + return "TestDegreeItem{" + "type=" + type + ", outgoing=" + outgoing + ", incoming=" + incoming + ", loop=" + + loop + '}'; } }