diff --git a/core/src/main/java/org/neo4j/graphalgo/core/huge/HugeGraphImpl.java b/core/src/main/java/org/neo4j/graphalgo/core/huge/HugeGraphImpl.java index 62ab03494..d4be66392 100644 --- a/core/src/main/java/org/neo4j/graphalgo/core/huge/HugeGraphImpl.java +++ b/core/src/main/java/org/neo4j/graphalgo/core/huge/HugeGraphImpl.java @@ -31,6 +31,8 @@ import org.neo4j.graphalgo.core.utils.paged.ByteArray; import org.neo4j.graphalgo.core.utils.paged.HugeLongArray; import org.neo4j.graphdb.Direction; +import org.neo4j.internal.kernel.api.CursorFactory; +import org.neo4j.internal.kernel.api.NodeCursor; import java.util.Collection; import java.util.function.LongPredicate; @@ -47,7 +49,7 @@ * degree ~ targetId1 ~ targetId2 ~ targetIdn * * The {@code degree} is stored as a fill-sized 4 byte long {@code int} - * (the neo kernel api returns an int for {@link org.neo4j.kernel.api.ReadOperations#nodeGetDegree(long, Direction)}). + * (the neo kernel api returns an int for {@link org.neo4j.internal.kernel.api.helpers.Nodes#countAll(NodeCursor, CursorFactory)}). * Every target ID is first sorted, then delta encoded, and finally written as variable-length vlongs. * The delta encoding does not write the actual value but only the difference to the previous value, which plays very nice with the vlong encoding. *

@@ -139,23 +141,7 @@ public double weightOf(final long sourceNodeId, final long targetNodeId) { @Override public void forEachRelationship(long nodeId, Direction direction, HugeRelationshipConsumer consumer) { - switch (direction) { - case INCOMING: - forEachIncoming(nodeId, consumer); - return; - - case OUTGOING: - forEachOutgoing(nodeId, consumer); - return; - - case BOTH: - forEachOutgoing(nodeId, consumer); - forEachIncoming(nodeId, consumer); - return; - - default: - throw new IllegalArgumentException(direction + ""); - } + runForEach(nodeId, direction, consumer, /* reuseCursor */ true); } @Override @@ -169,13 +155,9 @@ public void forEachRelationship(int nodeId, Direction direction, RelationshipCon forEachOutgoing(nodeId, consumer); return; - case BOTH: + default: forEachOutgoing(nodeId, consumer); forEachIncoming(nodeId, consumer); - return; - - default: - throw new IllegalArgumentException(direction + ""); } } @@ -190,13 +172,9 @@ public void forEachRelationship(int nodeId, Direction direction, WeightedRelatio forEachOutgoing(nodeId, consumer); return; - case BOTH: + default: forEachOutgoing(nodeId, consumer); forEachIncoming(nodeId, consumer); - return; - - default: - throw new IllegalArgumentException(direction + ""); } } @@ -239,40 +217,50 @@ public boolean contains(final long nodeId) { @Override public void forEachIncoming(long node, final HugeRelationshipConsumer consumer) { - forEachIncoming(node, inCache, consumer); + runForEach(node, Direction.INCOMING, consumer, /* reuseCursor */ true); } @Override public void forEachIncoming(int nodeId, RelationshipConsumer consumer) { - forEachIncoming((long) nodeId, inAdjacency.newCursor(), toHugeInConsumer(consumer)); + runForEach( + Integer.toUnsignedLong(nodeId), + Direction.INCOMING, + toHugeInConsumer(consumer), + /* reuseCursor */ false + ); } public void forEachIncoming(int nodeId, WeightedRelationshipConsumer consumer) { - forEachIncoming((long) nodeId, inAdjacency.newCursor(), toHugeInConsumer(consumer)); - } - - private void forEachIncoming(long node, ByteArray.DeltaCursor newCursor, final HugeRelationshipConsumer consumer) { - ByteArray.DeltaCursor cursor = cursor(node, newCursor, inOffsets, inAdjacency); - consumeNodes(node, cursor, consumer); + runForEach( + Integer.toUnsignedLong(nodeId), + Direction.INCOMING, + toHugeInConsumer(consumer), + /* reuseCursor */ false + ); } @Override public void forEachOutgoing(long node, final HugeRelationshipConsumer consumer) { - forEachOutgoing(node, outCache, consumer); + runForEach(node, Direction.OUTGOING, consumer, /* reuseCursor */ true); } @Override public void forEachOutgoing(int nodeId, RelationshipConsumer consumer) { - forEachOutgoing((long) nodeId, outAdjacency.newCursor(), toHugeOutConsumer(consumer)); + runForEach( + Integer.toUnsignedLong(nodeId), + Direction.OUTGOING, + toHugeOutConsumer(consumer), + /* reuseCursor */ false + ); } public void forEachOutgoing(int nodeId, WeightedRelationshipConsumer consumer) { - forEachOutgoing((long) nodeId, outAdjacency.newCursor(), toHugeOutConsumer(consumer)); - } - - private void forEachOutgoing(long node, ByteArray.DeltaCursor newCursor, final HugeRelationshipConsumer consumer) { - ByteArray.DeltaCursor cursor = cursor(node, newCursor, outOffsets, outAdjacency); - consumeNodes(node, cursor, consumer); + runForEach( + Integer.toUnsignedLong(nodeId), + Direction.OUTGOING, + toHugeOutConsumer(consumer), + /* reuseCursor */ false + ); } @Override @@ -293,26 +281,49 @@ public HugeRelationshipIntersect intersectionCopy() { return new HugeGraphIntersectImpl(outAdjacency, outOffsets); } - /* + /** + * O(n) ! + */ + @Override + public boolean exists(int sourceNodeId, int targetNodeId, Direction direction) { + return exists( + Integer.toUnsignedLong(sourceNodeId), + Integer.toUnsignedLong(targetNodeId), + direction, + // Graph interface should be thread-safe + false + ); + } + + /** * O(n) ! */ @Override public boolean exists(long sourceNodeId, long targetNodeId, Direction direction) { + return exists( + sourceNodeId, + targetNodeId, + direction, + // HugeGraph interface make no promises about thread-safety (that's what concurrentCopy is for) + true + ); + } + + private boolean exists(long sourceNodeId, long targetNodeId, Direction direction, boolean reuseCursor) { ExistsConsumer consumer = new ExistsConsumer(targetNodeId); - switch (direction) { - case OUTGOING: - forEachOutgoing(sourceNodeId, consumer); - case INCOMING: - forEachIncoming(sourceNodeId, consumer); - default: - forEachRelationship(sourceNodeId, Direction.BOTH, consumer); - } + runForEach(sourceNodeId, direction, consumer, reuseCursor); return consumer.found; } @Override public int getTarget(int nodeId, int index, Direction direction) { - return Math.toIntExact(getTarget(Integer.toUnsignedLong(nodeId), Integer.toUnsignedLong(index), direction)); + return Math.toIntExact(getTarget( + Integer.toUnsignedLong(nodeId), + Integer.toUnsignedLong(index), + direction, + // Graph interface should be thread-safe + false + )); } /* @@ -320,21 +331,51 @@ public int getTarget(int nodeId, int index, Direction direction) { */ @Override public long getTarget(long sourceNodeId, long index, Direction direction) { + return getTarget( + sourceNodeId, + index, + direction, + // HugeGraph interface make no promises about thread-safety (that's what concurrentCopy is for) + true + ); + } + + private long getTarget(long sourceNodeId, long index, Direction direction, boolean reuseCursor) { GetTargetConsumer consumer = new GetTargetConsumer(index); - switch (direction) { - case OUTGOING: - forEachOutgoing(sourceNodeId, consumer); - case INCOMING: - forEachIncoming(sourceNodeId, consumer); - default: - forEachRelationship(sourceNodeId, Direction.BOTH, consumer); - } + runForEach(sourceNodeId, direction, consumer, reuseCursor); return consumer.target; } - @Override - public boolean exists(int sourceNodeId, int targetNodeId, Direction direction) { - return exists(Integer.toUnsignedLong(sourceNodeId), Integer.toUnsignedLong(targetNodeId), direction); + private void runForEach( + long sourceNodeId, Direction direction, + HugeRelationshipConsumer consumer, + boolean reuseCursor) { + if (direction == Direction.BOTH) { + runForEach(sourceNodeId, Direction.OUTGOING, consumer, reuseCursor); + runForEach(sourceNodeId, Direction.INCOMING, consumer, reuseCursor); + return; + } + ByteArray.DeltaCursor cursor = forEachCursor(sourceNodeId, direction, reuseCursor); + consumeNodes(sourceNodeId, cursor, consumer); + } + + private ByteArray.DeltaCursor forEachCursor( + long sourceNodeId, + Direction direction, + boolean reuseCursor) { + if (direction == Direction.OUTGOING) { + return cursor( + sourceNodeId, + reuseCursor ? outCache : outAdjacency.newCursor(), + outOffsets, + outAdjacency); + } else { + return cursor( + sourceNodeId, + reuseCursor ? inCache : inAdjacency.newCursor(), + inOffsets, + inAdjacency); + } } @Override