diff --git a/community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/codegen/CompiledCursorUtils.java b/community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/codegen/CompiledCursorUtils.java index 9703e573966d2..4c03731c8b788 100644 --- a/community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/codegen/CompiledCursorUtils.java +++ b/community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/codegen/CompiledCursorUtils.java @@ -24,11 +24,8 @@ import org.neo4j.internal.kernel.api.NodeCursor; import org.neo4j.internal.kernel.api.PropertyCursor; import org.neo4j.internal.kernel.api.Read; -import org.neo4j.internal.kernel.api.RelationshipGroupCursor; -import org.neo4j.internal.kernel.api.RelationshipTraversalCursor; -import org.neo4j.internal.kernel.api.helpers.RelationshipDenseSelectionCursor; import org.neo4j.internal.kernel.api.helpers.RelationshipSelectionCursor; -import org.neo4j.internal.kernel.api.helpers.RelationshipSparseSelectionCursor; +import org.neo4j.internal.kernel.api.helpers.RelationshipSelections; import org.neo4j.kernel.api.StatementConstants; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.storageengine.api.EntityType; @@ -102,59 +99,14 @@ public static boolean nodeHasLabel( Read read, NodeCursor nodeCursor, long node, } public static RelationshipSelectionCursor nodeGetRelationships( Read read, CursorFactory cursors, NodeCursor node, - long nodeId, - Direction direction, int[] relTypes ) + long nodeId, Direction direction, int[] types ) { read.singleNode( nodeId, node ); if ( !node.next() ) { return RelationshipSelectionCursor.EMPTY; } - if ( node.isDense() ) - { - RelationshipGroupCursor groupCursor = cursors.allocateRelationshipGroupCursor(); - node.relationships( groupCursor ); - RelationshipTraversalCursor traversalCursor = cursors.allocateRelationshipTraversalCursor(); - RelationshipDenseSelectionCursor selectionCursor = - new RelationshipDenseSelectionCursor(); - switch ( direction ) - { - case OUTGOING: - selectionCursor.outgoing( groupCursor, traversalCursor, relTypes ); - break; - case INCOMING: - selectionCursor.incoming( groupCursor, traversalCursor, relTypes ); - break; - case BOTH: - selectionCursor.all( groupCursor, traversalCursor, relTypes ); - break; - default: - throw new IllegalStateException( "Code style is awesome" ); - } - return selectionCursor; - } - else - { - RelationshipTraversalCursor traversalCursor = cursors.allocateRelationshipTraversalCursor(); - node.allRelationships( traversalCursor ); - RelationshipSparseSelectionCursor selectionCursor = - new RelationshipSparseSelectionCursor(); - switch ( direction ) - { - case OUTGOING: - selectionCursor.outgoing( traversalCursor, relTypes ); - break; - case INCOMING: - selectionCursor.incoming( traversalCursor, relTypes ); - break; - case BOTH: - selectionCursor.all( traversalCursor, relTypes ); - break; - default: - throw new IllegalStateException( "Code style is awesome" ); - } - return selectionCursor; - } + return RelationshipSelections.selectionCursor( cursors, node, direction, types ); } public static RelationshipSelectionCursor nodeGetRelationships( Read read, CursorFactory cursors, NodeCursor node, diff --git a/community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContext.scala b/community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContext.scala index 136523c56e195..6ba87e5fad12c 100644 --- a/community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContext.scala +++ b/community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContext.scala @@ -34,7 +34,6 @@ import org.neo4j.cypher.internal.runtime.interpreted.commands.convert.DirectionC import org.neo4j.cypher.internal.runtime.interpreted.commands.expressions.{OnlyDirectionExpander, TypeAndDirectionExpander} import org.neo4j.cypher.internal.util.v3_4.{EntityNotFoundException, FailedIndexException} import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection -import org.neo4j.cypher.internal.v3_4.expressions.SemanticDirection.{BOTH, INCOMING, OUTGOING} import org.neo4j.cypher.internal.v3_4.logical.plans.{QualifiedName, _} import org.neo4j.graphalgo.impl.path.ShortestPath import org.neo4j.graphalgo.impl.path.ShortestPath.ShortestPathPredicate @@ -42,6 +41,7 @@ import org.neo4j.graphdb._ import org.neo4j.graphdb.security.URLAccessValidationError import org.neo4j.graphdb.traversal.{Evaluators, TraversalDescription, Uniqueness} import org.neo4j.internal.kernel.api._ +import org.neo4j.internal.kernel.api.helpers.RelationshipSelections.selectionCursor import org.neo4j.internal.kernel.api.helpers._ import org.neo4j.kernel.GraphDatabaseQueryService import org.neo4j.kernel.api.exceptions.ProcedureException @@ -172,70 +172,31 @@ final class TransactionBoundQueryContext(val transactionalContext: Transactional else tokenWrite.labelGetOrCreateForName(labelName) } - private def selectRelationships(nodeCursor: NodeCursor, dir: SemanticDirection, types: Option[Array[Int]]) = { - val cursors = transactionalContext.kernelTransaction.cursors() + def getRelationshipsForIds(node: Long, dir: SemanticDirection, + types: Option[Array[Int]]): Iterator[RelationshipValue] = { + val read = reads() + read.singleNode(node, nodeCursor) + if (!nodeCursor.next()) return Iterator.empty val factory = new RelationshipFactory[RelationshipValue] { override def relationship(id: Long, startNodeId: Long, typeId: Int, endNodeId: Long): RelationshipValue = fromRelationshipProxy(entityAccessor.newRelationshipProxy(id, startNodeId, typeId, endNodeId)) } - if (nodeCursor.isDense) { - val groupCursor = cursors.allocateRelationshipGroupCursor() - nodeCursor.relationships(groupCursor) - val traversalCursor = cursors.allocateRelationshipTraversalCursor() - val denseSelectionIterator = new RelationshipDenseSelectionIterator[RelationshipValue](factory) - dir match { - case OUTGOING => denseSelectionIterator.outgoing(groupCursor, traversalCursor, types.orNull) - case INCOMING => denseSelectionIterator.incoming(groupCursor, traversalCursor, types.orNull) - case BOTH => denseSelectionIterator.all(groupCursor, traversalCursor, types.orNull) - } - denseSelectionIterator - } else { - val traversalCursor = cursors.allocateRelationshipTraversalCursor() - nodeCursor.allRelationships(traversalCursor) - val sparseSelectionIterator = new RelationshipSparseSelectionIterator[RelationshipValue](factory) - dir match { - case OUTGOING => sparseSelectionIterator.outgoing(traversalCursor, types.orNull) - case INCOMING => sparseSelectionIterator.incoming(traversalCursor, types.orNull) - case BOTH => sparseSelectionIterator.all(traversalCursor, types.orNull) - } - sparseSelectionIterator - } - } - private def selectRelationshipsPrimitive(nodeCursor: NodeCursor, dir: SemanticDirection, types: Option[Array[Int]]) = { - val cursors = transactionalContext.kernelTransaction.cursors() - if (nodeCursor.isDense) { - val groupCursor = cursors.allocateRelationshipGroupCursor() - nodeCursor.relationships(groupCursor) - val traversalCursor = cursors.allocateRelationshipTraversalCursor() - val denseSelectionCursor = new RelationshipDenseSelectionCursor() - dir match { - case OUTGOING => denseSelectionCursor.outgoing(groupCursor, traversalCursor, types.orNull) - case INCOMING => denseSelectionCursor.incoming(groupCursor, traversalCursor, types.orNull) - case BOTH => denseSelectionCursor.all(groupCursor, traversalCursor, types.orNull) - } - denseSelectionCursor - } else { - val traversalCursor = cursors.allocateRelationshipTraversalCursor() - nodeCursor.allRelationships(traversalCursor) - val sparseSelectionCursor = new RelationshipSparseSelectionCursor() - dir match { - case OUTGOING => sparseSelectionCursor.outgoing(traversalCursor, types.orNull) - case INCOMING => sparseSelectionCursor.incoming(traversalCursor, types.orNull) - case BOTH => sparseSelectionCursor.all(traversalCursor, types.orNull) - } - sparseSelectionCursor - } - } + val cursor = selectionCursor(transactionalContext.kernelTransaction.cursors(), nodeCursor, + toGraphDb(dir), types.orNull) + new CursorIterator[RelationshipValue] { - def getRelationshipsForIds(node: Long, dir: SemanticDirection, - types: Option[Array[Int]]): Iterator[RelationshipValue] = { - val read = reads() - read.singleNode(node, nodeCursor) - if (!nodeCursor.next()) return Iterator.empty + override protected def close(): Unit = cursor.close() - selectRelationships(nodeCursor, dir, types).asScala + override protected def fetchNext(): RelationshipValue = + if (cursor.next()) + fromRelationshipProxy(entityAccessor.newRelationshipProxy(cursor.relationshipReference(), + cursor.sourceNodeReference(), + cursor.`type`(), + cursor.targetNodeReference())) + else null + } } override def getRelationshipsForIdsPrimitive(node: Long, dir: SemanticDirection, @@ -243,7 +204,8 @@ final class TransactionBoundQueryContext(val transactionalContext: Transactional val read = reads() read.singleNode(node, nodeCursor) if (!nodeCursor.next()) RelationshipIterator.EMPTY - else new RelationshipCursorIterator(selectRelationshipsPrimitive(nodeCursor, dir, types)) + else new RelationshipCursorIterator( + selectionCursor(transactionalContext.kernelTransaction.cursors(), nodeCursor, toGraphDb(dir), types.orNull)) } override def getRelationshipFor(relationshipId: Long, typeId: Int, startNodeId: Long, diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/helpers/RelationshipSelections.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/helpers/RelationshipSelections.java new file mode 100644 index 0000000000000..0f36980668b57 --- /dev/null +++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/helpers/RelationshipSelections.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.kernel.api.helpers; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.ResourceIterator; +import org.neo4j.internal.kernel.api.CursorFactory; +import org.neo4j.internal.kernel.api.NodeCursor; +import org.neo4j.internal.kernel.api.RelationshipGroupCursor; +import org.neo4j.internal.kernel.api.RelationshipTraversalCursor; + +/** + * Utilities for dealing with RelationshipSelectionCursor and corresponding iterators. + */ +public final class RelationshipSelections +{ + private RelationshipSelections() + { + throw new UnsupportedOperationException( "Do not instantiate" ); + } + + /** + * Returns a selection cursor given the provided node cursor, direction and relationship types. + * + * @param cursors A cursor factor used for allocating the needed cursors + * @param node A node cursor positioned at the current node. + * @param direction The direction of the the relationship. + * @param types The types of the relationship + * @return A cursor that allows traversing the relationship chain. + */ + public static RelationshipSelectionCursor selectionCursor( CursorFactory cursors, NodeCursor node, + Direction direction, int[] types ) + { + if ( node.isDense() ) + { + RelationshipDenseSelectionCursor selectionCursor = new RelationshipDenseSelectionCursor(); + setupDense( selectionCursor, cursors, node, direction, types ); + return selectionCursor; + } + else + { + RelationshipSparseSelectionCursor selectionCursor = new RelationshipSparseSelectionCursor(); + + setupSparse( selectionCursor, cursors, node, direction, types ); + return selectionCursor; + } + } + + /** + * Returns a resource iterator given the provided node cursor, direction and relationship types. + * + * @param cursors A cursor factor used for allocating the needed cursors + * @param node A node cursor positioned at the current node. + * @param direction The direction of the the relationship. + * @param types The types of the relationship + * @param factory factory for creating instance of generic type T + * @return An iterator that allows traversing the relationship chain. + */ + public static ResourceIterator selectionIterator( CursorFactory cursors, NodeCursor node, + Direction direction, int[] types, RelationshipFactory factory ) + { + if ( node.isDense() ) + { + RelationshipDenseSelectionIterator selectionIterator = + new RelationshipDenseSelectionIterator<>( factory ); + setupDense( selectionIterator, cursors, node, direction, types ); + return selectionIterator; + } + else + { + RelationshipSparseSelectionIterator selectionIterator = + new RelationshipSparseSelectionIterator<>( factory ); + + setupSparse( selectionIterator, cursors, node, direction, types ); + return selectionIterator; + } + } + + private static void setupDense( RelationshipDenseSelection denseSelection, CursorFactory cursors, NodeCursor node, + Direction direction, int[] types ) + { + + RelationshipGroupCursor groupCursor = cursors.allocateRelationshipGroupCursor(); + RelationshipTraversalCursor traversalCursor = cursors.allocateRelationshipTraversalCursor(); + try + { + node.relationships( groupCursor ); + switch ( direction ) + { + case OUTGOING: + denseSelection.outgoing( groupCursor, traversalCursor, types ); + break; + case INCOMING: + denseSelection.incoming( groupCursor, traversalCursor, types ); + break; + case BOTH: + denseSelection.all( groupCursor, traversalCursor, types ); + break; + default: + throw new IllegalStateException( "Unknown direction: " + direction ); + } + } + catch ( Throwable t ) + { + groupCursor.close(); + traversalCursor.close(); + throw t; + } + } + + private static void setupSparse( RelationshipSparseSelection sparseSelection, + CursorFactory cursors, NodeCursor node, Direction direction, int[] types ) + { + RelationshipTraversalCursor traversalCursor = cursors.allocateRelationshipTraversalCursor(); + try + { + node.allRelationships( traversalCursor ); + switch ( direction ) + { + case OUTGOING: + sparseSelection.outgoing( traversalCursor, types ); + break; + case INCOMING: + sparseSelection.incoming( traversalCursor, types ); + break; + case BOTH: + sparseSelection.all( traversalCursor, types ); + break; + default: + throw new IllegalStateException( "Unknown direction: " + direction ); + } + } + catch ( Throwable t ) + { + traversalCursor.close(); + throw t; + } + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/core/NodeProxy.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/core/NodeProxy.java index 947c8069ab99c..427d9b13f85df 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/core/NodeProxy.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/core/NodeProxy.java @@ -42,10 +42,6 @@ import org.neo4j.internal.kernel.api.LabelSet; import org.neo4j.internal.kernel.api.NodeCursor; import org.neo4j.internal.kernel.api.PropertyCursor; -import org.neo4j.internal.kernel.api.helpers.RelationshipDenseSelectionIterator; -import org.neo4j.internal.kernel.api.RelationshipGroupCursor; -import org.neo4j.internal.kernel.api.helpers.RelationshipSparseSelectionIterator; -import org.neo4j.internal.kernel.api.RelationshipTraversalCursor; import org.neo4j.internal.kernel.api.TokenRead; import org.neo4j.internal.kernel.api.exceptions.InvalidTransactionTypeKernelException; import org.neo4j.internal.kernel.api.exceptions.KernelException; @@ -71,6 +67,7 @@ import static org.neo4j.collection.primitive.PrimitiveIntCollections.map; import static org.neo4j.graphdb.Label.label; import static org.neo4j.helpers.collection.Iterators.asList; +import static org.neo4j.internal.kernel.api.helpers.RelationshipSelections.selectionIterator; import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_LABEL; import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_RELATIONSHIP_TYPE; import static org.neo4j.kernel.impl.core.TokenHolder.NO_ID; @@ -762,77 +759,7 @@ private ResourceIterator getRelationshipSelectionIterator( Directi throw new NotFoundException( format( "Node %d not found", nodeId ) ); } - if ( node.isDense() ) - { - RelationshipTraversalCursor relationship = transaction.cursors().allocateRelationshipTraversalCursor(); - RelationshipGroupCursor relationshipGroup = transaction.cursors().allocateRelationshipGroupCursor(); - try - { - node.relationships( relationshipGroup ); - RelationshipDenseSelectionIterator denseCursor = - new RelationshipDenseSelectionIterator<>( spi::newRelationshipProxy ); - - switch ( direction ) - { - case OUTGOING: - denseCursor.outgoing( relationshipGroup, relationship, typeIds ); - break; - - case INCOMING: - denseCursor.incoming( relationshipGroup, relationship, typeIds ); - break; - - case BOTH: - denseCursor.all( relationshipGroup, relationship, typeIds ); - break; - - default: - throw new IllegalStateException( "Unsupported direction: " + direction ); - } - - return denseCursor; - } - catch ( Throwable e ) - { - relationshipGroup.close(); - throw e; - } - } - else - { - RelationshipTraversalCursor relationship = transaction.cursors().allocateRelationshipTraversalCursor(); - try - { - node.allRelationships( relationship ); - RelationshipSparseSelectionIterator sparseCursor = - new RelationshipSparseSelectionIterator<>( spi::newRelationshipProxy ); - - switch ( direction ) - { - case OUTGOING: - sparseCursor.outgoing( relationship, typeIds ); - break; - - case INCOMING: - sparseCursor.incoming( relationship, typeIds ); - break; - - case BOTH: - sparseCursor.all( relationship, typeIds ); - break; - - default: - throw new IllegalStateException( "Unsupported direction: " + direction ); - } - - return sparseCursor; - } - catch ( Throwable e ) - { - relationship.close(); - throw e; - } - } + return selectionIterator( transaction.cursors(), node, direction, typeIds, spi::newRelationshipProxy ); } private int[] relTypeIds( RelationshipType[] types, Statement statement )