From 4f351ceb37684c3b0b85c4200b691241bc9903d4 Mon Sep 17 00:00:00 2001 From: Davide Grohmann Date: Wed, 8 Feb 2017 13:43:14 +0100 Subject: [PATCH] Move degrees and relationshipTypes api out of NodeItem Moved the logic to the EntityReadOperations and StorageLayer --- .../ConstraintEnforcingEntityOperations.java | 20 + .../impl/api/GuardingStatementOperations.java | 23 + .../kernel/impl/api/OperationsFacade.java | 14 +- .../api/StateHandlingStatementOperations.java | 71 +++ .../impl/api/cursor/TxSingleNodeCursor.java | 33 -- .../api/operations/EntityReadOperations.java | 35 ++ .../kernel/impl/api/store/StorageLayer.java | 36 ++ .../impl/api/store/StoreSingleNodeCursor.java | 52 -- .../impl/transaction/TransactionStats.java | 2 +- .../org/neo4j/storageengine/api/NodeItem.java | 25 - .../storageengine/api/StoreReadLayer.java | 6 + .../kernel/impl/api/state/StubCursors.java | 18 - .../api/store/StorageLayerDegreeTest.java | 370 -------------- ...=> StorageLayerRelTypesAndDegreeTest.java} | 463 ++++++++++++------ 14 files changed, 498 insertions(+), 670 deletions(-) delete mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerDegreeTest.java rename community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/{StoreSingleNodeCursorTest.java => StorageLayerRelTypesAndDegreeTest.java} (63%) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java index 6e455215af367..1d0065138d36e 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java @@ -22,6 +22,7 @@ import java.util.Iterator; import org.neo4j.collection.primitive.PrimitiveIntIterator; +import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.cursor.Cursor; import org.neo4j.helpers.Strings; @@ -61,6 +62,7 @@ import org.neo4j.kernel.impl.constraints.ConstraintSemantics; import org.neo4j.kernel.impl.locking.LockTracer; import org.neo4j.kernel.impl.locking.Locks; +import org.neo4j.storageengine.api.Direction; import org.neo4j.storageengine.api.NodeItem; import org.neo4j.storageengine.api.RelationshipItem; @@ -510,4 +512,22 @@ public boolean nodeExists( KernelStatement statement, long id ) { return entityReadOperations.nodeExists( statement, id ); } + + @Override + public PrimitiveIntSet relationshipTypes( KernelStatement statement, NodeItem nodeItem ) + { + return entityReadOperations.relationshipTypes( statement, nodeItem ); + } + + @Override + public int degree( KernelStatement statement, NodeItem nodeItem, Direction direction ) + { + return entityReadOperations.degree( statement, nodeItem, direction ); + } + + @Override + public int degree( KernelStatement statement, NodeItem nodeItem, Direction direction, int relType ) + { + return entityReadOperations.degree( statement, nodeItem, direction, relType ); + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/GuardingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/GuardingStatementOperations.java index 08cccedf34d90..e10363bf67a0d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/GuardingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/GuardingStatementOperations.java @@ -20,6 +20,7 @@ package org.neo4j.kernel.impl.api; import org.neo4j.collection.primitive.PrimitiveIntIterator; +import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.cursor.Cursor; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; @@ -35,6 +36,7 @@ import org.neo4j.kernel.guard.Guard; import org.neo4j.kernel.impl.api.operations.EntityReadOperations; import org.neo4j.kernel.impl.api.operations.EntityWriteOperations; +import org.neo4j.storageengine.api.Direction; import org.neo4j.storageengine.api.NodeItem; import org.neo4j.storageengine.api.RelationshipItem; @@ -336,4 +338,25 @@ public boolean nodeExists( KernelStatement statement, long id ) guard.check( statement ); return entityReadDelegate.nodeExists( statement, id ); } + + @Override + public PrimitiveIntSet relationshipTypes( KernelStatement statement, NodeItem nodeItem ) + { + guard.check( statement ); + return entityReadDelegate.relationshipTypes( statement, nodeItem ); + } + + @Override + public int degree( KernelStatement statement, NodeItem nodeItem, Direction direction ) + { + guard.check( statement ); + return entityReadDelegate.degree( statement, nodeItem, direction ); + } + + @Override + public int degree( KernelStatement statement, NodeItem nodeItem, Direction direction, int relType ) + { + guard.check( statement ); + return entityReadDelegate.degree( statement, nodeItem, direction, relType ); + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java index eb34e8d0f9796..82617cdfa0862 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java @@ -37,14 +37,12 @@ import org.neo4j.kernel.api.query.ExecutingQuery; import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.LegacyIndexHits; -import org.neo4j.kernel.api.TokenWriteOperations; -import org.neo4j.kernel.api.schema.NodePropertyDescriptor; import org.neo4j.kernel.api.ProcedureCallOperations; import org.neo4j.kernel.api.QueryRegistryOperations; import org.neo4j.kernel.api.ReadOperations; -import org.neo4j.kernel.api.schema.RelationshipPropertyDescriptor; import org.neo4j.kernel.api.SchemaWriteOperations; import org.neo4j.kernel.api.StatementConstants; +import org.neo4j.kernel.api.TokenWriteOperations; import org.neo4j.kernel.api.constraints.NodePropertyConstraint; import org.neo4j.kernel.api.constraints.NodePropertyExistenceConstraint; import org.neo4j.kernel.api.constraints.PropertyConstraint; @@ -72,7 +70,6 @@ import org.neo4j.kernel.api.exceptions.schema.IndexBrokenKernelException; import org.neo4j.kernel.api.exceptions.schema.SchemaRuleNotFoundException; import org.neo4j.kernel.api.exceptions.schema.TooManyLabelsException; -import org.neo4j.kernel.api.schema.IndexDescriptor; import org.neo4j.kernel.api.index.InternalIndexState; import org.neo4j.kernel.api.proc.BasicContext; import org.neo4j.kernel.api.proc.CallableUserAggregationFunction; @@ -82,6 +79,8 @@ import org.neo4j.kernel.api.proc.UserFunctionSignature; import org.neo4j.kernel.api.properties.DefinedProperty; import org.neo4j.kernel.api.properties.Property; +import org.neo4j.kernel.api.schema.NodePropertyDescriptor; +import org.neo4j.kernel.api.schema.RelationshipPropertyDescriptor; import org.neo4j.kernel.api.schema_new.SchemaBoundary; import org.neo4j.kernel.api.schema_new.SchemaDescriptorFactory; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -113,7 +112,6 @@ import org.neo4j.storageengine.api.schema.SchemaRule; import static java.lang.String.format; -import static org.neo4j.collection.primitive.Primitive.intSet; import static org.neo4j.collection.primitive.PrimitiveIntCollections.deduplicate; public class OperationsFacade @@ -405,7 +403,7 @@ public int nodeGetDegree( long nodeId, Direction direction, int relType ) throws statement.assertOpen(); try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { - return node.get().degree( direction( direction ), relType ); + return dataRead().degree( statement, node.get(), direction( direction ), relType ); } } @@ -415,7 +413,7 @@ public int nodeGetDegree( long nodeId, Direction direction ) throws EntityNotFou statement.assertOpen(); try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { - return node.get().degree( direction( direction ) ); + return dataRead().degree( statement, node.get(), direction( direction ) ); } } @@ -435,7 +433,7 @@ public PrimitiveIntIterator nodeGetRelationshipTypes( long nodeId ) throws Entit statement.assertOpen(); try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { - return node.get().relationshipTypes().iterator(); + return dataRead().relationshipTypes( statement,node.get() ).iterator(); } } 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 f3346cf2d2b05..6bf9262ba3822 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 @@ -22,7 +22,9 @@ import java.util.Iterator; import java.util.Map; +import org.neo4j.collection.primitive.Primitive; import org.neo4j.collection.primitive.PrimitiveIntIterator; +import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; @@ -86,6 +88,7 @@ import org.neo4j.kernel.impl.index.LegacyIndexStore; import org.neo4j.kernel.impl.util.Validators; import org.neo4j.register.Register.DoubleLongRegister; +import org.neo4j.storageengine.api.Direction; import org.neo4j.storageengine.api.EntityType; import org.neo4j.storageengine.api.NodeItem; import org.neo4j.storageengine.api.PropertyItem; @@ -99,6 +102,7 @@ import org.neo4j.storageengine.api.txstate.ReadableDiffSets; import static java.lang.String.format; +import static org.neo4j.collection.primitive.PrimitiveIntCollections.filter; import static org.neo4j.collection.primitive.PrimitiveLongCollections.single; import static org.neo4j.helpers.collection.Iterators.filter; import static org.neo4j.helpers.collection.Iterators.iterator; @@ -107,6 +111,7 @@ import static org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor.Filter.GENERAL; import static org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor.Filter.UNIQUE; import static org.neo4j.kernel.impl.api.PropertyValueComparison.COMPARE_NUMBERS; +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; @@ -1615,6 +1620,72 @@ else if ( txState.nodeIsAddedInThisTx( id ) ) return storeLayer.nodeExists( id ); } + @Override + public PrimitiveIntSet relationshipTypes( KernelStatement statement, NodeItem node ) + { + if ( statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) ) + { + return statement.txState().getNodeState( node.id() ).relationshipTypes(); + } + + // Read types in the current transaction + PrimitiveIntSet types = statement.hasTxStateWithChanges() + ? statement.txState().getNodeState( node.id() ).relationshipTypes() + : Primitive.intSet(); + + // Augment with types stored on disk, minus any types where all rels of that type are deleted + // in current tx. + types.addAll( filter( storeLayer.relationshipTypes( statement.getStoreStatement(), node ).iterator(), + ( current ) -> !types.contains( current ) && degree( statement, node, Direction.BOTH, current ) > 0 ) ); + + return types; + } + + @Override + public int degree( KernelStatement statement, NodeItem node, Direction direction ) + { + int degree = statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) + ? 0 + : computeDegree( statement, node, direction, null ); + + return statement.hasTxStateWithChanges() && augmentDegree( statement, node ) + ? statement.txState().getNodeState( node.id() ).augmentDegree( direction, degree ) + : degree; + } + + @Override + public int degree( KernelStatement statement, NodeItem node, Direction direction, int relType ) + { + int degree = statement.hasTxStateWithChanges() && statement.txState().nodeIsAddedInThisTx( node.id() ) + ? 0 + : computeDegree( statement, node, direction, relType ); + + return statement.hasTxStateWithChanges() && augmentDegree( statement, node ) + ? statement.txState().getNodeState( node.id() ).augmentDegree( direction, degree, relType ) + : degree; + } + + // FIXME: temporary workaround: the node item takes care of the tx state itself, hence don't count it twice! + private boolean augmentDegree( KernelStatement statement, NodeItem node ) + { + return node.isDense() || statement.txState().nodeIsAddedInThisTx( node.id() ); + } + + private int computeDegree( KernelStatement statement, NodeItem node, Direction direction, Integer relType ) + { + if ( node.isDense() ) + { + return storeLayer.degreeRelationshipsInGroup( statement.getStoreStatement(), node.id(), node.nextGroupId(), + direction, relType ); + } + else + { + return count( relType == null + ? node.relationships( direction ) + : node.relationships( direction, relType ) ); + } + } + private static DefinedProperty definedPropertyOrNull( Property existingProperty ) { return existingProperty instanceof DefinedProperty ? (DefinedProperty) existingProperty : null; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursor.java index a420f6ad86295..7f03720a97483 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursor.java @@ -33,7 +33,6 @@ import org.neo4j.storageengine.api.txstate.NodeState; import static org.neo4j.collection.primitive.Primitive.intSet; -import static org.neo4j.collection.primitive.PrimitiveIntCollections.filter; import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_RELATIONSHIP; import static org.neo4j.kernel.impl.util.Cursors.empty; @@ -166,38 +165,6 @@ public Cursor relationships( Direction direction ) return state.augmentNodeRelationshipCursor( cursor, nodeState, direction, null ); } - @Override - public PrimitiveIntSet relationshipTypes() - { - if ( nodeIsAddedInThisTx ) - { - return nodeState.relationshipTypes(); - } - - // Read types in the current transaction - PrimitiveIntSet types = nodeState.relationshipTypes(); - - // Augment with types stored on disk, minus any types where all rels of that type are deleted - // in current tx. - types.addAll( filter( cursor.get().relationshipTypes().iterator(), - ( current ) -> !types.contains( current ) && degree( Direction.BOTH, current ) > 0 ) ); - - return types; - } - - @Override - public int degree( Direction direction ) - { - return nodeState.augmentDegree( direction, nodeIsAddedInThisTx ? 0 : cursor.get().degree( direction ) ); - } - - @Override - public int degree( Direction direction, int relType ) - { - int degree = nodeIsAddedInThisTx ? 0 : cursor.get().degree( direction, relType ); - return nodeState.augmentDegree( direction, degree, relType ); - } - @Override public boolean isDense() { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java index 91822479afaaa..de7d7ee231640 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java @@ -20,6 +20,7 @@ package org.neo4j.kernel.impl.api.operations; import org.neo4j.collection.primitive.PrimitiveIntIterator; +import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.cursor.Cursor; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; @@ -28,6 +29,7 @@ import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; import org.neo4j.kernel.impl.api.KernelStatement; import org.neo4j.kernel.impl.api.RelationshipVisitor; +import org.neo4j.storageengine.api.Direction; import org.neo4j.storageengine.api.NodeItem; import org.neo4j.storageengine.api.RelationshipItem; @@ -147,4 +149,37 @@ Cursor relationshipCursorById( KernelStatement statement, long long relationshipsGetCount( KernelStatement statement ); boolean nodeExists( KernelStatement statement, long id ); + + /** + * Returns the set of types for relationships attached to this node. + * + * @param statement the current kernel statement + * @param nodeItem the node + * @return the set of types for relationships attached to this node. + * @throws IllegalStateException if no current node is selected + */ + PrimitiveIntSet relationshipTypes( KernelStatement statement, NodeItem nodeItem ); + + /** + * Returns degree, e.g. number of relationships for this node. + * + * @param statement the current kernel statement + * @param nodeItem the node + * @param direction {@link Direction} filter when counting relationships, e.g. only + * {@link Direction#OUTGOING outgoing} or {@link Direction#INCOMING incoming}. + * @return degree of relationships in the given direction. + */ + int degree( KernelStatement statement, NodeItem nodeItem, Direction direction ); + + /** + * Returns degree, e.g. number of relationships for this node. + * + * @param statement the current kernel statement + * @param nodeItem the node + * @param direction {@link Direction} filter on when counting relationships, e.g. only + * {@link Direction#OUTGOING outgoing} or {@link Direction#INCOMING incoming}. + * @param relType relationship type id to filter when counting relationships. + * @return degree of relationships in the given direction and relationship type. + */ + int degree( KernelStatement statement, NodeItem nodeItem, Direction direction, int relType ); } 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 22a1f9af4a96f..d7cb44f037d14 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 @@ -24,6 +24,7 @@ import java.util.function.Supplier; import org.neo4j.collection.primitive.PrimitiveIntIterator; +import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.cursor.Cursor; import org.neo4j.graphdb.TransactionFailureException; @@ -79,9 +80,11 @@ import org.neo4j.storageengine.api.schema.PopulationProgress; import org.neo4j.storageengine.api.schema.SchemaRule; +import static org.neo4j.collection.primitive.Primitive.intSet; import static org.neo4j.helpers.collection.Iterables.filter; import static org.neo4j.kernel.api.schema_new.SchemaDescriptorPredicates.hasLabel; 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; @@ -503,6 +506,29 @@ public boolean nodeExists( long id ) return nodeStore.isInUse( id ); } + @Override + public PrimitiveIntSet relationshipTypes( StorageStatement statement, NodeItem node ) + { + PrimitiveIntSet set = intSet(); + if ( node.isDense() ) + { + RelationshipGroupRecord groupRecord = relationshipGroupStore.newRecord(); + RecordCursor cursor = statement.recordCursors().relationshipGroup(); + for ( long id = node.nextGroupId(); id != NO_NEXT_RELATIONSHIP.intValue(); id = groupRecord.getNext() ) + { + if ( cursor.next( id, groupRecord, FORCE ) ) + { + set.add( groupRecord.getType() ); + } + } + } + else + { + node.relationships( Direction.BOTH ).forAll( relationship -> set.add( relationship.type() ) ); + } + return set; + } + @Override public void degrees( StorageStatement statement, NodeItem nodeItem, DegreeVisitor visitor ) { @@ -529,6 +555,16 @@ private IndexRule indexRule( NewIndexDescriptor index, NewIndexDescriptor.Filter return schemaStorage.indexGetForSchema( index.schema(), filter ); } + @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() ); + } + private void visitNode( NodeItem nodeItem, DegreeVisitor visitor ) { try ( Cursor relationships = nodeItem.relationships( Direction.BOTH ) ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursor.java index 68f732c75e018..4bec5aeb8281c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursor.java @@ -47,12 +47,10 @@ import org.neo4j.storageengine.api.RelationshipItem; import static org.neo4j.collection.primitive.Primitive.intSet; -import static org.neo4j.kernel.impl.api.store.DegreeCounter.countRelationshipsInGroup; import static org.neo4j.kernel.impl.locking.LockService.NO_LOCK_SERVICE; 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; -import static org.neo4j.kernel.impl.util.Cursors.count; import static org.neo4j.kernel.impl.util.IoPrimitiveUtils.safeCastLongToInt; /** @@ -242,56 +240,6 @@ public Cursor relationships( Direction direction, int... relTy return cursors.relationships( isDense(), nextRel(), id(), direction, relTypes ); } - @Override - public PrimitiveIntSet relationshipTypes() - { - PrimitiveIntSet set = intSet(); - if ( isDense() ) - { - RelationshipGroupRecord groupRecord = relationshipGroupStore.newRecord(); - for ( long id = nextGroupId(); id != NO_NEXT_RELATIONSHIP.intValue(); id = groupRecord.getNext() ) - { - if ( recordCursors.relationshipGroup().next( id, groupRecord, FORCE ) ) - { - set.add( groupRecord.getType() ); - } - } - } - else - { - relationships( Direction.BOTH ).forAll( relationship -> set.add( relationship.type() ) ); - } - return set; - } - - @Override - public int degree( Direction direction ) - { - if ( isDense() ) - { - return countRelationshipsInGroup( nextGroupId(), direction, null, id(), relationshipStore.newRecord(), - relationshipGroupStore.newRecord(), recordCursors ); - } - else - { - return count( relationships( direction ) ); - } - } - - @Override - public int degree( Direction direction, int relType ) - { - if ( isDense() ) - { - return countRelationshipsInGroup( nextGroupId(), direction, relType, id(), relationshipStore.newRecord(), - relationshipGroupStore.newRecord(), recordCursors ); - } - else - { - return count( relationships( direction, relType ) ); - } - } - @Override public boolean isDense() { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/TransactionStats.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/TransactionStats.java index a62c9959f3331..61f08122711f1 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/TransactionStats.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/TransactionStats.java @@ -165,6 +165,6 @@ private void incrementCounter( AtomicLong readCount, AtomicLong writeCount, bool private void decrementCounter( AtomicLong readCount, AtomicLong writeCount, boolean write ) { long count = write ? writeCount.decrementAndGet() : readCount.decrementAndGet(); - assert count >= 0; + assert count >= 0 : "count is " + count; } } diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/NodeItem.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/NodeItem.java index 548d8f999e7e5..1471bf74f75f2 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/NodeItem.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/NodeItem.java @@ -46,31 +46,6 @@ public interface NodeItem */ Cursor relationships( Direction direction ); - /** - * @return relationship type cursor for relationships attached to this node. - * @throws IllegalStateException if no current node is selected - */ - PrimitiveIntSet relationshipTypes(); - - /** - * Returns degree, e.g. number of relationships for this node. - * - * @param direction {@link Direction} filter when counting relationships, e.g. only - * {@link Direction#OUTGOING outgoing} or {@link Direction#INCOMING incoming}. - * @return degree of relationships in the given direction. - */ - int degree( Direction direction ); - - /** - * Returns degree, e.g. number of relationships for this node. - * - * @param direction {@link Direction} filter on when counting relationships, e.g. only - * {@link Direction#OUTGOING outgoing} or {@link Direction#INCOMING incoming}. - * @param typeId relationship type id to filter when counting relationships. - * @return degree of relationships in the given direction and relationship type. - */ - int degree( Direction direction, int typeId ); - /** * @return whether or not this node has been marked as being dense, i.e. exceeding a certain threshold * of number of relationships. 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 410a5d8430443..293599c9537b2 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 @@ -22,6 +22,7 @@ import java.util.Iterator; import org.neo4j.collection.primitive.PrimitiveIntIterator; +import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.kernel.api.constraints.NodePropertyConstraint; import org.neo4j.kernel.api.constraints.PropertyConstraint; @@ -370,5 +371,10 @@ DoubleLongRegister indexSample( LabelSchemaDescriptor descriptor, DoubleLongRegi boolean nodeExists( long id ); + PrimitiveIntSet relationshipTypes( StorageStatement statement, NodeItem node ); + void degrees( StorageStatement statement, NodeItem nodeItem, DegreeVisitor visitor ); + + int degreeRelationshipsInGroup( StorageStatement storeStatement, long id, long groupId, Direction direction, + Integer relType ); } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StubCursors.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StubCursors.java index 0a56ddad63331..5953db8bb0726 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StubCursors.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StubCursors.java @@ -161,24 +161,6 @@ public Cursor relationships( Direction direction ) throw new UnsupportedOperationException(); } - @Override - public PrimitiveIntSet relationshipTypes() - { - throw new UnsupportedOperationException(); - } - - @Override - public int degree( Direction direction ) - { - throw new UnsupportedOperationException(); - } - - @Override - public int degree( Direction direction, int relType ) - { - throw new UnsupportedOperationException(); - } - @Override public boolean isDense() { diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerDegreeTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerDegreeTest.java deleted file mode 100644 index 7e02f72ddb2c9..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerDegreeTest.java +++ /dev/null @@ -1,370 +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.junit.Rule; -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; -import java.util.function.Consumer; - -import org.neo4j.graphdb.DependencyResolver; -import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Transaction; -import org.neo4j.graphdb.factory.GraphDatabaseSettings; -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; -import org.neo4j.kernel.impl.store.record.RecordLoad; -import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; -import org.neo4j.kernel.impl.store.record.RelationshipRecord; -import org.neo4j.storageengine.api.NodeItem; -import org.neo4j.test.TestGraphDatabaseFactory; -import org.neo4j.test.rule.RandomRule; - -import static java.util.Arrays.asList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -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; -import static org.neo4j.kernel.impl.api.store.TestRelType.OUT; -import static org.neo4j.kernel.impl.core.TokenHolder.NO_ID; -import static org.neo4j.kernel.impl.locking.LockService.NO_LOCK_SERVICE; -import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_RELATIONSHIP; - -public class StorageLayerDegreeTest extends StorageLayerTest -{ - private static final int RELATIONSHIPS_COUNT = 20; - - @Rule - public final RandomRule random = new RandomRule(); - - protected GraphDatabaseService createGraphDatabase() - { - return new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder() - .setConfig( GraphDatabaseSettings.dense_node_threshold, String.valueOf( RELATIONSHIPS_COUNT ) ) - .newGraphDatabase(); - } - - @Test - public void degreesForDenseNodeWithPartiallyDeletedRelGroupChain() throws Exception - { - testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain(); - - testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( IN ); - testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( OUT ); - testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( LOOP ); - - testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( IN, OUT ); - testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( OUT, LOOP ); - testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( IN, LOOP ); - - testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( IN, OUT, LOOP ); - } - - @Test - public void degreesForDenseNodeWithPartiallyDeletedRelChains() throws Exception - { - testDegreesForDenseNodeWithPartiallyDeletedRelChains( false, false, false ); - - testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, false, false ); - testDegreesForDenseNodeWithPartiallyDeletedRelChains( false, true, false ); - testDegreesForDenseNodeWithPartiallyDeletedRelChains( false, false, true ); - - testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, true, false ); - testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, true, true ); - testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, false, true ); - - testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, true, true ); - } - - private void testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType... typesToDelete ) - throws Exception - { - int inRelCount = randomRelCount(); - int outRelCount = randomRelCount(); - int loopRelCount = randomRelCount(); - - long nodeId = createNode( inRelCount, outRelCount, loopRelCount ); - StoreSingleNodeCursor cursor = newCursor( nodeId ); - - for ( TestRelType type : typesToDelete ) - { - markRelGroupNotInUse( nodeId, type ); - switch ( type ) - { - case IN: - inRelCount = 0; - break; - case OUT: - outRelCount = 0; - break; - case LOOP: - loopRelCount = 0; - break; - default: - throw new IllegalArgumentException( "Unknown type: " + type ); - } - } - - Set expectedDegrees = new HashSet<>(); - if ( outRelCount != 0 ) - { - expectedDegrees.add( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0 ) ); - } - if ( inRelCount != 0 ) - { - expectedDegrees.add( new TestDegreeItem( relTypeId( IN ), 0, inRelCount ) ); - } - if ( loopRelCount != 0 ) - { - expectedDegrees.add( new TestDegreeItem( relTypeId( LOOP ), loopRelCount, loopRelCount ) ); - } - - Set actualDegrees = degrees( cursor ); - - assertEquals( expectedDegrees, actualDegrees ); - } - - private void testDegreesForDenseNodeWithPartiallyDeletedRelChains( boolean modifyInChain, boolean modifyOutChain, - boolean modifyLoopChain ) - { - int inRelCount = randomRelCount(); - int outRelCount = randomRelCount(); - int loopRelCount = randomRelCount(); - - long nodeId = createNode( inRelCount, outRelCount, loopRelCount ); - StoreSingleNodeCursor cursor = newCursor( nodeId ); - - if ( modifyInChain ) - { - markRandomRelsInGroupNotInUse( nodeId, IN ); - } - if ( modifyOutChain ) - { - markRandomRelsInGroupNotInUse( nodeId, OUT ); - } - if ( modifyLoopChain ) - { - markRandomRelsInGroupNotInUse( nodeId, LOOP ); - } - - Set expectedDegrees = new HashSet<>( - asList( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0 ), - new TestDegreeItem( relTypeId( IN ), 0, inRelCount ), - new TestDegreeItem( relTypeId( LOOP ), loopRelCount, loopRelCount ) ) ); - - Set actualDegrees = degrees( cursor.get() ); - - assertEquals( expectedDegrees, actualDegrees ); - } - - private Set degrees( NodeItem nodeItem ) - { - Set degrees = new HashSet<>(); - disk.degrees( disk.newStatement(), nodeItem, - ( type, outgoing, incoming ) -> degrees.add( new TestDegreeItem( type, outgoing, incoming ) ) ); - return degrees; - } - - @SuppressWarnings( "unchecked" ) - private StoreSingleNodeCursor newCursor( long nodeId ) - { - StoreSingleNodeCursor cursor = - new StoreSingleNodeCursor( new NodeRecord( -1 ), resolveNeoStores(), mock( Consumer.class ), - new RecordCursors( resolveNeoStores() ), NO_LOCK_SERVICE ); - - cursor.init( nodeId ); - assertTrue( cursor.next() ); - - return cursor; - } - - private void markRandomRelsInGroupNotInUse( long nodeId, TestRelType type ) - { - NodeRecord node = getNodeRecord( nodeId ); - assertTrue( node.isDense() ); - - long relGroupId = node.getNextRel(); - while ( relGroupId != NO_NEXT_RELATIONSHIP.intValue() ) - { - RelationshipGroupRecord relGroup = getRelGroupRecord( relGroupId ); - - if ( type == relTypeForId( relGroup.getType() ) ) - { - markRandomRelsInChainNotInUse( relGroup.getFirstOut() ); - markRandomRelsInChainNotInUse( relGroup.getFirstIn() ); - markRandomRelsInChainNotInUse( relGroup.getFirstLoop() ); - return; - } - - relGroupId = relGroup.getNext(); - } - - throw new IllegalStateException( "No relationship group with type: " + type + " found" ); - } - - private void markRandomRelsInChainNotInUse( long relId ) - { - if ( relId != NO_NEXT_RELATIONSHIP.intValue() ) - { - RelationshipRecord record = getRelRecord( relId ); - - boolean shouldBeMarked = random.nextBoolean(); - if ( shouldBeMarked ) - { - record.setInUse( false ); - update( record ); - } - - markRandomRelsInChainNotInUse( record.getFirstNextRel() ); - boolean isLoopRelationship = record.getFirstNextRel() == record.getSecondNextRel(); - if ( !isLoopRelationship ) - { - markRandomRelsInChainNotInUse( record.getSecondNextRel() ); - } - } - } - - private void markRelGroupNotInUse( long nodeId, TestRelType... types ) - { - NodeRecord node = getNodeRecord( nodeId ); - assertTrue( node.isDense() ); - - Set typesToRemove = asSet( types ); - - long relGroupId = node.getNextRel(); - while ( relGroupId != NO_NEXT_RELATIONSHIP.intValue() ) - { - RelationshipGroupRecord relGroup = getRelGroupRecord( relGroupId ); - TestRelType type = relTypeForId( relGroup.getType() ); - - if ( typesToRemove.contains( type ) ) - { - relGroup.setInUse( false ); - update( relGroup ); - } - - relGroupId = relGroup.getNext(); - } - } - - private int relTypeId( TestRelType type ) - { - DependencyResolver resolver = db.getDependencyResolver(); - RelationshipTypeTokenHolder relTypeHolder = resolver.resolveDependency( RelationshipTypeTokenHolder.class ); - int id = relTypeHolder.getIdByName( type.name() ); - assertNotEquals( NO_ID, id ); - return id; - } - - private long createNode( int inRelCount, int outRelCount, int loopRelCount ) - { - Node node; - try ( Transaction tx = db.beginTx() ) - { - node = db.createNode(); - for ( int i = 0; i < inRelCount; i++ ) - { - Node start = db.createNode(); - start.createRelationshipTo( node, IN ); - } - for ( int i = 0; i < outRelCount; i++ ) - { - Node end = db.createNode(); - node.createRelationshipTo( end, OUT ); - } - for ( int i = 0; i < loopRelCount; i++ ) - { - node.createRelationshipTo( node, LOOP ); - } - tx.success(); - } - return node.getId(); - } - - private TestRelType relTypeForId( int id ) - { - DependencyResolver resolver = db.getDependencyResolver(); - RelationshipTypeTokenHolder relTypeHolder = resolver.resolveDependency( RelationshipTypeTokenHolder.class ); - try - { - String typeName = relTypeHolder.getTokenById( id ).name(); - return TestRelType.valueOf( typeName ); - } - catch ( TokenNotFoundException e ) - { - throw new RuntimeException( e ); - } - } - - private static R getRecord( RecordStore store, long id ) - { - return RecordStore.getRecord( store, id, RecordLoad.FORCE ); - } - - private NodeRecord getNodeRecord( long id ) - { - return getRecord( resolveNeoStores().getNodeStore(), id ); - } - - private RelationshipRecord getRelRecord( long id ) - { - return getRecord( resolveNeoStores().getRelationshipStore(), id ); - } - - private RelationshipGroupRecord getRelGroupRecord( long id ) - { - return getRecord( resolveNeoStores().getRelationshipGroupStore(), id ); - } - - private void update( RelationshipGroupRecord record ) - { - resolveNeoStores().getRelationshipGroupStore().updateRecord( record ); - } - - private void update( RelationshipRecord record ) - { - resolveNeoStores().getRelationshipStore().updateRecord( record ); - } - - private NeoStores resolveNeoStores() - { - DependencyResolver resolver = db.getDependencyResolver(); - RecordStorageEngine storageEngine = resolver.resolveDependency( RecordStorageEngine.class ); - return storageEngine.testAccessNeoStores(); - } - - private int randomRelCount() - { - return RELATIONSHIPS_COUNT + random.nextInt( 20 ); - } - -} diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerRelTypesAndDegreeTest.java similarity index 63% rename from community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursorTest.java rename to community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerRelTypesAndDegreeTest.java index c0b18556dc89e..ba54df08de251 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursorTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerRelTypesAndDegreeTest.java @@ -19,16 +19,16 @@ */ package org.neo4j.kernel.impl.api.store; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.RuleChain; +import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; import java.util.function.LongConsumer; import org.neo4j.graphdb.DependencyResolver; +import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.factory.GraphDatabaseSettings; @@ -40,14 +40,15 @@ import org.neo4j.kernel.impl.store.RecordStore; import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; import org.neo4j.kernel.impl.store.record.NodeRecord; -import org.neo4j.kernel.impl.store.record.Record; import org.neo4j.kernel.impl.store.record.RecordLoad; import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; import org.neo4j.kernel.impl.store.record.RelationshipRecord; -import org.neo4j.test.rule.DatabaseRule; -import org.neo4j.test.rule.ImpermanentDatabaseRule; +import org.neo4j.storageengine.api.Direction; +import org.neo4j.storageengine.api.NodeItem; +import org.neo4j.test.TestGraphDatabaseFactory; import org.neo4j.test.rule.RandomRule; +import static java.util.Arrays.asList; import static java.util.Collections.emptySet; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -55,75 +56,78 @@ import static org.mockito.Mockito.mock; import static org.neo4j.collection.primitive.PrimitiveIntCollections.mapToSet; 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; +import static org.neo4j.kernel.impl.api.store.TestRelType.OUT; import static org.neo4j.kernel.impl.core.TokenHolder.NO_ID; import static org.neo4j.kernel.impl.locking.LockService.NO_LOCK_SERVICE; +import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_RELATIONSHIP; import static org.neo4j.storageengine.api.Direction.BOTH; import static org.neo4j.storageengine.api.Direction.INCOMING; import static org.neo4j.storageengine.api.Direction.OUTGOING; -public class StoreSingleNodeCursorTest +public class StorageLayerRelTypesAndDegreeTest extends StorageLayerTest { private static final int RELATIONSHIPS_COUNT = 20; - private final RandomRule random = new RandomRule(); - private final DatabaseRule db = new ImpermanentDatabaseRule().startLazily(); - @Rule - public final RuleChain ruleChain = RuleChain.outerRule( random ).around( db ); + public final RandomRule random = new RandomRule(); - @Before - public void startDb() throws Exception + protected GraphDatabaseService createGraphDatabase() { - db.setConfig( GraphDatabaseSettings.dense_node_threshold, String.valueOf( RELATIONSHIPS_COUNT ) ); + return new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder() + .setConfig( GraphDatabaseSettings.dense_node_threshold, String.valueOf( RELATIONSHIPS_COUNT ) ) + .newGraphDatabase(); } @Test - public void relationshipTypesForDenseNodeWithPartiallyDeletedRelGroupChain() throws Exception + public void degreesForDenseNodeWithPartiallyDeletedRelGroupChain() throws Exception { - testRelationshipTypesForDenseNode( this::noNodeChange, - asSet( TestRelType.IN, TestRelType.OUT, TestRelType.LOOP ) ); + testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain(); - testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.IN ), - asSet( TestRelType.OUT, TestRelType.LOOP ) ); - testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.OUT ), - asSet( TestRelType.IN, TestRelType.LOOP ) ); - testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.LOOP ), - asSet( TestRelType.IN, TestRelType.OUT ) ); + testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( IN ); + testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( OUT ); + testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( LOOP ); - testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.IN, TestRelType.OUT ), - asSet( TestRelType.LOOP ) ); - testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.IN, TestRelType.LOOP ), - asSet( TestRelType.OUT ) ); - testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.OUT, TestRelType.LOOP ), - asSet( TestRelType.IN ) ); + testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( IN, OUT ); + testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( OUT, LOOP ); + testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( IN, LOOP ); - testRelationshipTypesForDenseNode( - nodeId -> markRelGroupNotInUse( nodeId, TestRelType.IN, TestRelType.OUT, TestRelType.LOOP ), - emptySet() ); + testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( IN, OUT, LOOP ); } @Test - public void relationshipTypesForDenseNodeWithPartiallyDeletedRelChains() throws Exception + public void degreesForDenseNodeWithPartiallyDeletedRelChains() throws Exception { - testRelationshipTypesForDenseNode( this::markRandomRelsNotInUse, - asSet( TestRelType.IN, TestRelType.OUT, TestRelType.LOOP ) ); + testDegreesForDenseNodeWithPartiallyDeletedRelChains( false, false, false ); + + testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, false, false ); + testDegreesForDenseNodeWithPartiallyDeletedRelChains( false, true, false ); + testDegreesForDenseNodeWithPartiallyDeletedRelChains( false, false, true ); + + testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, true, false ); + testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, true, true ); + testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, false, true ); + + testDegreesForDenseNodeWithPartiallyDeletedRelChains( true, true, true ); } + @Test public void degreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain() { testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain(); - testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.IN ); - testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.OUT ); - testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.LOOP ); + testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( IN ); + testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( OUT ); + testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( LOOP ); - testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.IN, TestRelType.OUT ); - testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.IN, TestRelType.LOOP ); - testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.OUT, TestRelType.LOOP ); + testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( IN, OUT ); + testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( IN, LOOP ); + testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( OUT, LOOP ); - testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.IN, TestRelType.OUT, - TestRelType.LOOP ); + testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( IN, OUT, + LOOP ); } @Test @@ -147,16 +151,16 @@ public void degreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChai { testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain(); - testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.IN ); - testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.OUT ); - testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.LOOP ); + testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( IN ); + testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( OUT ); + testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( LOOP ); - testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.IN, TestRelType.OUT ); - testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.OUT, TestRelType.LOOP ); - testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.IN, TestRelType.LOOP ); + testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( IN, OUT ); + testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( OUT, LOOP ); + testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( IN, LOOP ); - testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType.IN, TestRelType.OUT, - TestRelType.LOOP ); + testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( IN, OUT, + LOOP ); } @Test @@ -175,20 +179,6 @@ public void degreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelChains() testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelChains( true, true, true ); } - private void testRelationshipTypesForDenseNode( LongConsumer nodeChanger, Set expectedTypes ) - { - int inRelCount = randomRelCount(); - int outRelCount = randomRelCount(); - int loopRelCount = randomRelCount(); - - long nodeId = createNode( inRelCount, outRelCount, loopRelCount ); - nodeChanger.accept( nodeId ); - - StoreSingleNodeCursor cursor = newCursor( nodeId ); - - assertEquals( expectedTypes, relTypes( cursor ) ); - } - private void testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType... typesToDelete ) { int inRelCount = randomRelCount(); @@ -217,9 +207,9 @@ private void testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelGroupChain( } } - assertEquals( outRelCount + loopRelCount, cursor.degree( OUTGOING ) ); - assertEquals( inRelCount + loopRelCount, cursor.degree( INCOMING ) ); - assertEquals( inRelCount + outRelCount + loopRelCount, cursor.degree( BOTH ) ); + assertEquals( outRelCount + loopRelCount, degreeForDirection( cursor, OUTGOING ) ); + assertEquals( inRelCount + loopRelCount, degreeForDirection( cursor, INCOMING ) ); + assertEquals( inRelCount + outRelCount + loopRelCount, degreeForDirection( cursor, BOTH ) ); } private void testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelChains( boolean modifyInChain, @@ -234,20 +224,31 @@ private void testDegreeByDirectionForDenseNodeWithPartiallyDeletedRelChains( boo if ( modifyInChain ) { - markRandomRelsInGroupNotInUse( nodeId, TestRelType.IN ); + markRandomRelsInGroupNotInUse( nodeId, IN ); } if ( modifyOutChain ) { - markRandomRelsInGroupNotInUse( nodeId, TestRelType.OUT ); + markRandomRelsInGroupNotInUse( nodeId, OUT ); } if ( modifyLoopChain ) { - markRandomRelsInGroupNotInUse( nodeId, TestRelType.LOOP ); + markRandomRelsInGroupNotInUse( nodeId, LOOP ); } - assertEquals( outRelCount + loopRelCount, cursor.degree( OUTGOING ) ); - assertEquals( inRelCount + loopRelCount, cursor.degree( INCOMING ) ); - assertEquals( inRelCount + outRelCount + loopRelCount, cursor.degree( BOTH ) ); + assertEquals( outRelCount + loopRelCount, degreeForDirection( cursor, OUTGOING ) ); + assertEquals( inRelCount + loopRelCount, degreeForDirection( cursor, INCOMING ) ); + assertEquals( inRelCount + outRelCount + loopRelCount, degreeForDirection( cursor, BOTH ) ); + } + + private int degreeForDirection( StoreSingleNodeCursor cursor, Direction direction ) + { + return disk + .degreeRelationshipsInGroup( disk.newStatement(), cursor.id(), cursor.nextGroupId(), direction, null ); + } + private int degreeForDirectionAndType( StoreSingleNodeCursor cursor, Direction direction, int relType ) + { + return disk.degreeRelationshipsInGroup( disk.newStatement(), cursor.id(), cursor.nextGroupId(), direction, + relType ); } private void testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGroupChain( @@ -279,17 +280,17 @@ private void testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelGrou } } - assertEquals( 0, cursor.degree( OUTGOING, relTypeId( TestRelType.IN ) ) ); - assertEquals( outRelCount, cursor.degree( OUTGOING, relTypeId( TestRelType.OUT ) ) ); - assertEquals( loopRelCount, cursor.degree( OUTGOING, relTypeId( TestRelType.LOOP ) ) ); + assertEquals( 0, degreeForDirectionAndType( cursor, OUTGOING, relTypeId( IN ) ) ); + assertEquals( outRelCount, degreeForDirectionAndType( cursor, OUTGOING, relTypeId( OUT ) ) ); + assertEquals( loopRelCount, degreeForDirectionAndType( cursor, OUTGOING, relTypeId( LOOP ) ) ); - assertEquals( 0, cursor.degree( INCOMING, relTypeId( TestRelType.OUT ) ) ); - assertEquals( inRelCount, cursor.degree( INCOMING, relTypeId( TestRelType.IN ) ) ); - assertEquals( loopRelCount, cursor.degree( INCOMING, relTypeId( TestRelType.LOOP ) ) ); + assertEquals( 0, degreeForDirectionAndType( cursor, INCOMING, relTypeId( OUT ) ) ); + assertEquals( inRelCount, degreeForDirectionAndType( cursor, INCOMING, relTypeId( IN ) ) ); + assertEquals( loopRelCount, degreeForDirectionAndType( cursor, INCOMING, relTypeId( LOOP ) ) ); - assertEquals( inRelCount, cursor.degree( BOTH, relTypeId( TestRelType.IN ) ) ); - assertEquals( outRelCount, cursor.degree( BOTH, relTypeId( TestRelType.OUT ) ) ); - assertEquals( loopRelCount, cursor.degree( BOTH, relTypeId( TestRelType.LOOP ) ) ); + assertEquals( inRelCount, degreeForDirectionAndType( cursor, BOTH, relTypeId( IN ) ) ); + assertEquals( outRelCount, degreeForDirectionAndType( cursor, BOTH, relTypeId( OUT ) ) ); + assertEquals( loopRelCount, degreeForDirectionAndType( cursor, BOTH, relTypeId( LOOP ) ) ); } private void testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelChains( boolean modifyInChain, @@ -304,105 +305,185 @@ private void testDegreeByDirectionAndTypeForDenseNodeWithPartiallyDeletedRelChai if ( modifyInChain ) { - markRandomRelsInGroupNotInUse( nodeId, TestRelType.IN ); + markRandomRelsInGroupNotInUse( nodeId, IN ); } if ( modifyOutChain ) { - markRandomRelsInGroupNotInUse( nodeId, TestRelType.OUT ); + markRandomRelsInGroupNotInUse( nodeId, OUT ); } if ( modifyLoopChain ) { - markRandomRelsInGroupNotInUse( nodeId, TestRelType.LOOP ); + markRandomRelsInGroupNotInUse( nodeId, LOOP ); } - assertEquals( 0, cursor.degree( OUTGOING, relTypeId( TestRelType.IN ) ) ); - assertEquals( outRelCount, cursor.degree( OUTGOING, relTypeId( TestRelType.OUT ) ) ); - assertEquals( loopRelCount, cursor.degree( OUTGOING, relTypeId( TestRelType.LOOP ) ) ); + assertEquals( 0, degreeForDirectionAndType( cursor, OUTGOING, relTypeId( IN ) ) ); + assertEquals( outRelCount, degreeForDirectionAndType( cursor, OUTGOING, relTypeId( OUT ) ) ); + assertEquals( loopRelCount, degreeForDirectionAndType( cursor, OUTGOING, relTypeId( LOOP ) ) ); - assertEquals( 0, cursor.degree( INCOMING, relTypeId( TestRelType.OUT ) ) ); - assertEquals( inRelCount, cursor.degree( INCOMING, relTypeId( TestRelType.IN ) ) ); - assertEquals( loopRelCount, cursor.degree( INCOMING, relTypeId( TestRelType.LOOP ) ) ); + assertEquals( 0, degreeForDirectionAndType( cursor, INCOMING, relTypeId( OUT ) ) ); + assertEquals( inRelCount, degreeForDirectionAndType( cursor, INCOMING, relTypeId( IN ) ) ); + assertEquals( loopRelCount, degreeForDirectionAndType( cursor, INCOMING, relTypeId( LOOP ) ) ); - assertEquals( inRelCount, cursor.degree( BOTH, relTypeId( TestRelType.IN ) ) ); - assertEquals( outRelCount, cursor.degree( BOTH, relTypeId( TestRelType.OUT ) ) ); - assertEquals( loopRelCount, cursor.degree( BOTH, relTypeId( TestRelType.LOOP ) ) ); + assertEquals( inRelCount, degreeForDirectionAndType( cursor, BOTH, relTypeId( IN ) ) ); + assertEquals( outRelCount, degreeForDirectionAndType( cursor, BOTH, relTypeId( OUT ) ) ); + assertEquals( loopRelCount, degreeForDirectionAndType( cursor, BOTH, relTypeId( LOOP ) ) ); } - private Set relTypes( StoreSingleNodeCursor cursor ) + @Test + public void relationshipTypesForDenseNodeWithPartiallyDeletedRelGroupChain() throws Exception { - return mapToSet( cursor.relationshipTypes().iterator(), this::relTypeForId ); + testRelationshipTypesForDenseNode( this::noNodeChange, + asSet( TestRelType.IN, TestRelType.OUT, TestRelType.LOOP ) ); + + testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.IN ), + asSet( TestRelType.OUT, TestRelType.LOOP ) ); + testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.OUT ), + asSet( TestRelType.IN, TestRelType.LOOP ) ); + testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.LOOP ), + asSet( TestRelType.IN, TestRelType.OUT ) ); + + testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.IN, TestRelType.OUT ), + asSet( TestRelType.LOOP ) ); + testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.IN, TestRelType.LOOP ), + asSet( TestRelType.OUT ) ); + testRelationshipTypesForDenseNode( nodeId -> markRelGroupNotInUse( nodeId, TestRelType.OUT, TestRelType.LOOP ), + asSet( TestRelType.IN ) ); + + testRelationshipTypesForDenseNode( + nodeId -> markRelGroupNotInUse( nodeId, TestRelType.IN, TestRelType.OUT, TestRelType.LOOP ), + emptySet() ); } - private TestRelType relTypeForId( int id ) + @Test + public void relationshipTypesForDenseNodeWithPartiallyDeletedRelChains() throws Exception { - DependencyResolver resolver = db.getDependencyResolver(); - RelationshipTypeTokenHolder relTypeHolder = resolver.resolveDependency( RelationshipTypeTokenHolder.class ); - try - { - String typeName = relTypeHolder.getTokenById( id ).name(); - return TestRelType.valueOf( typeName ); - } - catch ( TokenNotFoundException e ) - { - throw new RuntimeException( e ); - } + testRelationshipTypesForDenseNode( this::markRandomRelsNotInUse, + asSet( TestRelType.IN, TestRelType.OUT, TestRelType.LOOP ) ); } - private int relTypeId( TestRelType type ) + private void testRelationshipTypesForDenseNode( LongConsumer nodeChanger, Set expectedTypes ) { - DependencyResolver resolver = db.getDependencyResolver(); - RelationshipTypeTokenHolder relTypeHolder = resolver.resolveDependency( RelationshipTypeTokenHolder.class ); - int id = relTypeHolder.getIdByName( type.name() ); - assertNotEquals( NO_ID, id ); - return id; + int inRelCount = randomRelCount(); + int outRelCount = randomRelCount(); + int loopRelCount = randomRelCount(); + + long nodeId = createNode( inRelCount, outRelCount, loopRelCount ); + nodeChanger.accept( nodeId ); + + StoreSingleNodeCursor cursor = newCursor( nodeId ); + + assertEquals( expectedTypes, relTypes( cursor ) ); } - private long createNode( int inRelCount, int outRelCount, int loopRelCount ) + private Set relTypes( StoreSingleNodeCursor cursor ) { - Node node; - try ( Transaction tx = db.beginTx() ) + return mapToSet( disk.relationshipTypes( disk.newStatement(), cursor.get() ).iterator(), this::relTypeForId ); + } + + private void testDegreesForDenseNodeWithPartiallyDeletedRelGroupChain( TestRelType... typesToDelete ) + throws Exception + { + int inRelCount = randomRelCount(); + int outRelCount = randomRelCount(); + int loopRelCount = randomRelCount(); + + long nodeId = createNode( inRelCount, outRelCount, loopRelCount ); + StoreSingleNodeCursor cursor = newCursor( nodeId ); + + for ( TestRelType type : typesToDelete ) { - node = db.createNode(); - for ( int i = 0; i < inRelCount; i++ ) - { - Node start = db.createNode(); - start.createRelationshipTo( node, TestRelType.IN ); - } - for ( int i = 0; i < outRelCount; i++ ) - { - Node end = db.createNode(); - node.createRelationshipTo( end, TestRelType.OUT ); - } - for ( int i = 0; i < loopRelCount; i++ ) + markRelGroupNotInUse( nodeId, type ); + switch ( type ) { - node.createRelationshipTo( node, TestRelType.LOOP ); + case IN: + inRelCount = 0; + break; + case OUT: + outRelCount = 0; + break; + case LOOP: + loopRelCount = 0; + break; + default: + throw new IllegalArgumentException( "Unknown type: " + type ); } - tx.success(); } - return node.getId(); + + Set expectedDegrees = new HashSet<>(); + if ( outRelCount != 0 ) + { + expectedDegrees.add( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0 ) ); + } + if ( inRelCount != 0 ) + { + expectedDegrees.add( new TestDegreeItem( relTypeId( IN ), 0, inRelCount ) ); + } + if ( loopRelCount != 0 ) + { + expectedDegrees.add( new TestDegreeItem( relTypeId( LOOP ), loopRelCount, loopRelCount ) ); + } + + Set actualDegrees = degrees( cursor ); + + assertEquals( expectedDegrees, actualDegrees ); } - private void markRelGroupNotInUse( long nodeId, TestRelType... types ) + private void testDegreesForDenseNodeWithPartiallyDeletedRelChains( boolean modifyInChain, boolean modifyOutChain, + boolean modifyLoopChain ) { - NodeRecord node = getNodeRecord( nodeId ); - assertTrue( node.isDense() ); + int inRelCount = randomRelCount(); + int outRelCount = randomRelCount(); + int loopRelCount = randomRelCount(); - Set typesToRemove = asSet( types ); + long nodeId = createNode( inRelCount, outRelCount, loopRelCount ); + StoreSingleNodeCursor cursor = newCursor( nodeId ); - long relGroupId = node.getNextRel(); - while ( relGroupId != Record.NO_NEXT_RELATIONSHIP.intValue() ) + if ( modifyInChain ) { - RelationshipGroupRecord relGroup = getRelGroupRecord( relGroupId ); - TestRelType type = relTypeForId( relGroup.getType() ); + markRandomRelsInGroupNotInUse( nodeId, IN ); + } + if ( modifyOutChain ) + { + markRandomRelsInGroupNotInUse( nodeId, OUT ); + } + if ( modifyLoopChain ) + { + markRandomRelsInGroupNotInUse( nodeId, LOOP ); + } - if ( typesToRemove.contains( type ) ) - { - relGroup.setInUse( false ); - update( relGroup ); - } + Set expectedDegrees = new HashSet<>( + asList( new TestDegreeItem( relTypeId( OUT ), outRelCount, 0 ), + new TestDegreeItem( relTypeId( IN ), 0, inRelCount ), + new TestDegreeItem( relTypeId( LOOP ), loopRelCount, loopRelCount ) ) ); - relGroupId = relGroup.getNext(); - } + Set actualDegrees = degrees( cursor.get() ); + + assertEquals( expectedDegrees, actualDegrees ); + } + + private Set degrees( NodeItem nodeItem ) + { + Set degrees = new HashSet<>(); + disk.degrees( disk.newStatement(), nodeItem, + ( type, outgoing, incoming ) -> degrees.add( new TestDegreeItem( type, outgoing, incoming ) ) ); + return degrees; + } + + @SuppressWarnings( "unchecked" ) + private StoreSingleNodeCursor newCursor( long nodeId ) + { + StoreSingleNodeCursor cursor = + new StoreSingleNodeCursor( new NodeRecord( -1 ), resolveNeoStores(), mock( Consumer.class ), + new RecordCursors( resolveNeoStores() ), NO_LOCK_SERVICE ); + + cursor.init( nodeId ); + assertTrue( cursor.next() ); + + return cursor; + } + + private void noNodeChange( long nodeId ) + { } private void markRandomRelsNotInUse( long nodeId ) @@ -419,7 +500,7 @@ private void markRandomRelsInGroupNotInUse( long nodeId, TestRelType type ) assertTrue( node.isDense() ); long relGroupId = node.getNextRel(); - while ( relGroupId != Record.NO_NEXT_RELATIONSHIP.intValue() ) + while ( relGroupId != NO_NEXT_RELATIONSHIP.intValue() ) { RelationshipGroupRecord relGroup = getRelGroupRecord( relGroupId ); @@ -439,7 +520,7 @@ private void markRandomRelsInGroupNotInUse( long nodeId, TestRelType type ) private void markRandomRelsInChainNotInUse( long relId ) { - if ( relId != Record.NO_NEXT_RELATIONSHIP.intValue() ) + if ( relId != NO_NEXT_RELATIONSHIP.intValue() ) { RelationshipRecord record = getRelRecord( relId ); @@ -459,21 +540,76 @@ private void markRandomRelsInChainNotInUse( long relId ) } } - private void noNodeChange( long nodeId ) + private void markRelGroupNotInUse( long nodeId, TestRelType... types ) { + NodeRecord node = getNodeRecord( nodeId ); + assertTrue( node.isDense() ); + + Set typesToRemove = asSet( types ); + + long relGroupId = node.getNextRel(); + while ( relGroupId != NO_NEXT_RELATIONSHIP.intValue() ) + { + RelationshipGroupRecord relGroup = getRelGroupRecord( relGroupId ); + TestRelType type = relTypeForId( relGroup.getType() ); + + if ( typesToRemove.contains( type ) ) + { + relGroup.setInUse( false ); + update( relGroup ); + } + + relGroupId = relGroup.getNext(); + } } - @SuppressWarnings( "unchecked" ) - private StoreSingleNodeCursor newCursor( long nodeId ) + private int relTypeId( TestRelType type ) { - StoreSingleNodeCursor cursor = - new StoreSingleNodeCursor( new NodeRecord( -1 ), resolveNeoStores(), mock( Consumer.class ), - new RecordCursors( resolveNeoStores() ), NO_LOCK_SERVICE ); + DependencyResolver resolver = db.getDependencyResolver(); + RelationshipTypeTokenHolder relTypeHolder = resolver.resolveDependency( RelationshipTypeTokenHolder.class ); + int id = relTypeHolder.getIdByName( type.name() ); + assertNotEquals( NO_ID, id ); + return id; + } - cursor.init( nodeId ); - assertTrue( cursor.next() ); + private long createNode( int inRelCount, int outRelCount, int loopRelCount ) + { + Node node; + try ( Transaction tx = db.beginTx() ) + { + node = db.createNode(); + for ( int i = 0; i < inRelCount; i++ ) + { + Node start = db.createNode(); + start.createRelationshipTo( node, IN ); + } + for ( int i = 0; i < outRelCount; i++ ) + { + Node end = db.createNode(); + node.createRelationshipTo( end, OUT ); + } + for ( int i = 0; i < loopRelCount; i++ ) + { + node.createRelationshipTo( node, LOOP ); + } + tx.success(); + } + return node.getId(); + } - return cursor; + private TestRelType relTypeForId( int id ) + { + DependencyResolver resolver = db.getDependencyResolver(); + RelationshipTypeTokenHolder relTypeHolder = resolver.resolveDependency( RelationshipTypeTokenHolder.class ); + try + { + String typeName = relTypeHolder.getTokenById( id ).name(); + return TestRelType.valueOf( typeName ); + } + catch ( TokenNotFoundException e ) + { + throw new RuntimeException( e ); + } } private static R getRecord( RecordStore store, long id ) @@ -491,11 +627,6 @@ private RelationshipRecord getRelRecord( long id ) return getRecord( resolveNeoStores().getRelationshipStore(), id ); } - private void update( RelationshipRecord record ) - { - resolveNeoStores().getRelationshipStore().updateRecord( record ); - } - private RelationshipGroupRecord getRelGroupRecord( long id ) { return getRecord( resolveNeoStores().getRelationshipGroupStore(), id ); @@ -506,6 +637,11 @@ private void update( RelationshipGroupRecord record ) resolveNeoStores().getRelationshipGroupStore().updateRecord( record ); } + private void update( RelationshipRecord record ) + { + resolveNeoStores().getRelationshipStore().updateRecord( record ); + } + private NeoStores resolveNeoStores() { DependencyResolver resolver = db.getDependencyResolver(); @@ -517,4 +653,5 @@ private int randomRelCount() { return RELATIONSHIPS_COUNT + random.nextInt( 20 ); } + }