Skip to content

Commit 8be6f52

Browse files
committed
fix(hubble): complete runtime and UI quality follow-up
1 parent 1c15ebf commit 8be6f52

34 files changed

Lines changed: 885 additions & 280 deletions

File tree

.codex-task/GLOBAL-TODO.md

Lines changed: 0 additions & 11 deletions
This file was deleted.
89.8 KB
Loading
97.8 KB
Loading
100 KB
Loading

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ go.env
106106
# Local AI-agent workspaces and run evidence
107107
.codex-task/*
108108
!.codex-task/GLOBAL-TODO.md
109+
.goal-task/
109110
.autonomous/
110111
.claude/
111112
superpowers/

.goal-task/hubble-master-sync-runtime-validation/design.md

Lines changed: 0 additions & 44 deletions
This file was deleted.

.goal-task/hubble-master-sync-runtime-validation/state.md

Lines changed: 0 additions & 76 deletions
This file was deleted.

.goal-task/hubble-master-sync-runtime-validation/todo.md

Lines changed: 0 additions & 38 deletions
This file was deleted.

hugegraph-hubble/hubble-be/src/main/java/org/apache/hugegraph/controller/graph/SampleGraphController.java

Lines changed: 157 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import java.io.InputStreamReader;
2424
import java.nio.charset.StandardCharsets;
2525
import java.util.ArrayList;
26+
import java.util.Arrays;
27+
import java.util.Collections;
28+
import java.util.HashMap;
2629
import java.util.HashSet;
2730
import java.util.LinkedHashMap;
2831
import java.util.List;
@@ -41,6 +44,13 @@
4144
import org.apache.hugegraph.driver.HugeClient;
4245
import org.apache.hugegraph.driver.SchemaManager;
4346
import org.apache.hugegraph.exception.ExternalException;
47+
import org.apache.hugegraph.structure.constant.Cardinality;
48+
import org.apache.hugegraph.structure.constant.DataType;
49+
import org.apache.hugegraph.structure.constant.Frequency;
50+
import org.apache.hugegraph.structure.constant.IdStrategy;
51+
import org.apache.hugegraph.structure.schema.EdgeLabel;
52+
import org.apache.hugegraph.structure.schema.PropertyKey;
53+
import org.apache.hugegraph.structure.schema.VertexLabel;
4454
import org.apache.hugegraph.util.Ex;
4555

4656
@RestController
@@ -152,8 +162,10 @@ public Map<String, Object> load(@PathVariable("graphspace") String graphSpace,
152162
Ex.check(loader || rank || hlm, "common.param.should-belong-to",
153163
"dataset", "[loader, hlm, rank]");
154164
HugeClient client = this.authGremlinClient(graphSpace, graph);
165+
SchemaManager schema = client.schema();
166+
validateSchemaCompatibility(schema, dataset);
155167
try {
156-
createSchema(client, dataset);
168+
createSchema(schema, dataset);
157169
String script = IDEMPOTENT_TRAVERSAL_FALLBACK_MARKER +
158170
data(dataset);
159171
client.gremlin().gremlin(script).execute();
@@ -176,8 +188,7 @@ public Map<String, Object> load(@PathVariable("graphspace") String graphSpace,
176188
return result;
177189
}
178190

179-
private static void createSchema(HugeClient client, String dataset) {
180-
SchemaManager schema = client.schema();
191+
private static void createSchema(SchemaManager schema, String dataset) {
181192
Set<String> propertyKeys = new HashSet<>();
182193
schema.getPropertyKeys().forEach(key -> propertyKeys.add(key.name()));
183194
Set<String> vertexLabels = new HashSet<>();
@@ -193,6 +204,149 @@ private static void createSchema(HugeClient client, String dataset) {
193204
}
194205
}
195206

207+
private static void validateSchemaCompatibility(SchemaManager schema,
208+
String dataset) {
209+
Map<String, PropertyKey> propertyKeys = new HashMap<>();
210+
schema.getPropertyKeys().forEach(key -> propertyKeys.put(key.name(), key));
211+
Map<String, VertexLabel> vertexLabels = new HashMap<>();
212+
schema.getVertexLabels().forEach(label ->
213+
vertexLabels.put(label.name(), label));
214+
Map<String, EdgeLabel> edgeLabels = new HashMap<>();
215+
schema.getEdgeLabels().forEach(label ->
216+
edgeLabels.put(label.name(), label));
217+
218+
if ("loader".equals(dataset)) {
219+
validateLoaderSchema(dataset, propertyKeys, vertexLabels, edgeLabels);
220+
} else if ("rank".equals(dataset)) {
221+
validateRankSchema(dataset, propertyKeys, vertexLabels, edgeLabels);
222+
} else {
223+
validateHlmSchema(dataset, propertyKeys, vertexLabels, edgeLabels);
224+
}
225+
}
226+
227+
private static void validateLoaderSchema(
228+
String dataset, Map<String, PropertyKey> propertyKeys,
229+
Map<String, VertexLabel> vertexLabels,
230+
Map<String, EdgeLabel> edgeLabels) {
231+
requireProperty(dataset, propertyKeys, "name", DataType.TEXT);
232+
requireProperty(dataset, propertyKeys, "age", DataType.INT);
233+
requireProperty(dataset, propertyKeys, "city", DataType.TEXT);
234+
requireProperty(dataset, propertyKeys, "weight", DataType.DOUBLE);
235+
requireProperty(dataset, propertyKeys, "lang", DataType.TEXT);
236+
requireProperty(dataset, propertyKeys, "date", DataType.TEXT);
237+
requireProperty(dataset, propertyKeys, "price", DataType.DOUBLE);
238+
requireVertex(dataset, vertexLabels, "person", IdStrategy.PRIMARY_KEY,
239+
names("name", "age", "city"),
240+
Collections.singletonList("name"),
241+
names("age", "city"));
242+
requireVertex(dataset, vertexLabels, "software",
243+
IdStrategy.CUSTOMIZE_NUMBER,
244+
names("name", "lang", "price"),
245+
Collections.emptyList(), Collections.emptySet());
246+
requireEdge(dataset, edgeLabels, "knows", "person", "person",
247+
names("date", "weight"));
248+
requireEdge(dataset, edgeLabels, "created", "person", "software",
249+
names("date", "weight"));
250+
}
251+
252+
private static void validateRankSchema(
253+
String dataset, Map<String, PropertyKey> propertyKeys,
254+
Map<String, VertexLabel> vertexLabels,
255+
Map<String, EdgeLabel> edgeLabels) {
256+
requireProperty(dataset, propertyKeys, "name", DataType.TEXT);
257+
requireVertex(dataset, vertexLabels, "person",
258+
IdStrategy.CUSTOMIZE_STRING,
259+
names("name"), Collections.emptyList(),
260+
Collections.emptySet());
261+
requireVertex(dataset, vertexLabels, "movie",
262+
IdStrategy.CUSTOMIZE_STRING,
263+
names("name"), Collections.emptyList(),
264+
Collections.emptySet());
265+
requireEdge(dataset, edgeLabels, "follow", "person", "person",
266+
Collections.emptySet());
267+
requireEdge(dataset, edgeLabels, "like", "person", "movie",
268+
Collections.emptySet());
269+
requireEdge(dataset, edgeLabels, "directedBy", "movie", "person",
270+
Collections.emptySet());
271+
}
272+
273+
private static void validateHlmSchema(
274+
String dataset, Map<String, PropertyKey> propertyKeys,
275+
Map<String, VertexLabel> vertexLabels,
276+
Map<String, EdgeLabel> edgeLabels) {
277+
requireProperty(dataset, propertyKeys, "name", DataType.TEXT);
278+
requireProperty(dataset, propertyKeys, "gender", DataType.TEXT);
279+
requireProperty(dataset, propertyKeys, "age", DataType.INT);
280+
requireProperty(dataset, propertyKeys, "title", DataType.TEXT);
281+
requireProperty(dataset, propertyKeys, "feature", DataType.TEXT);
282+
requireProperty(dataset, propertyKeys, "intimacy", DataType.TEXT);
283+
requireVertex(dataset, vertexLabels, "人物", IdStrategy.PRIMARY_KEY,
284+
names("name", "gender", "age", "title", "feature"),
285+
Collections.singletonList("name"),
286+
Collections.emptySet());
287+
requireEdge(dataset, edgeLabels, "关系", "人物", "人物",
288+
names("intimacy"));
289+
}
290+
291+
private static void requireProperty(String dataset,
292+
Map<String, PropertyKey> existing,
293+
String name, DataType dataType) {
294+
PropertyKey propertyKey = existing.get(name);
295+
if (propertyKey != null &&
296+
(propertyKey.dataType() != dataType ||
297+
propertyKey.cardinality() != Cardinality.SINGLE)) {
298+
incompatible(dataset, "property key", name);
299+
}
300+
}
301+
302+
private static void requireVertex(String dataset,
303+
Map<String, VertexLabel> existing,
304+
String name, IdStrategy idStrategy,
305+
Set<String> properties,
306+
List<String> primaryKeys,
307+
Set<String> nullableKeys) {
308+
VertexLabel vertexLabel = existing.get(name);
309+
if (vertexLabel != null &&
310+
(vertexLabel.idStrategy() != idStrategy ||
311+
!vertexLabel.properties().equals(properties) ||
312+
!vertexLabel.primaryKeys().equals(primaryKeys) ||
313+
!vertexLabel.nullableKeys().containsAll(nullableKeys) ||
314+
!hasDefaultTtl(vertexLabel.ttl(), vertexLabel.ttlStartTime()))) {
315+
incompatible(dataset, "vertex label", name);
316+
}
317+
}
318+
319+
private static void requireEdge(String dataset,
320+
Map<String, EdgeLabel> existing,
321+
String name, String source, String target,
322+
Set<String> properties) {
323+
EdgeLabel edgeLabel = existing.get(name);
324+
Map<String, String> expectedLink = new HashMap<>();
325+
expectedLink.put(source, target);
326+
if (edgeLabel != null &&
327+
(!edgeLabel.properties().equals(properties) ||
328+
edgeLabel.links().size() != 1 ||
329+
!edgeLabel.links().contains(expectedLink) ||
330+
edgeLabel.frequency() == Frequency.MULTIPLE ||
331+
!edgeLabel.sortKeys().isEmpty() ||
332+
!hasDefaultTtl(edgeLabel.ttl(), edgeLabel.ttlStartTime()))) {
333+
incompatible(dataset, "edge label", name);
334+
}
335+
}
336+
337+
private static boolean hasDefaultTtl(long ttl, String ttlStartTime) {
338+
return ttl == 0L && ttlStartTime == null;
339+
}
340+
341+
private static Set<String> names(String... values) {
342+
return new HashSet<>(Arrays.asList(values));
343+
}
344+
345+
private static void incompatible(String dataset, String type, String name) {
346+
throw new ExternalException("graph.sample.schema-incompatible",
347+
dataset, type, name);
348+
}
349+
196350
private static void createLoaderSchema(SchemaManager schema,
197351
Set<String> propertyKeys,
198352
Set<String> vertexLabels,

hugegraph-hubble/hubble-be/src/main/resources/i18n/messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ graph.import.file.exceed-limit=The import file size {0} exceeds limit {1}
6868
graph.import.missing-field=The import field {0} is required
6969
graph.import.field-should-array=The import field {0} should be an array
7070
graph.sample.load-failed=Failed to load sample {0} into {1}/{2}. Existing data was not cleared. Check schema or ID conflicts, then retry.
71+
graph.sample.schema-incompatible=Example {0} was not imported because the existing {1} ''{2}'' is incompatible. No Schema or data was changed; use an empty or dedicated graph.
7172
graph.import.vertex.duplicate-id=The import vertex id {0} is duplicated
7273

7374
gremlin-collection.name.unmatch-regex=Invalid gremlin statement name, valid name is up to 48 alpha-numeric characters and underscores

0 commit comments

Comments
 (0)