Skip to content

Commit 7514d10

Browse files
committed
Add validation methods for inverse indexes to PregelBaseProc
1 parent 5f2cd00 commit 7514d10

File tree

3 files changed

+175
-16
lines changed

3 files changed

+175
-16
lines changed

proc/pregel/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencies {
2020
implementation project(':executor')
2121
implementation project(':proc-common')
2222
implementation project(':string-formatting')
23-
23+
implementation project(':graph-schema-api')
2424
api project(':pregel')
2525

2626
testAnnotationProcessor project(':annotations')

proc/pregel/src/main/java/org/neo4j/gds/pregel/proc/PregelBaseProc.java

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,34 +20,109 @@
2020
package org.neo4j.gds.pregel.proc;
2121

2222
import org.neo4j.gds.Algorithm;
23+
import org.neo4j.gds.RelationshipType;
24+
import org.neo4j.gds.api.GraphStore;
2325
import org.neo4j.gds.api.properties.nodes.DoubleArrayNodePropertyValues;
2426
import org.neo4j.gds.api.properties.nodes.LongArrayNodePropertyValues;
2527
import org.neo4j.gds.api.properties.nodes.NodePropertyValues;
28+
import org.neo4j.gds.beta.indexInverse.InverseRelationshipsAlgorithmFactory;
29+
import org.neo4j.gds.beta.indexInverse.InverseRelationshipsConfigImpl;
2630
import org.neo4j.gds.beta.pregel.PregelConfig;
2731
import org.neo4j.gds.beta.pregel.PregelResult;
2832
import org.neo4j.gds.beta.pregel.PregelSchema;
2933
import org.neo4j.gds.core.utils.paged.HugeObjectArray;
34+
import org.neo4j.gds.core.utils.progress.TaskRegistryFactory;
3035
import org.neo4j.gds.core.write.ImmutableNodeProperty;
3136
import org.neo4j.gds.core.write.NodeProperty;
3237
import org.neo4j.gds.executor.ComputationResult;
38+
import org.neo4j.gds.executor.validation.AfterLoadValidation;
39+
import org.neo4j.gds.executor.validation.ValidationConfiguration;
40+
import org.neo4j.gds.utils.StringJoining;
41+
import org.neo4j.logging.Log;
3342

43+
import java.util.Collection;
3444
import java.util.List;
45+
import java.util.Locale;
3546
import java.util.stream.Collectors;
3647

3748
import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
3849

39-
final class PregelBaseProc {
50+
public final class PregelBaseProc {
4051

41-
static <ALGO extends Algorithm<PregelResult>, CONFIG extends PregelConfig>
42-
List<NodeProperty> nodeProperties(
43-
ComputationResult<ALGO, PregelResult, CONFIG> computationResult,
44-
String propertyPrefix
52+
public static <CONFIG extends PregelConfig> ValidationConfiguration<CONFIG> ensureIndexValidation(
53+
Log log, TaskRegistryFactory taskRegistryFactory
54+
) {
55+
return new ValidationConfiguration<>() {
56+
@Override
57+
public List<AfterLoadValidation<CONFIG>> afterLoadValidations() {
58+
return List.of(
59+
(graphStore, graphProjectConfig, config) -> ensureDirectedRelationships(
60+
graphStore, config.internalRelationshipTypes(graphStore)
61+
),
62+
(graphStore, graphProjectConfig, config) -> ensureInverseIndexesExist(graphStore,
63+
config.internalRelationshipTypes(graphStore),
64+
config.concurrency(),
65+
log,
66+
taskRegistryFactory
67+
)
68+
);
69+
}
70+
};
71+
}
72+
73+
static void ensureInverseIndexesExist(
74+
GraphStore graphStore,
75+
Collection<RelationshipType> relationshipTypes,
76+
int concurrency,
77+
Log log,
78+
TaskRegistryFactory taskRegistryFactory
79+
) {
80+
relationshipTypes
81+
.stream()
82+
.filter(relType -> !graphStore.inverseIndexedRelationshipTypes().contains(relType))
83+
.forEach(relType -> {
84+
var inverseConfig = InverseRelationshipsConfigImpl
85+
.builder()
86+
.concurrency(concurrency)
87+
.relationshipType(relType.name)
88+
.build();
89+
90+
var inverseIndex = new InverseRelationshipsAlgorithmFactory()
91+
.build(graphStore, inverseConfig, log, taskRegistryFactory)
92+
.compute();
93+
94+
graphStore.addInverseIndex(relType, inverseIndex);
95+
});
96+
}
97+
98+
static void ensureDirectedRelationships(
99+
GraphStore graphStore, Collection<RelationshipType> relationshipTypes
100+
) {
101+
var relationshipSchema = graphStore.schema().relationshipSchema();
102+
var undirectedTypes = relationshipTypes
103+
.stream()
104+
.filter(relationshipSchema::isUndirected)
105+
.map(RelationshipType::name)
106+
.collect(Collectors.toList());
107+
108+
if (!undirectedTypes.isEmpty()) {
109+
throw new IllegalArgumentException(String.format(
110+
Locale.US,
111+
"This algorithm requires a directed graph, but the following configured relationship types are undirected: %s.",
112+
StringJoining.join(undirectedTypes)
113+
));
114+
}
115+
}
116+
117+
static <ALGO extends Algorithm<PregelResult>, CONFIG extends PregelConfig> List<NodeProperty> nodeProperties(
118+
ComputationResult<ALGO, PregelResult, CONFIG> computationResult, String propertyPrefix
45119
) {
46120
var compositeNodeValue = computationResult.result().nodeValues();
47121
var schema = compositeNodeValue.schema();
48122
// TODO change this to generic prefix setting
49123

50-
return schema.elements()
124+
return schema
125+
.elements()
51126
.stream()
52127
.filter(element -> element.visibility() == PregelSchema.Visibility.PUBLIC)
53128
.map(element -> {
@@ -62,24 +137,22 @@ List<NodeProperty> nodeProperties(
62137
nodePropertyValues = compositeNodeValue.doubleProperties(propertyKey).asNodeProperties();
63138
break;
64139
case LONG_ARRAY:
65-
nodePropertyValues = new HugeObjectArrayLongArrayPropertyValues(
66-
compositeNodeValue.longArrayProperties(propertyKey)
67-
);
140+
nodePropertyValues = new HugeObjectArrayLongArrayPropertyValues(compositeNodeValue.longArrayProperties(
141+
propertyKey));
68142
break;
69143
case DOUBLE_ARRAY:
70-
nodePropertyValues = new HugeObjectArrayDoubleArrayPropertyValues(
71-
compositeNodeValue.doubleArrayProperties(propertyKey)
72-
);
144+
nodePropertyValues = new HugeObjectArrayDoubleArrayPropertyValues(compositeNodeValue.doubleArrayProperties(
145+
propertyKey));
73146
break;
74147
default:
75148
throw new IllegalArgumentException("Unsupported property type: " + element.propertyType());
76149
}
77150

78-
return ImmutableNodeProperty.of(
79-
formatWithLocale("%s%s", propertyPrefix, propertyKey),
151+
return ImmutableNodeProperty.of(formatWithLocale("%s%s", propertyPrefix, propertyKey),
80152
nodePropertyValues
81153
);
82-
}).collect(Collectors.toList());
154+
})
155+
.collect(Collectors.toList());
83156
}
84157

85158
private PregelBaseProc() {}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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.pregel.proc;
21+
22+
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.params.ParameterizedTest;
24+
import org.junit.jupiter.params.provider.Arguments;
25+
import org.junit.jupiter.params.provider.MethodSource;
26+
import org.neo4j.gds.Orientation;
27+
import org.neo4j.gds.RelationshipType;
28+
import org.neo4j.gds.api.GraphStore;
29+
import org.neo4j.gds.core.utils.progress.TaskRegistryFactory;
30+
import org.neo4j.gds.extension.GdlExtension;
31+
import org.neo4j.gds.extension.GdlGraph;
32+
import org.neo4j.gds.extension.Inject;
33+
import org.neo4j.logging.NullLog;
34+
35+
import java.util.List;
36+
import java.util.stream.Stream;
37+
38+
import static org.assertj.core.api.Assertions.assertThat;
39+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
40+
41+
@GdlExtension
42+
class PregelBaseProcTest {
43+
44+
45+
@GdlGraph
46+
@GdlGraph(graphNamePrefix = "undirected", orientation = Orientation.UNDIRECTED)
47+
public static String GDL = "CREATE " + " ()-[:REL]->()," + " ()-[:REL2]->(),";
48+
49+
50+
@Inject
51+
GraphStore graphStore;
52+
53+
@Inject
54+
GraphStore undirectedGraphStore;
55+
56+
static Stream<Arguments> relTypeCombinations() {
57+
var rel = RelationshipType.of("REL");
58+
var rel2 = RelationshipType.of("REL2");
59+
return Stream.of(Arguments.arguments(List.of(rel)),
60+
Arguments.arguments(List.of(rel2)),
61+
Arguments.arguments(List.of(rel, rel2))
62+
);
63+
}
64+
65+
@ParameterizedTest
66+
@MethodSource("relTypeCombinations")
67+
void shouldGenerateInverseIndexes(List<RelationshipType> relTypes) {
68+
PregelBaseProc.ensureInverseIndexesExist(graphStore,
69+
relTypes,
70+
4,
71+
NullLog.getInstance(),
72+
TaskRegistryFactory.empty()
73+
);
74+
assertThat(graphStore.inverseIndexedRelationshipTypes()).containsExactlyElementsOf(relTypes);
75+
}
76+
77+
@Test
78+
void shouldThrowOnUndirectedRelTypes() {
79+
assertThatThrownBy(() -> PregelBaseProc.ensureDirectedRelationships(
80+
undirectedGraphStore,
81+
RelationshipType.listOf("REL", "REL2")
82+
)).hasMessage(
83+
"This algorithm requires a directed graph, but the following configured relationship types are undirected: ['REL', 'REL2']."
84+
);
85+
}
86+
}

0 commit comments

Comments
 (0)