Skip to content

Commit d9f4dd4

Browse files
s1cksoerenreichardt
andcommitted
Fix key and type validation
Co-Authored-By: Sören Reichardt <soren.reichardt@neotechnology.com>
1 parent 597ae77 commit d9f4dd4

File tree

2 files changed

+65
-12
lines changed

2 files changed

+65
-12
lines changed

core/src/main/java/org/neo4j/gds/core/loading/construction/NodeLabelTokenToPropertyKeys.java

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.neo4j.gds.NodeLabel;
2323
import org.neo4j.gds.api.schema.NodeSchema;
2424
import org.neo4j.gds.api.schema.PropertySchema;
25+
import org.neo4j.gds.utils.StringJoining;
2526

2627
import java.util.HashSet;
2728
import java.util.Map;
@@ -82,19 +83,45 @@ Map<String, PropertySchema> propertySchemas(
8283
NodeLabel nodeLabel,
8384
Map<String, PropertySchema> importPropertySchemas
8485
) {
85-
var inputPropertySchemas = nodeSchema.get(nodeLabel).properties();
86-
var loadPropertySchemas = importPropertySchemas
87-
.entrySet()
86+
var userDefinedPropertySchemas = nodeSchema.get(nodeLabel).properties();
87+
88+
// We validate that the property schemas we read during import have
89+
// at least a matching key and a matching type. We cannot do an
90+
// equality check because we cannot infer the default value or the
91+
// property state from just looking at the values.
92+
var overlap = importPropertySchemas
93+
.keySet()
8894
.stream()
89-
.filter(entry -> inputPropertySchemas.containsKey(entry.getKey()))
90-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
95+
.filter(userDefinedPropertySchemas::containsKey)
96+
.collect(Collectors.toSet());
97+
98+
if (overlap.size() < userDefinedPropertySchemas.size()) {
99+
var keySet = new HashSet<>(userDefinedPropertySchemas.keySet());
100+
keySet.removeAll(overlap);
101+
throw new IllegalStateException("Missing node properties during import. " +
102+
"The following keys were part of the schema, " +
103+
"but not contained in the input data: " +
104+
StringJoining.join(keySet)
105+
);
106+
}
91107

92-
if (!inputPropertySchemas.equals(loadPropertySchemas)) {
93-
throw new IllegalStateException(
94-
"Property schemas inferred from loading do not match input property schema.");
108+
// We got the same keys and can check the types.
109+
var keysWithIncompatibleTypes = overlap.stream()
110+
.filter(propertyKey -> userDefinedPropertySchemas
111+
.get(propertyKey)
112+
.valueType() != importPropertySchemas
113+
.get(propertyKey)
114+
.valueType()).collect(Collectors.toSet());
115+
116+
if (!keysWithIncompatibleTypes.isEmpty()) {
117+
throw new IllegalStateException("Incompatible value types between input schema and input data. " +
118+
"The following keys have incompatible types: " +
119+
StringJoining.join(keysWithIncompatibleTypes)
120+
);
95121
}
96122

97-
return inputPropertySchemas;
123+
124+
return userDefinedPropertySchemas;
98125
}
99126
}
100127

core/src/test/java/org/neo4j/gds/core/loading/construction/NodeLabelTokenToPropertyKeysTest.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,46 @@ void testPropertySchemasFixed() {
9999
}
100100

101101
@Test
102-
void shouldFailOnInconsistentPropertySchemas() {
102+
void shouldFailForMissingProperties() {
103103
var nodeSchema = NodeSchema.empty()
104104
.addLabel(NodeLabel.of("A"), Map.of(
105+
"foo", PropertySchema.of("foo", ValueType.LONG),
106+
"baz", PropertySchema.of("baz", ValueType.LONG)
107+
));
108+
109+
var fixed = NodeLabelTokenToPropertyKeys.fixed(nodeSchema);
110+
111+
assertThatThrownBy(() -> fixed.propertySchemas(
112+
NodeLabel.of("A"),
113+
Map.of(
105114
"foo", PropertySchema.of("foo", ValueType.LONG)
115+
)
116+
))
117+
.isInstanceOf(IllegalStateException.class)
118+
.hasMessageContaining("Missing node properties during import.")
119+
.hasMessageContaining("['baz']");
120+
}
121+
122+
@Test
123+
void shouldFailForIncompatibleTypes() {
124+
var nodeSchema = NodeSchema.empty()
125+
.addLabel(NodeLabel.of("A"), Map.of(
126+
"foo", PropertySchema.of("foo", ValueType.LONG),
127+
"baz", PropertySchema.of("baz", ValueType.LONG)
106128
));
107129

108130
var fixed = NodeLabelTokenToPropertyKeys.fixed(nodeSchema);
109131

110132
assertThatThrownBy(() -> fixed.propertySchemas(
111133
NodeLabel.of("A"),
112-
Map.of("bar", PropertySchema.of("bar", ValueType.DOUBLE))
134+
Map.of(
135+
"foo", PropertySchema.of("foo", ValueType.DOUBLE),
136+
"baz", PropertySchema.of("baz", ValueType.LONG)
137+
)
113138
))
114139
.isInstanceOf(IllegalStateException.class)
115-
.hasMessageContaining("Property schemas inferred from loading do not match input property schema.");
140+
.hasMessageContaining("Incompatible value types between input schema and input data.")
141+
.hasMessageContaining("['foo']");
116142
}
117143
}
118144

0 commit comments

Comments
 (0)