Skip to content

Commit a32307e

Browse files
DarthMaxyuvalr1neo
authored andcommitted
Fix to undirected for relationships with a single property
1 parent c186b4e commit a32307e

File tree

2 files changed

+123
-32
lines changed

2 files changed

+123
-32
lines changed

algo/src/main/java/org/neo4j/gds/beta/undirected/ToUndirected.java

Lines changed: 113 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,24 @@
1919
*/
2020
package org.neo4j.gds.beta.undirected;
2121

22+
import org.jetbrains.annotations.NotNull;
2223
import org.neo4j.gds.Algorithm;
2324
import org.neo4j.gds.Orientation;
2425
import org.neo4j.gds.RelationshipType;
2526
import org.neo4j.gds.api.CompositeRelationshipIterator;
27+
import org.neo4j.gds.api.Graph;
2628
import org.neo4j.gds.api.GraphStore;
2729
import org.neo4j.gds.api.PropertyState;
30+
import org.neo4j.gds.api.RelationshipIterator;
2831
import org.neo4j.gds.api.RelationshipProperty;
2932
import org.neo4j.gds.api.RelationshipPropertyStore;
3033
import org.neo4j.gds.api.schema.Direction;
3134
import org.neo4j.gds.api.schema.PropertySchema;
35+
import org.neo4j.gds.api.schema.RelationshipPropertySchema;
3236
import org.neo4j.gds.core.concurrency.RunWithConcurrency;
3337
import org.neo4j.gds.core.loading.SingleTypeRelationshipImportResult;
3438
import org.neo4j.gds.core.loading.construction.GraphFactory;
39+
import org.neo4j.gds.core.loading.construction.RelationshipsAndDirection;
3540
import org.neo4j.gds.core.loading.construction.RelationshipsBuilder;
3641
import org.neo4j.gds.core.loading.construction.RelationshipsBuilderBuilder;
3742
import org.neo4j.gds.core.utils.partition.DegreePartition;
@@ -42,6 +47,7 @@
4247
import java.util.List;
4348
import java.util.Optional;
4449
import java.util.concurrent.ExecutorService;
50+
import java.util.function.Function;
4551
import java.util.stream.Collectors;
4652
import java.util.stream.IntStream;
4753

@@ -75,30 +81,9 @@ public SingleTypeRelationshipImportResult compute() {
7581
.propertySchemasFor(fromRelationshipType);
7682
var propertyKeys = propertySchemas.stream().map(PropertySchema::key).collect(Collectors.toList());
7783

78-
RelationshipsBuilderBuilder relationshipsBuilderBuilder = GraphFactory.initRelationshipsBuilder()
79-
.concurrency(config.concurrency())
80-
.nodes(graphStore.nodes())
81-
.executorService(executorService)
82-
.orientation(Orientation.UNDIRECTED)
83-
.validateRelationships(false);
84-
85-
propertySchemas.forEach(propertySchema ->
86-
relationshipsBuilderBuilder.addPropertyConfig(propertySchema.aggregation(), propertySchema.defaultValue())
87-
);
84+
var relationshipsBuilder = initializeRelationshipsBuilder(propertySchemas);
8885

89-
RelationshipsBuilder relationshipsBuilder = relationshipsBuilderBuilder.build();
90-
91-
CompositeRelationshipIterator relationshipIterator = graphStore.getCompositeRelationshipIterator(
92-
fromRelationshipType,
93-
propertyKeys
94-
);
95-
96-
List<ToUndirectedTask> tasks = PartitionUtils.degreePartition(
97-
graphStore.getGraph(fromRelationshipType),
98-
config.concurrency(),
99-
(partition) -> new ToUndirectedTask(relationshipsBuilder, relationshipIterator.concurrentCopy(), partition, progressTracker),
100-
Optional.empty()
101-
);
86+
var tasks = createTasks(fromRelationshipType, propertyKeys, relationshipsBuilder);
10287

10388
progressTracker.beginSubTask();
10489

@@ -117,6 +102,38 @@ public SingleTypeRelationshipImportResult compute() {
117102
var relationships = relationshipsBuilder.buildAll();
118103
progressTracker.endSubTask();
119104

105+
SingleTypeRelationshipImportResult result = createResult(
106+
propertySchemas,
107+
propertyKeys,
108+
relationships
109+
);
110+
111+
progressTracker.endSubTask();
112+
113+
return result;
114+
}
115+
116+
@NotNull
117+
private RelationshipsBuilder initializeRelationshipsBuilder(List<RelationshipPropertySchema> propertySchemas) {
118+
RelationshipsBuilderBuilder relationshipsBuilderBuilder = GraphFactory.initRelationshipsBuilder()
119+
.concurrency(config.concurrency())
120+
.nodes(graphStore.nodes())
121+
.executorService(executorService)
122+
.orientation(Orientation.UNDIRECTED)
123+
.validateRelationships(false);
124+
125+
propertySchemas.forEach(propertySchema ->
126+
relationshipsBuilderBuilder.addPropertyConfig(propertySchema.aggregation(), propertySchema.defaultValue())
127+
);
128+
129+
return relationshipsBuilderBuilder.build();
130+
}
131+
132+
private static SingleTypeRelationshipImportResult createResult(
133+
List<RelationshipPropertySchema> propertySchemas,
134+
List<String> propertyKeys,
135+
List<RelationshipsAndDirection> relationships
136+
) {
120137
var topology = relationships.get(0).relationships().topology();
121138
var propertyValues = IntStream.range(0, propertyKeys.size())
122139
.boxed()
@@ -136,27 +153,96 @@ public SingleTypeRelationshipImportResult compute() {
136153
.putAllRelationshipProperties(propertyValues)
137154
.build();
138155

139-
progressTracker.endSubTask();
140156
return SingleTypeRelationshipImportResult.builder()
141157
.topology(topology)
142158
.properties(propertyStore)
143159
.direction(Direction.UNDIRECTED)
144160
.build();
145161
}
146162

163+
@NotNull
164+
private List<Runnable> createTasks(
165+
RelationshipType fromRelationshipType,
166+
List<String> propertyKeys,
167+
RelationshipsBuilder relationshipsBuilder
168+
) {
169+
Function<DegreePartition, Runnable> taskCreator;
170+
if (propertyKeys.size() == 1) {
171+
Graph graph = graphStore.getGraph(fromRelationshipType, Optional.of(propertyKeys.get(0)));
172+
173+
taskCreator = (partition) -> new ToUndirectedTaskWithSingleProperty(
174+
relationshipsBuilder,
175+
graph.concurrentCopy(),
176+
partition,
177+
progressTracker
178+
);
179+
}
180+
else {
181+
CompositeRelationshipIterator relationshipIterator = graphStore.getCompositeRelationshipIterator(
182+
fromRelationshipType,
183+
propertyKeys
184+
);
185+
186+
taskCreator = (partition) -> new ToUndirectedTaskWithMultipleProperties(
187+
relationshipsBuilder,
188+
relationshipIterator.concurrentCopy(),
189+
partition,
190+
progressTracker
191+
);
192+
}
193+
194+
return PartitionUtils.degreePartition(
195+
graphStore.getGraph(fromRelationshipType),
196+
config.concurrency(),
197+
taskCreator,
198+
Optional.empty()
199+
);
200+
}
201+
147202
@Override
148203
public void release() {
149204

150205
}
151206

152-
private static final class ToUndirectedTask implements Runnable {
207+
private static final class ToUndirectedTaskWithSingleProperty implements Runnable {
208+
209+
private final RelationshipsBuilder relationshipsBuilder;
210+
private final RelationshipIterator relationshipIterator;
211+
private final DegreePartition partition;
212+
private final ProgressTracker progressTracker;
213+
214+
private ToUndirectedTaskWithSingleProperty(
215+
RelationshipsBuilder relationshipsBuilder,
216+
RelationshipIterator relationshipIterator,
217+
DegreePartition partition,
218+
ProgressTracker progressTracker
219+
) {
220+
this.relationshipsBuilder = relationshipsBuilder;
221+
this.relationshipIterator = relationshipIterator;
222+
this.partition = partition;
223+
this.progressTracker = progressTracker;
224+
}
225+
226+
@Override
227+
public void run() {
228+
for (long i = partition.startNode(); i < partition.startNode() + partition.nodeCount(); i++) {
229+
relationshipIterator.forEachRelationship(i, 0.0D, (source, target, property) -> {
230+
relationshipsBuilder.addFromInternal(target, source, property);
231+
return true;
232+
});
233+
progressTracker.logProgress();
234+
}
235+
}
236+
}
237+
238+
private static final class ToUndirectedTaskWithMultipleProperties implements Runnable {
153239

154240
private final RelationshipsBuilder relationshipsBuilder;
155241
private final CompositeRelationshipIterator relationshipIterator;
156242
private final DegreePartition partition;
157243
private final ProgressTracker progressTracker;
158244

159-
private ToUndirectedTask(
245+
private ToUndirectedTaskWithMultipleProperties(
160246
RelationshipsBuilder relationshipsBuilder,
161247
CompositeRelationshipIterator relationshipIterator,
162248
DegreePartition partition,
@@ -172,7 +258,7 @@ private ToUndirectedTask(
172258
public void run() {
173259
for (long i = partition.startNode(); i < partition.startNode() + partition.nodeCount(); i++) {
174260
relationshipIterator.forEachRelationship(i, (source, target, properties) -> {
175-
relationshipsBuilder.add(target, source, properties);
261+
relationshipsBuilder.addFromInternal(target, source, properties);
176262
return true;
177263
});
178264
progressTracker.logProgress();

proc/beta/src/test/java/org/neo4j/gds/beta/undirected/ToUndirectedProcTest.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import java.util.List;
3434
import java.util.Map;
35+
import java.util.Optional;
3536

3637
import static org.hamcrest.Matchers.instanceOf;
3738
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -48,9 +49,9 @@ class ToUndirectedProcTest extends BaseProcTest {
4849
"(a:A) " +
4950
",(b:B) " +
5051
",(c:C) " +
51-
",(a)-[:REL]->(b)" +
52-
",(a)-[:REL]->(c)" +
53-
",(b)-[:REL]->(c)";
52+
",(a)-[:REL {prop1: 1.0}]->(b)" +
53+
",(a)-[:REL {prop1: 2.0}]->(c)" +
54+
",(b)-[:REL {prop1: 3.0}]->(c)";
5455

5556
@BeforeEach
5657
void setup() throws Exception {
@@ -62,6 +63,7 @@ void setup() throws Exception {
6263
.withNodeLabel("B")
6364
.withNodeLabel("C")
6465
.withRelationshipType("REL")
66+
.withRelationshipProperty("prop1")
6567
.yields()
6668
);
6769
}
@@ -80,8 +82,11 @@ void convertToUndirected() {
8082
));
8183

8284
var gs = GraphStoreCatalog.get("", "neo4j", "graph");
83-
var graph = gs.graphStore().getGraph(RelationshipType.of("REL2"));
84-
assertGraphEquals(fromGdl(DB.replace("REL", "REL2"), Orientation.UNDIRECTED), graph);
85+
var graph = gs.graphStore().getGraph(RelationshipType.of("REL2"), Optional.of("prop1"));
86+
assertGraphEquals(
87+
fromGdl(DB.replace("REL", "REL2"), Orientation.UNDIRECTED),
88+
graph
89+
);
8590
}
8691

8792
@Test

0 commit comments

Comments
 (0)