From fbed96269799e02d9e8245eeb677676f59872095 Mon Sep 17 00:00:00 2001 From: Davide Polato Date: Thu, 21 May 2026 15:29:56 +0200 Subject: [PATCH 1/5] fix(server): preserve typed DEFAULT_VALUE after JSON reload Normalizes PropertyKey default values to their declared data type upon retrieval. Previously, values stored in userdata could lose their original type during serialization and deserialization (e.g., Date becoming String), leading to type mismatches. The `defaultValue()` method now converts deserialized string representations back to their expected runtime types. This change is verified with extensive tests covering schema parsing, vertex property assignment, and both binary and text serializers. Fixes #3028 --- .../apache/hugegraph/schema/PropertyKey.java | 7 ++- .../apache/hugegraph/core/VertexCoreTest.java | 28 +++++++++ .../unit/core/SchemaElementTest.java | 61 +++++++++++++++++++ .../unit/serializer/BinarySerializerTest.java | 22 +++++++ .../unit/serializer/TextSerializerTest.java | 22 +++++++ .../hugegraph/struct/schema/PropertyKey.java | 7 ++- .../struct/schema/PropertyKeyTest.java | 47 ++++++++++++++ 7 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 hugegraph-struct/src/test/java/org/apache/hugegraph/struct/schema/PropertyKeyTest.java diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java index 64d5115d80..ecfdb7b3ef 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java @@ -121,7 +121,12 @@ public void defineDefaultValue(Object value) { public Object defaultValue() { // TODO add a field default_value - return this.userdata().get(Userdata.DEFAULT_VALUE); + Object value = this.userdata().get(Userdata.DEFAULT_VALUE); + // Userdata is reloaded from JSON as a raw Map, so a typed default + // value (e.g. Date) comes back as a String. Normalize it to the + // runtime type expected by this property key's data type. Idempotent + // for values already of the expected type. + return value == null ? null : this.validValue(value); } public boolean hasSameContent(PropertyKey other) { diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java index c56db9f2b9..285b3c026d 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java @@ -69,6 +69,7 @@ import org.apache.hugegraph.type.define.WriteType; import org.apache.hugegraph.util.Blob; import org.apache.hugegraph.util.CollectionUtil; +import org.apache.hugegraph.util.DateUtil; import org.apache.hugegraph.util.LongEncoding; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; @@ -826,6 +827,33 @@ public void testAddVertexWithDefaultPropertyValue() { Assert.assertFalse(vertex.values("age").hasNext()); } + @Test + public void testAddVertexWithDateDefaultValue() { + SchemaManager schema = graph().schema(); + + Date joined = DateUtil.parse("2026-05-14 10:11:12.345"); + schema.propertyKey("joinDate").asDate() + .userdata(Userdata.DEFAULT_VALUE, joined).create(); + schema.vertexLabel("person") + .properties("joinDate") + .nullableKeys("joinDate").append(); + + // No 'joinDate' supplied + Vertex vertex = graph().addVertex(T.label, "person", + "name", "Baby", "city", "Shanghai"); + + this.commitTx(); + + // Reload from backend then query: the typed default must survive the + // JSON serialize/reload round-trip as a Date, not a String (#3028). + vertex = graph().vertex(vertex.id()); + Object value = vertex.value("joinDate"); + Assert.assertTrue("default 'joinDate' should be a Date, was " + + (value == null ? "null" : value.getClass()), + value instanceof Date); + Assert.assertEquals(joined, value); + } + @Test public void testAddVertexWithNotExistsVertexPropKey() { HugeGraph graph = graph(); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/SchemaElementTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/SchemaElementTest.java index 0bcdddf89a..9853af107e 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/SchemaElementTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/core/SchemaElementTest.java @@ -17,8 +17,11 @@ package org.apache.hugegraph.unit.core; +import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.hugegraph.backend.id.IdGenerator; @@ -27,6 +30,8 @@ import org.apache.hugegraph.schema.Userdata; import org.apache.hugegraph.schema.VertexLabel; import org.apache.hugegraph.testutil.Assert; +import org.apache.hugegraph.type.define.Cardinality; +import org.apache.hugegraph.type.define.DataType; import org.apache.hugegraph.unit.FakeObjects; import org.apache.hugegraph.util.DateUtil; import org.junit.Test; @@ -199,6 +204,62 @@ public void testVertexLabelFromMapNormalizesCreateTimeString() { createTime); } + @Test + public void testPropertyKeyFromMapNormalizesDateDefaultValue() { + String formatted = "2026-05-14 10:11:12.345"; + Map userdata = new HashMap<>(); + userdata.put(Userdata.DEFAULT_VALUE, formatted); + + Map map = new HashMap<>(); + map.put(PropertyKey.P.ID, 1); + map.put(PropertyKey.P.NAME, "birth"); + map.put(PropertyKey.P.DATA_TYPE, DataType.DATE.string()); + map.put(PropertyKey.P.CARDINALITY, Cardinality.SINGLE.string()); + map.put(PropertyKey.P.USERDATA, userdata); + + PropertyKey propertyKey = PropertyKey.fromMap(map, + new FakeObjects().graph()); + + Object value = propertyKey.defaultValue(); + Assert.assertTrue("DEFAULT_VALUE should be a Date, was " + + (value == null ? "null" : value.getClass()), + value instanceof Date); + Assert.assertEquals(DateUtil.parse(formatted), value); + } + + @Test + public void testPropertyKeyFromMapNormalizesDateSetDefaultValue() { + String first = "2026-05-14 10:11:12.345"; + String second = "2026-05-15 11:12:13.456"; + Map userdata = new HashMap<>(); + userdata.put(Userdata.DEFAULT_VALUE, Arrays.asList(first, second)); + + Map map = new HashMap<>(); + map.put(PropertyKey.P.ID, 1); + map.put(PropertyKey.P.NAME, "tags"); + map.put(PropertyKey.P.DATA_TYPE, DataType.DATE.string()); + map.put(PropertyKey.P.CARDINALITY, Cardinality.SET.string()); + map.put(PropertyKey.P.USERDATA, userdata); + + PropertyKey propertyKey = PropertyKey.fromMap(map, + new FakeObjects().graph()); + + Object value = propertyKey.defaultValue(); + Assert.assertTrue("DEFAULT_VALUE should be a Collection, was " + + (value == null ? "null" : value.getClass()), + value instanceof Collection); + Collection values = (Collection) value; + Assert.assertEquals(2, values.size()); + for (Object element : values) { + Assert.assertTrue("each element should be a Date, was " + + (element == null ? "null" : element.getClass()), + element instanceof Date); + } + List expected = Arrays.asList(DateUtil.parse(first), + DateUtil.parse(second)); + Assert.assertTrue(values.containsAll(expected)); + } + @Test public void testBulkSetterRejectsNullUserdata() { SchemaElement schema = newSchema(); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/BinarySerializerTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/BinarySerializerTest.java index ba5136923d..5fdaa9fda4 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/BinarySerializerTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/BinarySerializerTest.java @@ -29,6 +29,7 @@ import org.apache.hugegraph.structure.HugeVertex; import org.apache.hugegraph.testutil.Assert; import org.apache.hugegraph.testutil.Whitebox; +import org.apache.hugegraph.type.define.DataType; import org.apache.hugegraph.unit.BaseUnitTest; import org.apache.hugegraph.unit.FakeObjects; import org.apache.hugegraph.util.DateUtil; @@ -132,6 +133,27 @@ public void testPropertyKeyUserdataCreateTimeRoundTripsAsDate() { Assert.assertEquals(created, value); } + @Test + public void testPropertyKeyDefaultValueRoundTripsAsDate() { + HugeConfig config = FakeObjects.newConfig(); + BinarySerializer ser = new BinarySerializer(config); + + FakeObjects objects = new FakeObjects(); + PropertyKey original = objects.newPropertyKey(IdGenerator.of(1L), + "name", DataType.DATE); + Date defaultValue = DateUtil.parse("2026-05-14 10:11:12.345"); + original.userdata(Userdata.DEFAULT_VALUE, defaultValue); + + BackendEntry entry = ser.writePropertyKey(original); + PropertyKey reloaded = ser.readPropertyKey(objects.graph(), entry); + + Object value = reloaded.defaultValue(); + Assert.assertTrue("DEFAULT_VALUE should be a Date after round-trip, " + + "was " + (value == null ? "null" : value.getClass()), + value instanceof Date); + Assert.assertEquals(defaultValue, value); + } + @Test public void testEdgeForPartition() { BinarySerializer ser = new BinarySerializer(true, true, true); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/TextSerializerTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/TextSerializerTest.java index 97df554c43..8181303991 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/TextSerializerTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/TextSerializerTest.java @@ -26,6 +26,7 @@ import org.apache.hugegraph.schema.PropertyKey; import org.apache.hugegraph.schema.Userdata; import org.apache.hugegraph.testutil.Assert; +import org.apache.hugegraph.type.define.DataType; import org.apache.hugegraph.unit.BaseUnitTest; import org.apache.hugegraph.unit.FakeObjects; import org.apache.hugegraph.util.DateUtil; @@ -53,4 +54,25 @@ public void testPropertyKeyUserdataCreateTimeRoundTripsAsDate() { value instanceof Date); Assert.assertEquals(created, value); } + + @Test + public void testPropertyKeyDefaultValueRoundTripsAsDate() { + HugeConfig config = FakeObjects.newConfig(); + TextSerializer ser = new TextSerializer(config); + + FakeObjects objects = new FakeObjects(); + PropertyKey original = objects.newPropertyKey(IdGenerator.of(1L), + "name", DataType.DATE); + Date defaultValue = DateUtil.parse("2026-05-14 10:11:12.345"); + original.userdata(Userdata.DEFAULT_VALUE, defaultValue); + + BackendEntry entry = ser.writePropertyKey(original); + PropertyKey reloaded = ser.readPropertyKey(objects.graph(), entry); + + Object value = reloaded.defaultValue(); + Assert.assertTrue("DEFAULT_VALUE should be a Date after round-trip, " + + "was " + (value == null ? "null" : value.getClass()), + value instanceof Date); + Assert.assertEquals(defaultValue, value); + } } diff --git a/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java b/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java index eaf02db04b..c7ac18760c 100644 --- a/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java +++ b/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java @@ -126,7 +126,12 @@ public void defineDefaultValue(Object value) { public Object defaultValue() { // TODO add a field default_value - return this.userdata().get(Userdata.DEFAULT_VALUE); + Object value = this.userdata().get(Userdata.DEFAULT_VALUE); + // Userdata is reloaded from JSON as a raw Map, so a typed default + // value (e.g. Date) comes back as a String. Normalize it to the + // runtime type expected by this property key's data type. Idempotent + // for values already of the expected type. + return value == null ? null : this.validValue(value); } public boolean hasSameContent(PropertyKey other) { diff --git a/hugegraph-struct/src/test/java/org/apache/hugegraph/struct/schema/PropertyKeyTest.java b/hugegraph-struct/src/test/java/org/apache/hugegraph/struct/schema/PropertyKeyTest.java new file mode 100644 index 0000000000..620588ac51 --- /dev/null +++ b/hugegraph-struct/src/test/java/org/apache/hugegraph/struct/schema/PropertyKeyTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hugegraph.struct.schema; + +import java.util.Date; + +import org.apache.hugegraph.id.IdGenerator; +import org.apache.hugegraph.type.define.DataType; +import org.apache.hugegraph.util.DateUtil; +import org.junit.Assert; +import org.junit.Test; + +public class PropertyKeyTest { + + @Test + public void testDefaultValueNormalizedToDate() { + // Userdata reloaded from JSON keeps ~default_value as a String; + // defaultValue() must normalize it to the data type's runtime type + // (#3028). + String formatted = "2026-05-14 10:11:12.345"; + PropertyKey propertyKey = new PropertyKey(null, IdGenerator.of(1), + "joinDate"); + propertyKey.dataType(DataType.DATE); + propertyKey.userdata(Userdata.DEFAULT_VALUE, formatted); + + Object value = propertyKey.defaultValue(); + Assert.assertTrue("DEFAULT_VALUE should be a Date, was " + + (value == null ? "null" : value.getClass()), + value instanceof Date); + Assert.assertEquals(DateUtil.parse(formatted), value); + } +} From a0fe682e4706aaa6450cccdd0428946c0f333735 Mon Sep 17 00:00:00 2001 From: Davide Polato Date: Mon, 1 Jun 2026 12:22:07 +0200 Subject: [PATCH 2/5] fix(schema): enforce Set container for SET-cardinality defaults Update `convValue()` in both core and struct to choose the target collection based on `this.cardinality`. This ensures SET defaults reloaded from JSON (as ArrayList) are converted to a `LinkedHashSet`, collapsing duplicates and preserving cardinality semantics. Added explicit tests to verify duplicate collapsing. --- .../apache/hugegraph/schema/PropertyKey.java | 13 ++++++++++- .../hugegraph/struct/schema/PropertyKey.java | 13 ++++++++++- .../struct/schema/PropertyKeyTest.java | 23 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java index ecfdb7b3ef..7a4d325cce 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java @@ -122,11 +122,22 @@ public void defineDefaultValue(Object value) { public Object defaultValue() { // TODO add a field default_value Object value = this.userdata().get(Userdata.DEFAULT_VALUE); + if (value == null) { + return null; + } + // Userdata is reloaded from JSON as a raw Map, so a typed default // value (e.g. Date) comes back as a String. Normalize it to the // runtime type expected by this property key's data type. Idempotent // for values already of the expected type. - return value == null ? null : this.validValue(value); + Object normalized = this.validValue(value); + + // For SET cardinality, ensure we return a Set container and collapse duplicates + if (this.cardinality == Cardinality.SET && normalized instanceof Collection) { + return new LinkedHashSet<>((Collection) normalized); + } + + return normalized; } public boolean hasSameContent(PropertyKey other) { diff --git a/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java b/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java index c7ac18760c..a2b4eca8f8 100644 --- a/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java +++ b/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java @@ -127,11 +127,22 @@ public void defineDefaultValue(Object value) { public Object defaultValue() { // TODO add a field default_value Object value = this.userdata().get(Userdata.DEFAULT_VALUE); + if (value == null) { + return null; + } + // Userdata is reloaded from JSON as a raw Map, so a typed default // value (e.g. Date) comes back as a String. Normalize it to the // runtime type expected by this property key's data type. Idempotent // for values already of the expected type. - return value == null ? null : this.validValue(value); + Object normalized = this.validValue(value); + + // For SET cardinality, ensure we return a Set container and collapse duplicates + if (this.cardinality == Cardinality.SET && normalized instanceof Collection) { + return new LinkedHashSet<>((Collection) normalized); + } + + return normalized; } public boolean hasSameContent(PropertyKey other) { diff --git a/hugegraph-struct/src/test/java/org/apache/hugegraph/struct/schema/PropertyKeyTest.java b/hugegraph-struct/src/test/java/org/apache/hugegraph/struct/schema/PropertyKeyTest.java index 620588ac51..d8441144f2 100644 --- a/hugegraph-struct/src/test/java/org/apache/hugegraph/struct/schema/PropertyKeyTest.java +++ b/hugegraph-struct/src/test/java/org/apache/hugegraph/struct/schema/PropertyKeyTest.java @@ -17,9 +17,12 @@ package org.apache.hugegraph.struct.schema; +import java.util.Arrays; import java.util.Date; +import java.util.Set; import org.apache.hugegraph.id.IdGenerator; +import org.apache.hugegraph.type.define.Cardinality; import org.apache.hugegraph.type.define.DataType; import org.apache.hugegraph.util.DateUtil; import org.junit.Assert; @@ -44,4 +47,24 @@ public void testDefaultValueNormalizedToDate() { value instanceof Date); Assert.assertEquals(DateUtil.parse(formatted), value); } + + @Test + public void testSetDefaultValueCollapsesDuplicatesAndReturnsSet() { + String formatted = "2026-05-14 10:11:12.345"; + PropertyKey propertyKey = new PropertyKey(null, IdGenerator.of(1), + "joinDate"); + propertyKey.dataType(DataType.DATE); + propertyKey.cardinality(Cardinality.SET); + propertyKey.userdata(Userdata.DEFAULT_VALUE, + Arrays.asList(formatted, formatted)); + + Object value = propertyKey.defaultValue(); + Assert.assertTrue("DEFAULT_VALUE should be a Set, was " + + (value == null ? "null" : value.getClass()), + value instanceof Set); + + Set values = (Set) value; + Assert.assertEquals(1, values.size()); + Assert.assertTrue(values.contains(DateUtil.parse(formatted))); + } } From aeb354aeb54fa4268515dbef7f2e1ecef0750761 Mon Sep 17 00:00:00 2001 From: Davide Polato Date: Tue, 2 Jun 2026 15:30:46 +0200 Subject: [PATCH 3/5] fix(schema): enforce strict validation when converting property key values The `convValue` method now utilizes `validValueOrThrow` to ensure that any value which cannot be successfully normalized to the PropertyKey's declared data type will explicitly throw an exception. This prevents silent type mismatches or unexpected behavior and reinforces type safety when handling property values, especially after deserialization. --- .../src/main/java/org/apache/hugegraph/schema/PropertyKey.java | 2 +- .../java/org/apache/hugegraph/struct/schema/PropertyKey.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java index 7a4d325cce..37f0ed56f0 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java @@ -130,7 +130,7 @@ public Object defaultValue() { // value (e.g. Date) comes back as a String. Normalize it to the // runtime type expected by this property key's data type. Idempotent // for values already of the expected type. - Object normalized = this.validValue(value); + Object normalized = this.validValueOrThrow(value); // For SET cardinality, ensure we return a Set container and collapse duplicates if (this.cardinality == Cardinality.SET && normalized instanceof Collection) { diff --git a/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java b/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java index a2b4eca8f8..aa75d09359 100644 --- a/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java +++ b/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java @@ -135,7 +135,7 @@ public Object defaultValue() { // value (e.g. Date) comes back as a String. Normalize it to the // runtime type expected by this property key's data type. Idempotent // for values already of the expected type. - Object normalized = this.validValue(value); + Object normalized = this.validValueOrThrow(value); // For SET cardinality, ensure we return a Set container and collapse duplicates if (this.cardinality == Cardinality.SET && normalized instanceof Collection) { From dcacd32233f7f764f55011d13971360d41b29ef7 Mon Sep 17 00:00:00 2001 From: Davide Polato Date: Tue, 2 Jun 2026 15:32:53 +0200 Subject: [PATCH 4/5] test(schema): cover SET defaults through server round trips Add server-side regression tests to ensure that Cardinality.SET default values (which deserialize from JSON as String ArrayLists) are correctly normalized to a Set and duplicates are collapsed. --- .../apache/hugegraph/core/VertexCoreTest.java | 31 +++++++++++++++++++ .../unit/serializer/BinarySerializerTest.java | 30 ++++++++++++++++++ .../unit/serializer/TextSerializerTest.java | 29 +++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java index 285b3c026d..d33f9bb07d 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/VertexCoreTest.java @@ -19,6 +19,7 @@ import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashSet; @@ -854,6 +855,36 @@ public void testAddVertexWithDateDefaultValue() { Assert.assertEquals(joined, value); } + @Test + public void testAddVertexWithDateSetDefaultValue() { + SchemaManager schema = graph().schema(); + + String dateStr = "2026-05-14 10:11:12.345"; + Date expected = DateUtil.parse(dateStr); + + // Simulate JSON-deserialized default: ArrayList of Strings with duplicates + schema.propertyKey("joinDates").asDate().valueSet() + .userdata(Userdata.DEFAULT_VALUE, Arrays.asList(dateStr, dateStr)) + .create(); + schema.vertexLabel("person") + .properties("joinDates") + .nullableKeys("joinDates").append(); + + Vertex vertex = graph().addVertex(T.label, "person", + "name", "Baby", "city", "Shanghai"); + this.commitTx(); + + vertex = graph().vertex(vertex.id()); + Object raw = vertex.value("joinDates"); + + Assert.assertTrue("joinDates should be a Set, was " + + (raw == null ? "null" : raw.getClass()), + raw instanceof Set); + Set values = (Set) raw; + Assert.assertEquals("duplicates must be collapsed", 1, values.size()); + Assert.assertTrue(values.contains(expected)); + } + @Test public void testAddVertexWithNotExistsVertexPropKey() { HugeGraph graph = graph(); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/BinarySerializerTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/BinarySerializerTest.java index 5fdaa9fda4..02d64bcc78 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/BinarySerializerTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/BinarySerializerTest.java @@ -17,7 +17,9 @@ package org.apache.hugegraph.unit.serializer; +import java.util.Arrays; import java.util.Date; +import java.util.Set; import org.apache.hugegraph.backend.id.IdGenerator; import org.apache.hugegraph.backend.serializer.BinarySerializer; @@ -29,6 +31,7 @@ import org.apache.hugegraph.structure.HugeVertex; import org.apache.hugegraph.testutil.Assert; import org.apache.hugegraph.testutil.Whitebox; +import org.apache.hugegraph.type.define.Cardinality; import org.apache.hugegraph.type.define.DataType; import org.apache.hugegraph.unit.BaseUnitTest; import org.apache.hugegraph.unit.FakeObjects; @@ -154,6 +157,33 @@ public void testPropertyKeyDefaultValueRoundTripsAsDate() { Assert.assertEquals(defaultValue, value); } + @Test + public void testPropertyKeySetDefaultValueRoundTripsAsDate() { + HugeConfig config = FakeObjects.newConfig(); + BinarySerializer ser = new BinarySerializer(config); + + FakeObjects objects = new FakeObjects(); + PropertyKey original = objects.newPropertyKey(IdGenerator.of(2L), + "tags", DataType.DATE); + original.cardinality(Cardinality.SET); + + String dateStr = "2026-05-14 10:11:12.345"; + Date expected = DateUtil.parse(dateStr); + // ArrayList with duplicates — what JSON deserialization produces + original.userdata(Userdata.DEFAULT_VALUE, Arrays.asList(dateStr, dateStr)); + + BackendEntry entry = ser.writePropertyKey(original); + PropertyKey reloaded = ser.readPropertyKey(objects.graph(), entry); + + Object value = reloaded.defaultValue(); + Assert.assertTrue("DEFAULT_VALUE should be a Set after round-trip, was " + + (value == null ? "null" : value.getClass()), + value instanceof Set); + Set values = (Set) value; + Assert.assertEquals("duplicates must be collapsed", 1, values.size()); + Assert.assertTrue(values.contains(expected)); + } + @Test public void testEdgeForPartition() { BinarySerializer ser = new BinarySerializer(true, true, true); diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/TextSerializerTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/TextSerializerTest.java index 8181303991..4d55a9b046 100644 --- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/TextSerializerTest.java +++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/serializer/TextSerializerTest.java @@ -17,7 +17,9 @@ package org.apache.hugegraph.unit.serializer; +import java.util.Arrays; import java.util.Date; +import java.util.Set; import org.apache.hugegraph.backend.id.IdGenerator; import org.apache.hugegraph.backend.serializer.TextSerializer; @@ -26,6 +28,7 @@ import org.apache.hugegraph.schema.PropertyKey; import org.apache.hugegraph.schema.Userdata; import org.apache.hugegraph.testutil.Assert; +import org.apache.hugegraph.type.define.Cardinality; import org.apache.hugegraph.type.define.DataType; import org.apache.hugegraph.unit.BaseUnitTest; import org.apache.hugegraph.unit.FakeObjects; @@ -75,4 +78,30 @@ public void testPropertyKeyDefaultValueRoundTripsAsDate() { value instanceof Date); Assert.assertEquals(defaultValue, value); } + + @Test + public void testPropertyKeySetDefaultValueRoundTripsAsDate() { + HugeConfig config = FakeObjects.newConfig(); + TextSerializer ser = new TextSerializer(config); + + FakeObjects objects = new FakeObjects(); + PropertyKey original = objects.newPropertyKey(IdGenerator.of(2L), + "tags", DataType.DATE); + original.cardinality(Cardinality.SET); + + String dateStr = "2026-05-14 10:11:12.345"; + Date expected = DateUtil.parse(dateStr); + original.userdata(Userdata.DEFAULT_VALUE, Arrays.asList(dateStr, dateStr)); + + BackendEntry entry = ser.writePropertyKey(original); + PropertyKey reloaded = ser.readPropertyKey(objects.graph(), entry); + + Object value = reloaded.defaultValue(); + Assert.assertTrue("DEFAULT_VALUE should be a Set after round-trip, was " + + (value == null ? "null" : value.getClass()), + value instanceof Set); + Set values = (Set) value; + Assert.assertEquals("duplicates must be collapsed", 1, values.size()); + Assert.assertTrue(values.contains(expected)); + } } From 5481f9e66deaca9a5ef6d7779019583e2b5307b5 Mon Sep 17 00:00:00 2001 From: Davide Polato Date: Tue, 2 Jun 2026 18:26:42 +0200 Subject: [PATCH 5/5] Address reviewer comments --- .../java/org/apache/hugegraph/schema/PropertyKey.java | 11 +++++------ .../apache/hugegraph/struct/schema/PropertyKey.java | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java index 37f0ed56f0..5bf34ea530 100644 --- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java +++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/schema/PropertyKey.java @@ -130,14 +130,13 @@ public Object defaultValue() { // value (e.g. Date) comes back as a String. Normalize it to the // runtime type expected by this property key's data type. Idempotent // for values already of the expected type. - Object normalized = this.validValueOrThrow(value); - - // For SET cardinality, ensure we return a Set container and collapse duplicates - if (this.cardinality == Cardinality.SET && normalized instanceof Collection) { - return new LinkedHashSet<>((Collection) normalized); + Object raw = value; + if (this.cardinality == Cardinality.SET && value instanceof Collection && + !(value instanceof Set)) { + raw = new LinkedHashSet<>((Collection) value); } - return normalized; + return this.validValueOrThrow(raw); } public boolean hasSameContent(PropertyKey other) { diff --git a/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java b/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java index aa75d09359..81dae36697 100644 --- a/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java +++ b/hugegraph-struct/src/main/java/org/apache/hugegraph/struct/schema/PropertyKey.java @@ -135,14 +135,13 @@ public Object defaultValue() { // value (e.g. Date) comes back as a String. Normalize it to the // runtime type expected by this property key's data type. Idempotent // for values already of the expected type. - Object normalized = this.validValueOrThrow(value); - - // For SET cardinality, ensure we return a Set container and collapse duplicates - if (this.cardinality == Cardinality.SET && normalized instanceof Collection) { - return new LinkedHashSet<>((Collection) normalized); + Object raw = value; + if (this.cardinality == Cardinality.SET && value instanceof Collection && + !(value instanceof Set)) { + raw = new LinkedHashSet<>((Collection) value); } - return normalized; + return this.validValueOrThrow(raw); } public boolean hasSameContent(PropertyKey other) {