Skip to content

Commit 7f70751

Browse files
Merge pull request #10563 from IoannisPanagiotas/knn-validation-preload
Move Validation out of algorithm and change exception being thrown
2 parents 007f82b + f423bcd commit 7f70751

File tree

14 files changed

+181
-14
lines changed

14 files changed

+181
-14
lines changed

algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/SimilarityComputer.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,8 @@
2525
import org.neo4j.gds.api.properties.nodes.NodePropertyValues;
2626
import org.neo4j.gds.similarity.knn.KnnNodePropertySpec;
2727
import org.neo4j.gds.similarity.knn.metrics.LongArrayPropertySimilarityComputer.SortedLongArrayPropertyValues;
28-
import org.neo4j.gds.utils.StringJoining;
2928

3029
import java.util.List;
31-
import java.util.Objects;
3230

3331
import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
3432

@@ -52,14 +50,7 @@ static SimilarityComputer ofProperties(Graph graph, List<KnnNodePropertySpec> kn
5250

5351
static SimilarityComputer ofProperty(Graph graph, KnnNodePropertySpec knnNodePropertySpec) {
5452
var propertyName = knnNodePropertySpec.name();
55-
var nodeProperties = Objects.requireNonNull(
56-
graph.nodeProperties(propertyName),
57-
() -> formatWithLocale(
58-
"The property `%s` has not been loaded. Available properties: %s",
59-
propertyName,
60-
StringJoining.join(graph.availableNodeProperties())
61-
)
62-
);
53+
var nodeProperties = graph.nodeProperties(propertyName);
6354

6455
if (knnNodePropertySpec.metric() == SimilarityMetric.DEFAULT) {
6556
knnNodePropertySpec.setMetric(SimilarityMetric.defaultMetricForType(nodeProperties.valueType()));
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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.similarity.filteredknn;
21+
22+
import org.junit.jupiter.api.Test;
23+
import org.neo4j.gds.api.GraphStore;
24+
import org.neo4j.gds.similarity.knn.KnnBaseConfig;
25+
import org.neo4j.gds.similarity.knn.KnnNodePropertySpec;
26+
27+
import java.util.List;
28+
import java.util.Set;
29+
30+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
31+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
32+
import static org.mockito.ArgumentMatchers.anyCollection;
33+
import static org.mockito.ArgumentMatchers.eq;
34+
import static org.mockito.Mockito.doCallRealMethod;
35+
import static org.mockito.Mockito.mock;
36+
import static org.mockito.Mockito.when;
37+
38+
class KnnBaseConfigTest {
39+
40+
@Test
41+
void shouldNotThrowForExistingProperty(){
42+
var graphStore = mock(GraphStore.class);
43+
when(graphStore.hasNodeProperty("foo")).thenReturn(true);
44+
45+
var knnSpec = mock(KnnNodePropertySpec.class);
46+
when(knnSpec.name()).thenReturn("foo");
47+
48+
List<KnnNodePropertySpec> props = List.of(knnSpec);
49+
50+
var config = mock(KnnBaseConfig.class);
51+
when(config.nodeProperties()).thenReturn(props);
52+
53+
doCallRealMethod().when(config).checkPropertiesExist(eq(graphStore),anyCollection(),anyCollection());
54+
55+
assertDoesNotThrow( () -> config.checkPropertiesExist(graphStore,Set.of(),Set.of()));
56+
}
57+
58+
@Test
59+
void shouldThrowForNonExistingProperty(){
60+
var graphStore = mock(GraphStore.class);
61+
when(graphStore.hasNodeProperty("foo")).thenReturn(true);
62+
when(graphStore.hasNodeProperty("bar")).thenReturn(false);
63+
64+
when(graphStore.nodePropertyKeys(anyCollection())).thenReturn(Set.of("foo"));
65+
66+
var knnSpec1 = mock(KnnNodePropertySpec.class);
67+
when(knnSpec1.name()).thenReturn("foo");
68+
var knnSpec2 = mock(KnnNodePropertySpec.class);
69+
when(knnSpec2.name()).thenReturn("bar");
70+
71+
List<KnnNodePropertySpec> props = List.of(knnSpec1, knnSpec2);
72+
73+
var config = mock(KnnBaseConfig.class);
74+
when(config.nodeProperties()).thenReturn(props);
75+
doCallRealMethod().when(config).checkPropertiesExist(eq(graphStore),anyCollection(),anyCollection());
76+
77+
assertThatThrownBy(() -> config.checkPropertiesExist(graphStore,Set.of(),Set.of()))
78+
.hasMessageContaining("The property `bar` has not been loaded. Available properties: ['foo']");
79+
}
80+
}

applications/algorithms/similarity/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ dependencies {
1919
// this is needed because NodeSimilarity internally calls Wcc
2020
implementation project(':community-algorithms')
2121
implementation project(":community-configs")
22+
2223
}

applications/algorithms/similarity/src/main/java/org/neo4j/gds/applications/algorithms/similarity/SimilarityAlgorithmsMutateModeBusinessFacade.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public <RESULT> RESULT filteredKnn(
7979
mutateStep,
8080
resultBuilder
8181
);
82+
8283
}
8384

8485
public <RESULT> RESULT filteredNodeSimilarity(

applications/algorithms/similarity/src/main/java/org/neo4j/gds/applications/algorithms/similarity/SimilarityAlgorithmsWriteModeBusinessFacade.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public <RESULT> RESULT filteredKnn(
7878
(graph, __) -> similarityAlgorithms.filteredKnn(graph, configuration),
7979
writeStep,
8080
resultBuilder
81+
8182
);
8283
}
8384

proc/similarity/src/test/java/org/neo4j/gds/similarity/filteredknn/FilteredKnnMutateProcTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.List;
3737

3838
import static org.assertj.core.api.Assertions.assertThat;
39+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3940
import static org.assertj.core.api.InstanceOfAssertFactories.DOUBLE;
4041
import static org.assertj.core.api.InstanceOfAssertFactories.LONG;
4142
import static org.assertj.core.api.InstanceOfAssertFactories.MAP;
@@ -282,4 +283,11 @@ void shouldMutateWithFilteredNodesAndMultipleProperties() {
282283
);
283284
}
284285

286+
@Test
287+
void shouldIllegalArgumentExceptionThrowForNullProperty(){
288+
var query = "CALL gds.knn.filtered.mutate('filteredKnnGraph', {nodeProperties: ['foo'], mutateProperty: 'foo', mutateRelationshipType: 'bar'}) YIELD *";
289+
assertThatThrownBy(() -> runQuery(query))
290+
.hasMessageContaining("Caused by: java.lang.IllegalArgumentException");
291+
}
292+
285293
}

proc/similarity/src/test/java/org/neo4j/gds/similarity/filteredknn/FilteredKnnStatsProcTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.junit.jupiter.api.AfterEach;
2323
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
2425
import org.junit.jupiter.params.ParameterizedTest;
2526
import org.junit.jupiter.params.provider.Arguments;
2627
import org.junit.jupiter.params.provider.MethodSource;
@@ -36,6 +37,7 @@
3637
import java.util.stream.Stream;
3738

3839
import static org.assertj.core.api.Assertions.assertThat;
40+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3941
import static org.assertj.core.api.InstanceOfAssertFactories.DOUBLE;
4042
import static org.assertj.core.api.InstanceOfAssertFactories.LONG;
4143
import static org.assertj.core.api.InstanceOfAssertFactories.MAP;
@@ -143,4 +145,11 @@ private static Stream<Arguments> negativeGraphs() {
143145
Arguments.of("CREATE ({weight: -99}), ({weight: -10})", "negative long values")
144146
);
145147
}
148+
149+
@Test
150+
void shouldIllegalArgumentExceptionThrowForNullProperty(){
151+
var query = "CALL gds.knn.filtered.stats('filteredKnnGraph', {nodeProperties: ['foo']}) YIELD *";
152+
assertThatThrownBy(() -> runQuery(query))
153+
.hasMessageContaining("Caused by: java.lang.IllegalArgumentException");
154+
}
146155
}

proc/similarity/src/test/java/org/neo4j/gds/similarity/filteredknn/FilteredKnnStreamProcTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import java.util.Map;
3737
import java.util.stream.Collectors;
3838

39+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
40+
3941
class FilteredKnnStreamProcTest extends BaseProcTest {
4042

4143
@Neo4jGraph
@@ -211,4 +213,11 @@ void shouldEmployTargetNodeFilter() {
211213
Map.of("node1", idMap.get("dave"), "node2", idMap.get("bob"), "similarity", 1.0)
212214
));
213215
}
216+
217+
@Test
218+
void shouldIllegalArgumentExceptionThrowForNullProperty(){
219+
var query = "CALL gds.knn.filtered.stream('filteredKnnGraph', {nodeProperties: ['foo']}) YIELD *";
220+
assertThatThrownBy(() -> runQuery(query))
221+
.hasMessageContaining("Caused by: java.lang.IllegalArgumentException");
222+
}
214223
}

proc/similarity/src/test/java/org/neo4j/gds/similarity/filteredknn/FilteredKnnWriteProcTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.List;
4040

4141
import static org.assertj.core.api.Assertions.assertThat;
42+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
4243
import static org.assertj.core.api.InstanceOfAssertFactories.DOUBLE;
4344
import static org.assertj.core.api.InstanceOfAssertFactories.LONG;
4445
import static org.assertj.core.api.InstanceOfAssertFactories.MAP;
@@ -219,4 +220,11 @@ void shouldWriteWithFilteredNodes() {
219220
knnGraph
220221
);
221222
}
223+
224+
@Test
225+
void shouldIllegalArgumentExceptionThrowForNullProperty(){
226+
var query = "CALL gds.knn.filtered.write('filteredKnnGraph', {nodeProperties: ['foo'], writeProperty: 'foo', writeRelationshipType: 'bar'}) YIELD *";
227+
assertThatThrownBy(() -> runQuery(query))
228+
.hasMessageContaining("Caused by: java.lang.IllegalArgumentException");
229+
}
222230
}

proc/similarity/src/test/java/org/neo4j/gds/similarity/knn/KnnMutateProcTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.Map;
3535

3636
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3738
import static org.assertj.core.api.InstanceOfAssertFactories.DOUBLE;
3839
import static org.assertj.core.api.InstanceOfAssertFactories.LONG;
3940
import static org.assertj.core.api.InstanceOfAssertFactories.MAP;
@@ -279,4 +280,11 @@ void shouldMutateWithFilteredNodesAndMultipleProperties() {
279280
);
280281
}
281282

283+
@Test
284+
void shouldIllegalArgumentExceptionThrowForNullProperty(){
285+
var query = "CALL gds.knn.mutate('myGraph', {nodeProperties: ['foo'], mutateProperty: 'foo', mutateRelationshipType: 'bar'}) YIELD *";
286+
assertThatThrownBy(() -> runQuery(query))
287+
.hasMessageContaining("Caused by: java.lang.IllegalArgumentException");
288+
}
289+
282290
}

0 commit comments

Comments
 (0)