2020package org .neo4j .gds .core .huge ;
2121
2222import org .apache .commons .lang3 .mutable .MutableInt ;
23+ import org .jetbrains .annotations .Nullable ;
2324import org .neo4j .gds .NodeLabel ;
2425import org .neo4j .gds .api .CSRGraph ;
2526import org .neo4j .gds .api .CSRGraphAdapter ;
3233import org .neo4j .gds .api .RelationshipWithPropertyConsumer ;
3334import org .neo4j .gds .api .properties .nodes .NodePropertyValues ;
3435import org .neo4j .gds .api .schema .GraphSchema ;
36+ import org .neo4j .gds .collections .ha .HugeIntArray ;
3537import org .neo4j .gds .collections .primitive .PrimitiveLongIterable ;
3638import org .neo4j .gds .config .ConcurrencyConfig ;
3739import org .neo4j .gds .core .concurrency .RunWithConcurrency ;
38- import org .neo4j .gds .collections .ha .HugeIntArray ;
3940import org .neo4j .gds .core .utils .partition .Partition ;
4041import org .neo4j .gds .core .utils .partition .PartitionUtils ;
4142import org .neo4j .gds .utils .CloseableThreadLocal ;
@@ -56,16 +57,30 @@ public class NodeFilteredGraph extends CSRGraphAdapter implements FilteredIdMap
5657 private final FilteredIdMap filteredIdMap ;
5758 private long relationshipCount ;
5859 private final HugeIntArray degreeCache ;
60+ private final HugeIntArray degreeInverseCache ;
5961 private final CloseableThreadLocal <Graph > threadLocalGraph ;
6062
6163 public NodeFilteredGraph (CSRGraph originalGraph , FilteredIdMap filteredIdMap ) {
62- this (originalGraph , filteredIdMap , emptyDegreeCache (filteredIdMap ), -1 );
64+ this (
65+ originalGraph ,
66+ filteredIdMap ,
67+ emptyDegreeCache (filteredIdMap ),
68+ originalGraph .characteristics ().isInverseIndexed () ? emptyDegreeCache (filteredIdMap ) : null ,
69+ -1
70+ );
6371 }
6472
65- private NodeFilteredGraph (CSRGraph originalGraph , FilteredIdMap filteredIdMap , HugeIntArray degreeCache , long relationshipCount ) {
73+ private NodeFilteredGraph (
74+ CSRGraph originalGraph ,
75+ FilteredIdMap filteredIdMap ,
76+ HugeIntArray degreeCache ,
77+ @ Nullable HugeIntArray degreeInverseCache ,
78+ long relationshipCount
79+ ) {
6680 super (originalGraph );
6781
6882 this .degreeCache = degreeCache ;
83+ this .degreeInverseCache = degreeInverseCache ;
6984 this .filteredIdMap = filteredIdMap ;
7085 this .relationshipCount = relationshipCount ;
7186 this .threadLocalGraph = CloseableThreadLocal .withInitial (this ::concurrentCopy );
@@ -104,18 +119,18 @@ public void forEachNode(LongPredicate consumer) {
104119
105120 @ Override
106121 public int degree (long nodeId ) {
107- int cachedDegree = degreeCache .get (nodeId );
122+ int cachedDegree = this . degreeCache .get (nodeId );
108123 if (cachedDegree != NO_DEGREE ) {
109124 return cachedDegree ;
110125 }
111126
112127 var degree = new MutableInt ();
113128
114- threadLocalGraph .get ().forEachRelationship (nodeId , (s , t ) -> {
129+ this . threadLocalGraph .get ().forEachRelationship (nodeId , (s , t ) -> {
115130 degree .increment ();
116131 return true ;
117132 });
118- degreeCache .set (nodeId , degree .intValue ());
133+ this . degreeCache .set (nodeId , degree .intValue ());
119134
120135 return degree .intValue ();
121136 }
@@ -130,6 +145,24 @@ public int degreeWithoutParallelRelationships(long nodeId) {
130145 return degreeCounter .degree ;
131146 }
132147
148+ @ Override
149+ public int degreeInverse (long nodeId ) {
150+ int cachedDegree = this .degreeInverseCache .get (nodeId );
151+ if (cachedDegree != NO_DEGREE ) {
152+ return cachedDegree ;
153+ }
154+
155+ var degree = new MutableInt ();
156+
157+ this .threadLocalGraph .get ().forEachInverseRelationship (nodeId , (s , t ) -> {
158+ degree .increment ();
159+ return true ;
160+ });
161+ this .degreeInverseCache .set (nodeId , degree .intValue ());
162+
163+ return degree .intValue ();
164+ }
165+
133166 @ Override
134167 public long nodeCount () {
135168 return filteredIdMap .nodeCount ();
@@ -218,9 +251,30 @@ public void forEachRelationship(long nodeId, double fallbackValue, RelationshipW
218251 );
219252 }
220253
254+ @ Override
255+ public void forEachInverseRelationship (long nodeId , RelationshipConsumer consumer ) {
256+ super .forEachInverseRelationship (
257+ filteredIdMap .toRootNodeId (nodeId ),
258+ (s , t ) -> filterAndConsume (s , t , consumer )
259+ );
260+ }
261+
262+ @ Override
263+ public void forEachInverseRelationship (
264+ long nodeId ,
265+ double fallbackValue ,
266+ RelationshipWithPropertyConsumer consumer
267+ ) {
268+ super .forEachInverseRelationship (
269+ filteredIdMap .toRootNodeId (nodeId ),
270+ fallbackValue ,
271+ (s , t , p ) -> filterAndConsume (s , t , p , consumer )
272+ );
273+ }
274+
221275 @ Override
222276 public Stream <RelationshipCursor > streamRelationships (long nodeId , double fallbackValue ) {
223- if (! filteredIdMap .containsRootNodeId (filteredIdMap .toRootNodeId (nodeId ))) {
277+ if (!filteredIdMap .containsRootNodeId (filteredIdMap .toRootNodeId (nodeId ))) {
224278 return Stream .empty ();
225279 }
226280
@@ -266,7 +320,13 @@ public double relationshipProperty(long sourceNodeId, long targetNodeId) {
266320
267321 @ Override
268322 public CSRGraph concurrentCopy () {
269- return new NodeFilteredGraph (csrGraph .concurrentCopy (), filteredIdMap , degreeCache , relationshipCount );
323+ return new NodeFilteredGraph (
324+ csrGraph .concurrentCopy (),
325+ filteredIdMap ,
326+ degreeCache ,
327+ degreeInverseCache ,
328+ relationshipCount
329+ );
270330 }
271331
272332 @ Override
@@ -312,7 +372,12 @@ private boolean filterAndConsume(long source, long target, RelationshipConsumer
312372 return true ;
313373 }
314374
315- private boolean filterAndConsume (long source , long target , double propertyValue , RelationshipWithPropertyConsumer consumer ) {
375+ private boolean filterAndConsume (
376+ long source ,
377+ long target ,
378+ double propertyValue ,
379+ RelationshipWithPropertyConsumer consumer
380+ ) {
316381 if (filteredIdMap .containsRootNodeId (source ) && filteredIdMap .containsRootNodeId (target )) {
317382 long internalSourceId = filteredIdMap .toFilteredNodeId (source );
318383 long internalTargetId = filteredIdMap .toFilteredNodeId (target );
0 commit comments