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); + } +}