Skip to content

Commit 39efdef

Browse files
committed
Add ScanningRelationshipsImporter test for inverse adjacency list
1 parent 98a8ecf commit 39efdef

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Neo4j is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
package org.neo4j.gds.core.loading;
21+
22+
import org.junit.jupiter.api.Test;
23+
import org.neo4j.gds.BaseTest;
24+
import org.neo4j.gds.NodeProjections;
25+
import org.neo4j.gds.RelationshipProjection;
26+
import org.neo4j.gds.RelationshipProjections;
27+
import org.neo4j.gds.RelationshipType;
28+
import org.neo4j.gds.api.AdjacencyList;
29+
import org.neo4j.gds.api.GraphLoaderContext;
30+
import org.neo4j.gds.api.ImmutableGraphLoaderContext;
31+
import org.neo4j.gds.compat.GraphDatabaseApiProxy;
32+
import org.neo4j.gds.config.GraphProjectFromStoreConfig;
33+
import org.neo4j.gds.config.ImmutableGraphProjectFromStoreConfig;
34+
import org.neo4j.gds.core.GraphDimensions;
35+
import org.neo4j.gds.core.GraphDimensionsStoreReader;
36+
import org.neo4j.gds.core.concurrency.Pools;
37+
import org.neo4j.gds.core.huge.DirectIdMap;
38+
import org.neo4j.gds.core.utils.TerminationFlag;
39+
import org.neo4j.gds.core.utils.progress.TaskRegistryFactory;
40+
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
41+
import org.neo4j.gds.core.utils.warnings.EmptyUserLogRegistryFactory;
42+
import org.neo4j.gds.extension.IdFunction;
43+
import org.neo4j.gds.extension.Inject;
44+
import org.neo4j.gds.extension.Neo4jGraph;
45+
import org.neo4j.gds.transaction.TransactionContext;
46+
import org.neo4j.internal.id.IdGeneratorFactory;
47+
import org.neo4j.logging.NullLog;
48+
49+
import static org.assertj.core.api.Assertions.assertThat;
50+
51+
class ScanningRelationshipsImporterTest extends BaseTest {
52+
53+
@Neo4jGraph
54+
public static final String DB = "CREATE " +
55+
"(a)-[:R]->(b)," +
56+
"(a)-[:R]->(c)," +
57+
"(a)-[:R]->(d)," +
58+
"(b)-[:R]->(c)," +
59+
"(c)-[:R]->(a)";
60+
61+
@Inject
62+
private IdFunction idFunction;
63+
64+
@Test
65+
void shouldLoadInverseRelationships() {
66+
var relationshipType = RelationshipType.of("R");
67+
var graphProjectConfig = ImmutableGraphProjectFromStoreConfig.builder()
68+
.graphName("testGraph")
69+
.nodeProjections(NodeProjections.ALL)
70+
.relationshipProjections(
71+
RelationshipProjections.single(
72+
relationshipType,
73+
RelationshipProjection.builder()
74+
.type("R")
75+
.indexInverse(true)
76+
.build()
77+
))
78+
.build();
79+
80+
var graphLoaderContext = graphLoaderContext();
81+
var graphDimensions = graphDimensions(graphProjectConfig, graphLoaderContext);
82+
var importer = new ScanningRelationshipsImporterBuilder()
83+
.idMap(new DirectIdMap(graphDimensions.nodeCount()))
84+
.loadingContext(graphLoaderContext)
85+
.progressTracker(ProgressTracker.NULL_TRACKER)
86+
.dimensions(graphDimensions)
87+
.concurrency(1)
88+
.graphProjectConfig(graphProjectConfig)
89+
.build();
90+
91+
var relationshipsAndProperties = importer.call();
92+
93+
var topology = relationshipsAndProperties.relationships().get(relationshipType);
94+
95+
assertThat(topology.inverseAdjacencyList()).isPresent();
96+
97+
var inverseAdjacencyList = topology.inverseAdjacencyList().get();
98+
99+
assertThat(degree("a", inverseAdjacencyList)).isEqualTo(1); // (c)-->(a)
100+
assertThat(degree("b", inverseAdjacencyList)).isEqualTo(1); // (a)-->(b)
101+
assertThat(degree("c", inverseAdjacencyList)).isEqualTo(2); // (a)-->(c),(b)-->(c)
102+
assertThat(degree("d", inverseAdjacencyList)).isEqualTo(1); // (a)-->(d)
103+
104+
assertThat(targets("a", inverseAdjacencyList)).isEqualTo(nodeIds("c")); // (c)-->(a)
105+
assertThat(targets("b", inverseAdjacencyList)).isEqualTo(nodeIds("a")); // (a)-->(b)
106+
assertThat(targets("c", inverseAdjacencyList)).isEqualTo(nodeIds("a", "b")); // (a)-->(c),(b)-->(c)
107+
assertThat(targets("d", inverseAdjacencyList)).isEqualTo(nodeIds("a")); // (a)-->(d)
108+
}
109+
110+
private int degree(String nodeVariable, AdjacencyList adjacencyList) {
111+
var nodeId = idFunction.of(nodeVariable);
112+
return adjacencyList.degree(nodeId);
113+
}
114+
115+
private long[] targets(String nodeVariable, AdjacencyList adjacencyList) {
116+
var nodeId = idFunction.of(nodeVariable);
117+
var degree = adjacencyList.degree(nodeId);
118+
var targets = new long[degree];
119+
var cursor = adjacencyList.adjacencyCursor(nodeId);
120+
int i = 0;
121+
while (cursor.hasNextVLong()) {
122+
targets[i++] = cursor.nextVLong();
123+
}
124+
return targets;
125+
}
126+
127+
private long[] nodeIds(String... nodeVariables) {
128+
var nodeIds = new long[nodeVariables.length];
129+
for (int i = 0; i < nodeVariables.length; i++) {
130+
nodeIds[i] = idFunction.of(nodeVariables[i]);
131+
}
132+
return nodeIds;
133+
}
134+
135+
private GraphLoaderContext graphLoaderContext() {
136+
return ImmutableGraphLoaderContext.builder()
137+
.graphDatabaseService(db)
138+
.executor(Pools.DEFAULT)
139+
.log(NullLog.getInstance())
140+
.terminationFlag(TerminationFlag.RUNNING_TRUE)
141+
.transactionContext(TransactionContext.of(db, db.beginTx()))
142+
.taskRegistryFactory(TaskRegistryFactory.empty())
143+
.userLogRegistryFactory(EmptyUserLogRegistryFactory.INSTANCE)
144+
.build();
145+
}
146+
147+
private GraphDimensions graphDimensions(
148+
GraphProjectFromStoreConfig graphProjectConfig,
149+
GraphLoaderContext graphLoaderContext
150+
) {
151+
return new GraphDimensionsStoreReader(
152+
graphLoaderContext.transactionContext(),
153+
graphProjectConfig,
154+
GraphDatabaseApiProxy.resolveDependency(graphLoaderContext.graphDatabaseService(), IdGeneratorFactory.class)
155+
).call();
156+
}
157+
}

0 commit comments

Comments
 (0)