This is an automated email from the ASF dual-hosted git repository. colegreer pushed a commit to branch goSerializerFixes in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 1116c6c9873306253a0f02e67e93332f54454bc2 Author: Cole Greer <[email protected]> AuthorDate: Fri Mar 27 23:37:29 2026 -0700 Fix misc GremlinLang and GraphBinary issues in Go --- gremlin-go/driver/graphBinaryDeserializer.go | 26 ++++-- gremlin-go/driver/graphBinaryDeserializer_test.go | 104 ++++++++++++++++++++++ gremlin-go/driver/gremlinlang.go | 2 +- gremlin-go/driver/gremlinlang_test.go | 12 +-- gremlin-go/driver/traversal_test.go | 12 +-- 5 files changed, 138 insertions(+), 18 deletions(-) diff --git a/gremlin-go/driver/graphBinaryDeserializer.go b/gremlin-go/driver/graphBinaryDeserializer.go index 8a61541599..dece9bcb3f 100644 --- a/gremlin-go/driver/graphBinaryDeserializer.go +++ b/gremlin-go/driver/graphBinaryDeserializer.go @@ -254,7 +254,7 @@ func (d *GraphBinaryDeserializer) readValue(dt dataType, flag byte) (interface{} case byteBuffer: return d.readByteBuffer() case tType, directionType, mergeType, gTypeType: - return d.readEnum() + return d.readEnum(dt) default: return nil, newError(err0408GetSerializerToReadUnknownTypeError, dt) } @@ -479,6 +479,7 @@ func (d *GraphBinaryDeserializer) readVertexProperty() (*VertexProperty, error) } vp := &VertexProperty{ Element: Element{Id: id, Label: label}, + Key: label, Value: value, } vp.Properties = make([]interface{}, 0) @@ -575,14 +576,29 @@ func (d *GraphBinaryDeserializer) readByteBuffer() (*ByteBuffer, error) { return &ByteBuffer{Data: data}, nil } -func (d *GraphBinaryDeserializer) readEnum() (string, error) { +func (d *GraphBinaryDeserializer) readEnum(dt dataType) (interface{}, error) { if _, err := d.readByte(); err != nil { // type code (string) - return "", err + return nil, err } if _, err := d.readByte(); err != nil { // null flag - return "", err + return nil, err + } + s, err := d.readString() + if err != nil { + return nil, err + } + switch dt { + case tType: + return t(s), nil + case directionType: + return direction(s), nil + case mergeType: + return merge(s), nil + case gTypeType: + return gType(s), nil + default: + return s, nil } - return d.readString() } // ReadStatus reads the response status after the EndOfStream marker. diff --git a/gremlin-go/driver/graphBinaryDeserializer_test.go b/gremlin-go/driver/graphBinaryDeserializer_test.go index f6490b425d..3b9fe9a52a 100644 --- a/gremlin-go/driver/graphBinaryDeserializer_test.go +++ b/gremlin-go/driver/graphBinaryDeserializer_test.go @@ -27,6 +27,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "golang.org/x/text/language" ) // slowReader simulates a network stream that delivers data in chunks with delays. @@ -369,6 +370,109 @@ func TestGraphBinaryDeserializerComplexTypes(t *testing.T) { assert.Equal(t, "person", v.Label) }) + t.Run("round-trip VertexProperty preserves Key field", func(t *testing.T) { + original := &VertexProperty{ + Element: Element{Id: int32(1), Label: "name"}, + Key: "name", + Value: "marko", + } + + var buf bytes.Buffer + ser := newGraphBinarySerializer(newLogHandler(&defaultLogger{}, Error, language.English)) + err := ser.ser.write(original, &buf) + assert.Nil(t, err) + + d := NewGraphBinaryDeserializer(&buf) + val, err := d.ReadFullyQualified() + assert.Nil(t, err) + + vp := val.(*VertexProperty) + assert.Equal(t, "marko", vp.Value) + assert.Equal(t, "name", vp.Key, "VertexProperty.Key should survive round-trip") + }) + + t.Run("round-trip map with T enum keys preserves types", func(t *testing.T) { + original := map[interface{}]interface{}{ + T.Id: "1", + T.Label: "person", + "name": "marko", + } + + var buf bytes.Buffer + ser := newGraphBinarySerializer(newLogHandler(&defaultLogger{}, Error, language.English)) + err := ser.ser.write(original, &buf) + assert.Nil(t, err) + + d := NewGraphBinaryDeserializer(&buf) + val, err := d.ReadFullyQualified() + assert.Nil(t, err) + + m := val.(map[interface{}]interface{}) + assert.Equal(t, "1", m[T.Id], "T.Id key should survive round-trip") + assert.Equal(t, "person", m[T.Label], "T.Label key should survive round-trip") + assert.Equal(t, "marko", m["name"]) + }) + + t.Run("round-trip map with Direction enum keys preserves types", func(t *testing.T) { + original := map[interface{}]interface{}{ + Direction.Out: "outVertex", + Direction.In: "inVertex", + } + + var buf bytes.Buffer + ser := newGraphBinarySerializer(newLogHandler(&defaultLogger{}, Error, language.English)) + err := ser.ser.write(original, &buf) + assert.Nil(t, err) + + d := NewGraphBinaryDeserializer(&buf) + val, err := d.ReadFullyQualified() + assert.Nil(t, err) + + m := val.(map[interface{}]interface{}) + assert.Equal(t, "outVertex", m[Direction.Out], "Direction.Out key should survive round-trip") + assert.Equal(t, "inVertex", m[Direction.In], "Direction.In key should survive round-trip") + }) + + t.Run("round-trip map with Merge enum keys preserves types", func(t *testing.T) { + original := map[interface{}]interface{}{ + Merge.OnCreate: "created", + Merge.OnMatch: "matched", + } + + var buf bytes.Buffer + ser := newGraphBinarySerializer(newLogHandler(&defaultLogger{}, Error, language.English)) + err := ser.ser.write(original, &buf) + assert.Nil(t, err) + + d := NewGraphBinaryDeserializer(&buf) + val, err := d.ReadFullyQualified() + assert.Nil(t, err) + + m := val.(map[interface{}]interface{}) + assert.Equal(t, "created", m[Merge.OnCreate], "Merge.OnCreate key should survive round-trip") + assert.Equal(t, "matched", m[Merge.OnMatch], "Merge.OnMatch key should survive round-trip") + }) + + t.Run("round-trip map with GType enum keys preserves types", func(t *testing.T) { + original := map[interface{}]interface{}{ + GType.Int: "integer", + GType.String: "string", + } + + var buf bytes.Buffer + ser := newGraphBinarySerializer(newLogHandler(&defaultLogger{}, Error, language.English)) + err := ser.ser.write(original, &buf) + assert.Nil(t, err) + + d := NewGraphBinaryDeserializer(&buf) + val, err := d.ReadFullyQualified() + assert.Nil(t, err) + + m := val.(map[interface{}]interface{}) + assert.Equal(t, "integer", m[GType.Int], "GType.Int key should survive round-trip") + assert.Equal(t, "string", m[GType.String], "GType.String key should survive round-trip") + }) + t.Run("reads list of integers from chunked stream", func(t *testing.T) { // List type split across chunks chunk1 := []byte{ diff --git a/gremlin-go/driver/gremlinlang.go b/gremlin-go/driver/gremlinlang.go index 92a66b28b6..baec7e6547 100644 --- a/gremlin-go/driver/gremlinlang.go +++ b/gremlin-go/driver/gremlinlang.go @@ -173,7 +173,7 @@ func (gl *GremlinLang) argAsString(arg interface{}) (string, error) { case *BigDecimal: return fmt.Sprintf("%vM", v.Value()), nil case time.Time: - return fmt.Sprintf("datetime(\"%v\")", v.Format(time.RFC3339)), nil + return fmt.Sprintf("datetime(\"%v\")", v.Format("2006-01-02T15:04:05.000Z07:00")), nil // equivalent to time.RFC3339 but with ms precision instead of seconds case cardinality, column, direction, operator, order, pick, pop, barrier, scope, t, merge, gType: name := reflect.ValueOf(v).Type().Name() return fmt.Sprintf("%s.%s", strings.ToUpper(name[:1])+name[1:], v), nil diff --git a/gremlin-go/driver/gremlinlang_test.go b/gremlin-go/driver/gremlinlang_test.go index 30f66696b7..7160056f24 100644 --- a/gremlin-go/driver/gremlinlang_test.go +++ b/gremlin-go/driver/gremlinlang_test.go @@ -385,7 +385,7 @@ func Test_GremlinLang(t *testing.T) { assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Has("date", P.Gt(time.Date(2021, 1, 1, 9, 30, 0, 0, time.UTC))) }, - equals: "g.V().has(\"date\",gt(datetime(\"2021-01-01T09:30:00Z\")))", + equals: "g.V().has(\"date\",gt(datetime(\"2021-01-01T09:30:00.000Z\")))", }, { assert: func(g *GraphTraversalSource) *GraphTraversal { @@ -481,31 +481,31 @@ func Test_GremlinLang(t *testing.T) { assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Has("date", time.Date(2021, 2, 22, 0, 0, 0, 0, time.UTC)) }, - equals: "g.V().has(\"date\",datetime(\"2021-02-22T00:00:00Z\"))", + equals: "g.V().has(\"date\",datetime(\"2021-02-22T00:00:00.000Z\"))", }, { assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Has("date", P.Within(time.Date(2021, 2, 22, 0, 0, 0, 0, time.UTC), time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC))) }, - equals: "g.V().has(\"date\",within([datetime(\"2021-02-22T00:00:00Z\"),datetime(\"2021-01-01T00:00:00Z\")]))", + equals: "g.V().has(\"date\",within([datetime(\"2021-02-22T00:00:00.000Z\"),datetime(\"2021-01-01T00:00:00.000Z\")]))", }, { assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Has("date", P.Between(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2021, 2, 22, 0, 0, 0, 0, time.UTC))) }, - equals: "g.V().has(\"date\",between(datetime(\"2021-01-01T00:00:00Z\"),datetime(\"2021-02-22T00:00:00Z\")))", + equals: "g.V().has(\"date\",between(datetime(\"2021-01-01T00:00:00.000Z\"),datetime(\"2021-02-22T00:00:00.000Z\")))", }, { assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Has("date", P.Inside(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2021, 2, 22, 0, 0, 0, 0, time.UTC))) }, - equals: "g.V().has(\"date\",inside(datetime(\"2021-01-01T00:00:00Z\"),datetime(\"2021-02-22T00:00:00Z\")))", + equals: "g.V().has(\"date\",inside(datetime(\"2021-01-01T00:00:00.000Z\"),datetime(\"2021-02-22T00:00:00.000Z\")))", }, { assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Has("date", P.Gt(time.Date(2021, 1, 1, 9, 30, 0, 0, time.UTC))) }, - equals: "g.V().has(\"date\",gt(datetime(\"2021-01-01T09:30:00Z\")))", + equals: "g.V().has(\"date\",gt(datetime(\"2021-01-01T09:30:00.000Z\")))", }, { assert: func(g *GraphTraversalSource) *GraphTraversal { diff --git a/gremlin-go/driver/traversal_test.go b/gremlin-go/driver/traversal_test.go index 95eec7a5cb..e0795567b6 100644 --- a/gremlin-go/driver/traversal_test.go +++ b/gremlin-go/driver/traversal_test.go @@ -396,7 +396,7 @@ func TestTraversal(t *testing.T) { // Expect each result to contain a id. No additional items. for _, result := range results { - key, ok := result.GetInterface().(map[interface{}]interface{})["id"] + key, ok := result.GetInterface().(map[interface{}]interface{})[T.Id] assert.True(t, ok) assert.NotNil(t, key) @@ -417,7 +417,7 @@ func TestTraversal(t *testing.T) { // Expect each result to contain a key. No additional items. for _, result := range results { - key, ok := result.GetInterface().(map[interface{}]interface{})["key"] + key, ok := result.GetInterface().(map[interface{}]interface{})[T.Key] assert.True(t, ok) assert.NotNil(t, key) @@ -438,7 +438,7 @@ func TestTraversal(t *testing.T) { // Expect each result to contain a value. No additional items. for _, result := range results { - key, ok := result.GetInterface().(map[interface{}]interface{})["value"] + key, ok := result.GetInterface().(map[interface{}]interface{})[T.Value] assert.True(t, ok) assert.NotNil(t, key) @@ -459,15 +459,15 @@ func TestTraversal(t *testing.T) { // Expect each result to contain an id, key, and value. No additional items. for _, result := range results { - id, ok := result.GetInterface().(map[interface{}]interface{})["id"] + id, ok := result.GetInterface().(map[interface{}]interface{})[T.Id] assert.True(t, ok) assert.NotNil(t, id) - key, ok := result.GetInterface().(map[interface{}]interface{})["key"] + key, ok := result.GetInterface().(map[interface{}]interface{})[T.Key] assert.True(t, ok) assert.NotNil(t, key) - value, ok := result.GetInterface().(map[interface{}]interface{})["value"] + value, ok := result.GetInterface().(map[interface{}]interface{})[T.Value] assert.True(t, ok) assert.NotNil(t, value)
