You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by xi...@apache.org on 2025/02/20 18:13:36 UTC

(tinkerpop) 01/01: Updated datetime, added GValue, updated Options strategy, removed unneeded strategy package name, added NewTraversalStrategy to allow custom strategies.

This is an automated email from the ASF dual-hosted git repository.

xiazcy pushed a commit to branch go-opts-strat-gl-updates
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 97a6b9d663d127d5336ef75732ab164f70be835e
Author: Yang Xia <55...@users.noreply.github.com>
AuthorDate: Thu Feb 20 10:13:22 2025 -0800

    Updated datetime, added GValue, updated Options strategy, removed unneeded strategy package name, added NewTraversalStrategy to allow custom strategies.
---
 gremlin-go/driver/connection_test.go           |  2 +-
 gremlin-go/driver/gValue.go                    | 61 ++++++++++++++++++
 gremlin-go/driver/gValue_test.go               | 87 ++++++++++++++++++++++++++
 gremlin-go/driver/graphTraversalSource.go      |  7 ++-
 gremlin-go/driver/graphTraversalSource_test.go | 31 +++------
 gremlin-go/driver/gremlinlang.go               | 25 +++++++-
 gremlin-go/driver/gremlinlang_test.go          | 26 ++++----
 gremlin-go/driver/request.go                   |  2 +-
 gremlin-go/driver/strategies.go                | 70 ++++++++++-----------
 gremlin-go/driver/strategies_test.go           | 34 ++++++----
 gremlin-go/driver/traversal_test.go            |  4 +-
 11 files changed, 258 insertions(+), 91 deletions(-)

diff --git a/gremlin-go/driver/connection_test.go b/gremlin-go/driver/connection_test.go
index 6bcb2e75dd..fc7497b767 100644
--- a/gremlin-go/driver/connection_test.go
+++ b/gremlin-go/driver/connection_test.go
@@ -606,7 +606,7 @@ func TestConnection(t *testing.T) {
 		assert.True(t, ok)
 		assert.NotNil(t, result)
 
-		g := cloneGraphTraversalSource(&Graph{}, NewBytecode(nil), NewGremlinLang(nil), nil)
+		g := cloneGraphTraversalSource(&Graph{}, NewGremlinLang(nil), nil)
 		b := g.V().Count().Bytecode
 		resultSet, err = client.submitBytecode(b)
 		assert.Nil(t, err)
diff --git a/gremlin-go/driver/gValue.go b/gremlin-go/driver/gValue.go
new file mode 100644
index 0000000000..a1e5028129
--- /dev/null
+++ b/gremlin-go/driver/gValue.go
@@ -0,0 +1,61 @@
+/*
+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 gremlingo
+
+import (
+	"fmt"
+	"strings"
+)
+
+// GValue is a variable or literal value that is used in a Traversal. It is composed of a key-value pair where the key
+// is the name given to the variable and the value is the object that the variable resolved to.
+type GValue interface {
+	Name() string
+	IsNil() bool
+	Value() interface{}
+}
+
+type gValue struct {
+	name  string
+	value interface{}
+}
+
+// NewGValue creates a new GValue to be used in traversals. The GValue name cannot begin with "_".
+func NewGValue(name string, value interface{}) GValue {
+	if strings.HasPrefix(name, "_") {
+		panic(fmt.Sprintf("invalid GValue name '%v'. Should not start with _.", name))
+	}
+	return &gValue{name, value}
+}
+
+// Name returns the name of the GValue.
+func (gv *gValue) Name() string {
+	return gv.name
+}
+
+// IsNil determines if the value held is of a nil value.
+func (gv *gValue) IsNil() bool {
+	return gv.value == nil
+}
+
+// Value returns the value held by the GValue.
+func (gv *gValue) Value() interface{} {
+	return gv.value
+}
diff --git a/gremlin-go/driver/gValue_test.go b/gremlin-go/driver/gValue_test.go
new file mode 100644
index 0000000000..cd4235278d
--- /dev/null
+++ b/gremlin-go/driver/gValue_test.go
@@ -0,0 +1,87 @@
+/*
+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 gremlingo
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestGValue(t *testing.T) {
+
+	t.Run("test simple gValue", func(t *testing.T) {
+		gVal := NewGValue("intVal", 2)
+		assert.Equal(t, "intVal", gVal.Name())
+		assert.Equal(t, 2, gVal.Value())
+		assert.False(t, gVal.IsNil())
+	})
+
+	t.Run("test gValue allow parameter reuse with arrays", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+		val := [3]int{1, 2, 3}
+		param := NewGValue("ids", val)
+		gl := g.Inject(param).V(param).GremlinLang
+		assert.Equal(t, "g.inject(ids).V(ids)", gl.GetGremlin())
+		assert.Equal(t, val, gl.parameters["ids"])
+	})
+
+	t.Run("test gValue allow parameter reuse with slices", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+		val := []int{1, 2, 3}
+		param := NewGValue("ids", val)
+		gl := g.Inject(param).V(param).GremlinLang
+		assert.Equal(t, "g.inject(ids).V(ids)", gl.GetGremlin())
+		assert.Equal(t, val, gl.parameters["ids"])
+	})
+
+	t.Run("test gValue allow parameter reuse with maps", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+		val := map[string]int{"foo": 1, "bar": 2}
+		param := NewGValue("ids", val)
+		gl := g.Inject(param).V(param).GremlinLang
+		assert.Equal(t, "g.inject(ids).V(ids)", gl.GetGremlin())
+		assert.Equal(t, val, gl.parameters["ids"])
+	})
+
+	t.Run("test gValue name not duplicated", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+		param1 := NewGValue("ids", [2]int{1, 2})
+		param2 := NewGValue("ids", [2]int{2, 3})
+		assert.Panics(t, func() { g.Inject(param1).V(param2) }, "parameter with name ids already exists.")
+	})
+
+	t.Run("test invalid name that starts with _", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+		assert.Panics(t, func() { g.Inject(NewGValue("_ids", [2]int{1, 2})) },
+			"invalid GValue name _1. Should not start with _.")
+	})
+
+	t.Run("test name is valid identifier", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+		assert.Panics(t, func() { g.Inject(NewGValue("1a", [2]int{1, 2})) },
+			"invalid parameter name '1a'")
+	})
+
+	t.Run("test name is not a number", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+		assert.Panics(t, func() { g.Inject(NewGValue("1", [2]int{1, 2})) },
+			"invalid parameter name '1'")
+	})
+}
diff --git a/gremlin-go/driver/graphTraversalSource.go b/gremlin-go/driver/graphTraversalSource.go
index a93e7768ed..0035de1664 100644
--- a/gremlin-go/driver/graphTraversalSource.go
+++ b/gremlin-go/driver/graphTraversalSource.go
@@ -120,8 +120,11 @@ func (gts *GraphTraversalSource) WithoutStrategies(args ...TraversalStrategy) *G
 func (gts *GraphTraversalSource) With(key interface{}, value interface{}) *GraphTraversalSource {
 	source := gts.clone()
 
-	//TODO verify
-	var optionsStrategy TraversalStrategy = gts.gremlinLang.optionsStrategies[0]
+	//TODO verify remote when connection is set-up
+	var optionsStrategy TraversalStrategy = nil
+	if len(gts.gremlinLang.optionsStrategies) != 0 {
+		optionsStrategy = gts.gremlinLang.optionsStrategies[0]
+	}
 
 	if optionsStrategy == nil {
 		optionsStrategy = OptionsStrategy(map[string]interface{}{key.(string): value})
diff --git a/gremlin-go/driver/graphTraversalSource_test.go b/gremlin-go/driver/graphTraversalSource_test.go
index 4c31423107..7a842e5e4b 100644
--- a/gremlin-go/driver/graphTraversalSource_test.go
+++ b/gremlin-go/driver/graphTraversalSource_test.go
@@ -26,44 +26,31 @@ import (
 
 func TestGraphTraversalSource(t *testing.T) {
 
-	// TODO update once option strategy application is property updated
 	t.Run("GraphTraversalSource.With tests", func(t *testing.T) {
 		t.Run("Test for single property", func(t *testing.T) {
-			g := &GraphTraversalSource{graph: &Graph{}, bytecode: NewBytecode(nil), remoteConnection: nil}
+			g := &GraphTraversalSource{graph: &Graph{}, gremlinLang: NewGremlinLang(nil), remoteConnection: nil}
 			traversal := g.With("foo", "bar")
 			assert.NotNil(t, traversal)
-			assert.Equal(t, 1, len(traversal.bytecode.sourceInstructions))
-			instruction := traversal.bytecode.sourceInstructions[0]
-			assert.Equal(t, "withStrategies", instruction.operator)
-			assert.Equal(t, "org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy",
-				instruction.arguments[0].(*traversalStrategy).name)
-			config := instruction.arguments[0].(*traversalStrategy).configuration
+			assert.Equal(t, 1, len(traversal.gremlinLang.optionsStrategies))
+			config := traversal.gremlinLang.optionsStrategies[0].configuration
 			assert.Equal(t, map[string]interface{}{"foo": "bar"}, config)
 		})
 
 		t.Run("Test for multiple property", func(t *testing.T) {
-			g := &GraphTraversalSource{graph: &Graph{}, bytecode: NewBytecode(nil), remoteConnection: nil}
+			g := &GraphTraversalSource{graph: &Graph{}, gremlinLang: NewGremlinLang(nil), remoteConnection: nil}
 			traversal := g.With("foo", "bar").With("foo2", "bar2")
 			assert.NotNil(t, traversal)
-			assert.Equal(t, 1, len(traversal.bytecode.sourceInstructions))
-			instruction := traversal.bytecode.sourceInstructions[0]
-			assert.Equal(t, "withStrategies", instruction.operator)
-			assert.Equal(t, "org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy",
-				instruction.arguments[0].(*traversalStrategy).name)
-			config := instruction.arguments[0].(*traversalStrategy).configuration
+			assert.Equal(t, 1, len(traversal.gremlinLang.optionsStrategies))
+			config := traversal.gremlinLang.optionsStrategies[0].configuration
 			assert.Equal(t, map[string]interface{}{"foo": "bar", "foo2": "bar2"}, config)
 		})
 
 		t.Run("Test for property replacement", func(t *testing.T) {
-			g := &GraphTraversalSource{graph: &Graph{}, bytecode: NewBytecode(nil), remoteConnection: nil}
+			g := &GraphTraversalSource{graph: &Graph{}, gremlinLang: NewGremlinLang(nil), remoteConnection: nil}
 			traversal := g.With("foo", "bar").With("foo", "not bar")
 			assert.NotNil(t, traversal)
-			assert.Equal(t, 1, len(traversal.bytecode.sourceInstructions))
-			instruction := traversal.bytecode.sourceInstructions[0]
-			assert.Equal(t, "withStrategies", instruction.operator)
-			assert.Equal(t, "org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy",
-				instruction.arguments[0].(*traversalStrategy).name)
-			config := instruction.arguments[0].(*traversalStrategy).configuration
+			assert.Equal(t, 1, len(traversal.gremlinLang.optionsStrategies))
+			config := traversal.gremlinLang.optionsStrategies[0].configuration
 			assert.Equal(t, map[string]interface{}{"foo": "not bar"}, config)
 		})
 	})
diff --git a/gremlin-go/driver/gremlinlang.go b/gremlin-go/driver/gremlinlang.go
index 66cdc20826..2d66d0c47f 100644
--- a/gremlin-go/driver/gremlinlang.go
+++ b/gremlin-go/driver/gremlinlang.go
@@ -21,12 +21,14 @@ package gremlingo
 
 import (
 	"fmt"
+	"go/token"
 	"math"
 	"math/big"
 	"reflect"
 	"strconv"
 	"strings"
 	"sync/atomic"
+	"time"
 )
 
 type GremlinLang struct {
@@ -150,6 +152,8 @@ func (gl *GremlinLang) argAsString(arg interface{}) (string, error) {
 		return fmt.Sprintf("%vD", v), nil
 	case *BigDecimal, BigDecimal:
 		return fmt.Sprintf("%vM", v), nil
+	case time.Time:
+		return fmt.Sprintf("datetime(\"%v\")", v.Format(time.RFC3339)), nil
 	case cardinality, column, direction, operator, order, pick, pop, barrier, scope, t, merge:
 		name := reflect.ValueOf(v).Type().Name()
 		return fmt.Sprintf("%s.%s", strings.ToUpper(name[:1])+name[1:], v), nil
@@ -198,6 +202,25 @@ func (gl *GremlinLang) argAsString(arg interface{}) (string, error) {
 			gl.parameters[key] = val
 		}
 		return v.GetGremlin("__"), nil
+	case GValue:
+		key := v.Name()
+		if !token.IsIdentifier(key) {
+			panic(fmt.Sprintf("invalid parameter name '%v'.", key))
+		}
+		value := v.Value()
+		if val, ok := gl.parameters[key]; ok {
+			if reflect.TypeOf(val).Kind() == reflect.Slice || reflect.TypeOf(value).Kind() == reflect.Slice ||
+				reflect.TypeOf(val).Kind() == reflect.Map || reflect.TypeOf(value).Kind() == reflect.Map {
+				if !reflect.DeepEqual(val, value) {
+					panic(fmt.Sprintf("parameter with name '%v' already exists.", key))
+				}
+			} else if val != value {
+				panic(fmt.Sprintf("parameter with name '%v' already exists.", key))
+			}
+		} else {
+			gl.parameters[key] = v.Value()
+		}
+		return key, nil
 	default:
 		switch reflect.TypeOf(arg).Kind() {
 		case reflect.Map:
@@ -395,7 +418,7 @@ func (gl *GremlinLang) buildStrategyArgs(args ...interface{}) string {
 			continue
 		}
 		// special handling for OptionsStrategy
-		if strategy.name == decorationNamespace+"OptionsStrategy" {
+		if strategy.name == "OptionsStrategy" {
 			gl.optionsStrategies = append(gl.optionsStrategies, strategy)
 			continue
 		}
diff --git a/gremlin-go/driver/gremlinlang_test.go b/gremlin-go/driver/gremlinlang_test.go
index c4f3237a6e..450bb729ff 100644
--- a/gremlin-go/driver/gremlinlang_test.go
+++ b/gremlin-go/driver/gremlinlang_test.go
@@ -20,7 +20,6 @@ under the License.
 package gremlingo
 
 import (
-	"fmt"
 	"regexp"
 	"testing"
 	"time"
@@ -389,7 +388,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(new Date(121,1,1,9,30,0)))",
+			equals: "g.V().has(\"date\",gt(datetime(\"2021-01-01T09:30:00Z\")))",
 		},
 		{
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
@@ -485,31 +484,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\",new Date(121,2,22,0,0,0))",
+			equals: "g.V().has(\"date\",datetime(\"2021-02-22T00:00:00Z\"))",
 		},
 		{
 			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([new Date(121,2,22,0,0,0),new Date(121,1,1,0,0,0)]))",
+			equals: "g.V().has(\"date\",within([datetime(\"2021-02-22T00:00:00Z\"),datetime(\"2021-01-01T00:00:00Z\")]))",
 		},
 		{
 			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(new Date(121,1,1,0,0,0),new Date(121,2,22,0,0,0)))",
+			equals: "g.V().has(\"date\",between(datetime(\"2021-01-01T00:00:00Z\"),datetime(\"2021-02-22T00:00:00Z\")))",
 		},
 		{
 			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(new Date(121,1,1,0,0,0),new Date(121,2,22,0,0,0)))",
+			equals: "g.V().has(\"date\",inside(datetime(\"2021-01-01T00:00:00Z\"),datetime(\"2021-02-22T00:00:00Z\")))",
 		},
 		{
 			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(new Date(121,1,1,9,30,0)))",
+			equals: "g.V().has(\"date\",gt(datetime(\"2021-01-01T09:30:00Z\")))",
 		},
 		{
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
@@ -563,7 +562,7 @@ func Test_GremlinLang(t *testing.T) {
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
 				return g.WithStrategies(ReadOnlyStrategy()).AddV("test")
 			},
-			equals: "g.withStrategies(new ReadOnlyStrategy()).addV(\"test\")",
+			equals: "g.withStrategies(ReadOnlyStrategy).addV(\"test\")",
 		},
 		{
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
@@ -590,19 +589,20 @@ func Test_GremlinLang(t *testing.T) {
 				return g.WithStrategies(ReadOnlyStrategy(), SubgraphStrategy(SubgraphStrategyConfig{Vertices: T__.Has("region", "US-TX"), Edges: T__.HasLabel("route")})).V().Count()
 			},
 			containsRandomClassParams: true,
-			equals:                    "g.withStrategies(new ReadOnlyStrategy(),new SubgraphStrategy(vertices:__.has(\"region\",\"US-TX\"),edges:__.hasLabel(\"route\"))).V().count()",
+			equals:                    "g.withStrategies(ReadOnlyStrategy,new SubgraphStrategy(vertices:__.has(\"region\",\"US-TX\"),edges:__.hasLabel(\"route\"))).V().count()",
 		},
 		{
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
 				return g.WithStrategies(ReadOnlyStrategy(), SubgraphStrategy(SubgraphStrategyConfig{Vertices: T__.Has("region", "US-TX")})).V().Count()
 			},
-			equals: "g.withStrategies(new ReadOnlyStrategy(),new SubgraphStrategy(vertices:__.has(\"region\",\"US-TX\"))).V().count()",
+			equals: "g.withStrategies(ReadOnlyStrategy,new SubgraphStrategy(vertices:__.has(\"region\",\"US-TX\"))).V().count()",
 		},
 		{
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
 				return g.WithStrategies(OptionsStrategy(map[string]interface{}{"evaluationTimeout": 500})).V().Count()
 			},
-			equals: "g.withStrategies(new OptionsStrategy(evaluationTimeout:500)).V().count()",
+			// OptionsStrategy are now extracted into request message and is no longer sent with the script
+			equals: "g.V().count()",
 		},
 		{
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
@@ -615,7 +615,7 @@ func Test_GremlinLang(t *testing.T) {
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
 				return g.WithStrategies(VertexProgramStrategy(VertexProgramStrategyConfig{})).V().ShortestPath().With("~tinkerpop.shortestPath.target", T__.Has("name", "peter"))
 			},
-			equals: "g.withStrategies(new VertexProgramStrategy()).V().shortestPath().with(\"~tinkerpop.shortestPath.target\",__.has(\"name\",\"peter\"))",
+			equals: "g.withStrategies(VertexProgramStrategy).V().shortestPath().with(\"~tinkerpop.shortestPath.target\",__.has(\"name\",\"peter\"))",
 		},
 		{
 			assert: func(g *GraphTraversalSource) *GraphTraversal {
@@ -671,8 +671,6 @@ func Test_GremlinLang(t *testing.T) {
 		t.Run(testName, func(t *testing.T) {
 			g := NewGraphTraversalSource(nil, nil)
 			gremlinLang := tt.assert(g).GremlinLang.GetGremlin()
-			fmt.Println("---gremlin lang???")
-			fmt.Println(gremlinLang)
 			if !tt.containsRandomClassParams && gremlinLang != tt.equals {
 				t.Errorf("GremlinLang = %v, equals %v", gremlinLang, tt.equals)
 			}
diff --git a/gremlin-go/driver/request.go b/gremlin-go/driver/request.go
index 31a57ef8cd..e64195adb5 100644
--- a/gremlin-go/driver/request.go
+++ b/gremlin-go/driver/request.go
@@ -157,7 +157,7 @@ func extractWithStrategiesReqArgs(insn instruction) map[string]interface{} {
 			continue
 		}
 
-		if strategy.name != decorationNamespace+"OptionsStrategy" {
+		if strategy.name != "OptionsStrategy" {
 			continue
 		}
 
diff --git a/gremlin-go/driver/strategies.go b/gremlin-go/driver/strategies.go
index d47636ee4c..ca53da00c9 100644
--- a/gremlin-go/driver/strategies.go
+++ b/gremlin-go/driver/strategies.go
@@ -19,15 +19,6 @@ under the License.
 
 package gremlingo
 
-const (
-	baseNamespace               = "org.apache.tinkerpop.gremlin.process.traversal.strategy."
-	decorationNamespace         = baseNamespace + "decoration."
-	finalizationNamespace       = baseNamespace + "finalization."
-	optimizationNamespace       = baseNamespace + "optimization."
-	verificationNamespace       = baseNamespace + "verification."
-	computerDecorationNamespace = "org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration."
-)
-
 type TraversalStrategy interface {
 }
 
@@ -37,12 +28,17 @@ type traversalStrategy struct {
 	apply         func(g GraphTraversal)
 }
 
+// NewTraversalStrategy creates a new strategy with custom name and config
+func NewTraversalStrategy(name string, configuration map[string]interface{}) TraversalStrategy {
+	return &traversalStrategy{name: name, configuration: configuration}
+}
+
 // Decoration strategies
 
 // ConnectiveStrategy rewrites the binary conjunction form of a.And().b into a AndStep of
 // And(a,b) (likewise for OrStep).
 func ConnectiveStrategy() TraversalStrategy {
-	return &traversalStrategy{name: decorationNamespace + "ConnectiveStrategy"}
+	return &traversalStrategy{name: "ConnectiveStrategy"}
 }
 
 // ElementIdStrategy provides a degree of control over element identifier assignment as some Graphs don't provide
@@ -50,7 +46,7 @@ func ConnectiveStrategy() TraversalStrategy {
 // under the hood, thus simulating that capability.
 // By default, when an identifier is not supplied by the user, newly generated identifiers are UUID objects.
 func ElementIdStrategy() TraversalStrategy {
-	return &traversalStrategy{name: decorationNamespace + "ElementIdStrategy"}
+	return &traversalStrategy{name: "ElementIdStrategy"}
 }
 
 func HaltedTraverserStrategy(config HaltedTraverserStrategyConfig) TraversalStrategy {
@@ -58,7 +54,7 @@ func HaltedTraverserStrategy(config HaltedTraverserStrategyConfig) TraversalStra
 	if config.HaltedTraverserFactoryName != "" {
 		configMap["haltedTraverserFactory"] = config.HaltedTraverserFactoryName
 	}
-	return &traversalStrategy{name: decorationNamespace + "HaltedTraverserStrategy", configuration: configMap}
+	return &traversalStrategy{name: "HaltedTraverserStrategy", configuration: configMap}
 }
 
 // HaltedTraverserStrategyConfig provides configuration options for HaltedTraverserStrategy.
@@ -72,7 +68,7 @@ type HaltedTraverserStrategyConfig struct {
 // essentially a way for users to provide Traversal level configuration options that can be used in various ways
 // by different Graph providers.
 func OptionsStrategy(options map[string]interface{}) TraversalStrategy {
-	return &traversalStrategy{name: decorationNamespace + "OptionsStrategy", configuration: options}
+	return &traversalStrategy{name: "OptionsStrategy", configuration: options}
 }
 
 // PartitionStrategy partitions the Vertices, Edges and Vertex properties of a Graph into String named
@@ -89,7 +85,7 @@ func PartitionStrategy(config PartitionStrategyConfig) TraversalStrategy {
 	if len(config.ReadPartitions.ToSlice()) != 0 {
 		configMap["readPartitions"] = config.ReadPartitions
 	}
-	return &traversalStrategy{name: decorationNamespace + "PartitionStrategy", configuration: configMap}
+	return &traversalStrategy{name: "PartitionStrategy", configuration: configMap}
 }
 
 // PartitionStrategyConfig provides configuration options for PartitionStrategy.
@@ -110,7 +106,7 @@ type PartitionStrategyConfig struct {
 // sense is to apply some form of order() in these cases.
 func SeedStrategy(config SeedStrategyConfig) TraversalStrategy {
 	configMap := map[string]interface{}{"seed": config.Seed}
-	return &traversalStrategy{name: decorationNamespace + "SeedStrategy", configuration: configMap}
+	return &traversalStrategy{name: "SeedStrategy", configuration: configMap}
 }
 
 // SeedStrategyConfig provides configuration options for SeedStrategy. Zeroed (unset) values are used.
@@ -135,7 +131,7 @@ func SubgraphStrategy(config SubgraphStrategyConfig) TraversalStrategy {
 	if config.CheckAdjacentVertices != nil {
 		configMap["checkAdjacentVertices"] = config.CheckAdjacentVertices.(bool)
 	}
-	return &traversalStrategy{name: decorationNamespace + "SubgraphStrategy", configuration: configMap}
+	return &traversalStrategy{name: "SubgraphStrategy", configuration: configMap}
 }
 
 // SubgraphStrategyConfig provides configuration options for SubgraphStrategy. Zeroed (unset) values are ignored.
@@ -169,7 +165,7 @@ func VertexProgramStrategy(config VertexProgramStrategyConfig) TraversalStrategy
 	for k, v := range config.Configuration {
 		configMap[k] = v
 	}
-	return &traversalStrategy{name: computerDecorationNamespace + "VertexProgramStrategy", configuration: configMap}
+	return &traversalStrategy{name: "VertexProgramStrategy", configuration: configMap}
 }
 
 // VertexProgramStrategyConfig provides configuration options for VertexProgramStrategy.
@@ -191,7 +187,7 @@ func MatchAlgorithmStrategy(config MatchAlgorithmStrategyConfig) TraversalStrate
 	if config.MatchAlgorithm != "" {
 		configMap["matchAlgorithm"] = config.MatchAlgorithm
 	}
-	return &traversalStrategy{name: finalizationNamespace + "MatchAlgorithmStrategy", configuration: configMap}
+	return &traversalStrategy{name: "MatchAlgorithmStrategy", configuration: configMap}
 }
 
 // MatchAlgorithmStrategyConfig provides configuration options for MatchAlgorithmStrategy.
@@ -211,7 +207,7 @@ func EdgeLabelVerificationStrategy(config EdgeLabelVerificationStrategyConfig) T
 		"throwException": config.ThrowExcecption,
 	}
 
-	return &traversalStrategy{name: verificationNamespace + "EdgeLabelVerificationStrategy", configuration: configMap}
+	return &traversalStrategy{name: "EdgeLabelVerificationStrategy", configuration: configMap}
 }
 
 // EdgeLabelVerificationStrategyConfig provides configuration options for EdgeLabelVerificationStrategy.
@@ -226,12 +222,12 @@ type EdgeLabelVerificationStrategyConfig struct {
 // about the traversal. This strategy is not activated by default. However, graph system providers may choose
 // to make this a default strategy in order to ensure their respective strategies are better able to operate.
 func LambdaRestrictionStrategy() TraversalStrategy {
-	return &traversalStrategy{name: verificationNamespace + "LambdaRestrictionStrategy"}
+	return &traversalStrategy{name: "LambdaRestrictionStrategy"}
 }
 
 // ReadOnlyStrategy detects steps marked with Mutating and returns an error if one is found.
 func ReadOnlyStrategy() TraversalStrategy {
-	return &traversalStrategy{name: verificationNamespace + "ReadOnlyStrategy"}
+	return &traversalStrategy{name: "ReadOnlyStrategy"}
 }
 
 // ReservedKeysVerificationStrategy detects property keys that should not be used by the traversal.
@@ -244,7 +240,7 @@ func ReservedKeysVerificationStrategy(config ReservedKeysVerificationStrategyCon
 	if len(config.Keys) != 0 {
 		configMap["keys"] = config.Keys
 	}
-	return &traversalStrategy{name: verificationNamespace + "ReservedKeysVerificationStrategy", configuration: configMap}
+	return &traversalStrategy{name: "ReservedKeysVerificationStrategy", configuration: configMap}
 }
 
 // ReservedKeysVerificationStrategyConfig provides configuration options for ReservedKeysVerificationStrategy.
@@ -265,20 +261,20 @@ type ReservedKeysVerificationStrategyConfig struct {
 // the Vertex on the other side of an Edge) can be satisfied by trips to incident Graph Elements (e.g. just the Edge
 // itself).
 func AdjacentToIncidentStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "AdjacentToIncidentStrategy"}
+	return &traversalStrategy{name: "AdjacentToIncidentStrategy"}
 }
 
 // ByModulatorOptimizationStrategy looks for standard traversals in By-modulators and replaces them with more
 // optimized traversals (e.g. TokenTraversal) if possible.
 func ByModulatorOptimizationStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "ByModulatorOptimizationStrategy"}
+	return &traversalStrategy{name: "ByModulatorOptimizationStrategy"}
 }
 
 // CountStrategy optimizes any occurrence of CountGlobalStep followed by an IsStep The idea is to limit
 // the number of incoming elements in a way that it's enough for the IsStep to decide whether it evaluates
 // true or false. If the traversal already contains a user supplied limit, the strategy won't modify it.
 func CountStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "CountStrategy"}
+	return &traversalStrategy{name: "CountStrategy"}
 }
 
 // EarlyLimitStrategy looks for RangeGlobalSteps that can be moved further left in the traversal and thus be applied
@@ -286,7 +282,7 @@ func CountStrategy() TraversalStrategy {
 // If the logical consequence of one or multiple RangeGlobalSteps is an empty result, the strategy will remove
 // as many steps as possible and add a NoneStep instead.
 func EarlyLimitStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "EarlyLimitStrategy"}
+	return &traversalStrategy{name: "EarlyLimitStrategy"}
 }
 
 // FilterRankingStrategy reorders filter- and order-steps according to their rank. Step ranks are defined within
@@ -294,14 +290,14 @@ func EarlyLimitStrategy() TraversalStrategy {
 // push step labels as far "right" as possible in order to keep Traversers as small and bulkable as possible prior to
 // the absolute need for Path-labeling.
 func FilterRankingStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "FilterRankingStrategy"}
+	return &traversalStrategy{name: "FilterRankingStrategy"}
 }
 
 // IdentityRemovalStrategy looks for IdentityStep instances and removes them.
 // If the identity step is labeled, its labels are added to the previous step.
 // If the identity step is labeled and it's the first step in the traversal, it stays.
 func IdentityRemovalStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "IdentityRemovalStrategy"}
+	return &traversalStrategy{name: "IdentityRemovalStrategy"}
 }
 
 // IncidentToAdjacentStrategy looks for .OutE().InV(), .InE().OutV() and .BothE().OtherV()
@@ -312,7 +308,7 @@ func IdentityRemovalStrategy() TraversalStrategy {
 //	the traversal contains a Path step
 //	the traversal contains a Lambda step
 func IncidentToAdjacentStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "IncidentToAdjacentStrategy"}
+	return &traversalStrategy{name: "IncidentToAdjacentStrategy"}
 }
 
 // InlineFilterStrategy analyzes filter-steps with child traversals that themselves are pure filters. If
@@ -321,7 +317,7 @@ func IncidentToAdjacentStrategy() TraversalStrategy {
 // a graph provider may need to reason about when writing their own strategies. As a result, this strategy helps
 // increase the likelihood that a provider's filtering optimization will succeed at re-writing the traversal.
 func InlineFilterStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "InlineFilterStrategy"}
+	return &traversalStrategy{name: "InlineFilterStrategy"}
 }
 
 // LazyBarrierStrategy is an OLTP-only strategy that automatically inserts a NoOpBarrierStep after every
@@ -329,21 +325,21 @@ func InlineFilterStrategy() TraversalStrategy {
 // traversal's last step or a barrier. NoOpBarrierSteps allow Traversers to be bulked, thus this strategy
 // is meant to reduce memory requirements and improve the overall query performance.
 func LazyBarrierStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "LazyBarrierStrategy"}
+	return &traversalStrategy{name: "LazyBarrierStrategy"}
 }
 
 // MatchPredicateStrategy will fold any post-Where() step that maintains a traversal constraint into
 // Match(). MatchStep is intelligent with traversal constraint applications and thus, can more
 // efficiently use the constraint of WhereTraversalStep or WherePredicateStep.
 func MatchPredicateStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "MatchPredicateStrategy"}
+	return &traversalStrategy{name: "MatchPredicateStrategy"}
 }
 
 // OrderLimitStrategy is an OLAP strategy that folds a RangeGlobalStep into a preceding
 // OrderGlobalStep. This helps to eliminate traversers early in the traversal and can
 // significantly reduce the amount of memory required by the OLAP execution engine.
 func OrderLimitStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "OrderLimitStrategy"}
+	return &traversalStrategy{name: "OrderLimitStrategy"}
 }
 
 // PathProcessorStrategy  is an OLAP strategy that does its best to turn non-local children in Where()
@@ -351,13 +347,13 @@ func OrderLimitStrategy() TraversalStrategy {
 // PathProcessorStrategy helps to ensure that more traversals meet the local child constraint imposed
 // on OLAP traversals.
 func PathProcessorStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "PathProcessorStrategy"}
+	return &traversalStrategy{name: "PathProcessorStrategy"}
 }
 
 // PathRetractionStrategy will remove Paths from the Traversers and increase the likelihood of bulking
 // as Path data is not required after Select('b').
 func PathRetractionStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "PathRetractionStrategy"}
+	return &traversalStrategy{name: "PathRetractionStrategy"}
 }
 
 // ProductiveByStrategy takes an argument of By() and wraps it CoalesceStep so that the result is either
@@ -370,7 +366,7 @@ func ProductiveByStrategy(config ...ProductiveByStrategyConfig) TraversalStrateg
 		configMap["productiveKeys"] = config[0].ProductiveKeys
 	}
 
-	return &traversalStrategy{name: optimizationNamespace + "ProductiveByStrategy", configuration: configMap}
+	return &traversalStrategy{name: "ProductiveByStrategy", configuration: configMap}
 }
 
 // ProductiveByStrategyConfig provides configuration options for ProductiveByStrategy.
@@ -387,7 +383,7 @@ type ProductiveByStrategyConfig struct {
 //	LoopsStep
 //	LambdaHolder
 func RepeatUnrollStrategy() TraversalStrategy {
-	return &traversalStrategy{name: optimizationNamespace + "RepeatUnrollStrategy"}
+	return &traversalStrategy{name: "RepeatUnrollStrategy"}
 }
 
 // RemoteStrategy reconstructs a Traversal by appending a RemoteStep to its end. That step will submit the Traversal to
diff --git a/gremlin-go/driver/strategies_test.go b/gremlin-go/driver/strategies_test.go
index 3323a78954..3649eedb5a 100644
--- a/gremlin-go/driver/strategies_test.go
+++ b/gremlin-go/driver/strategies_test.go
@@ -21,6 +21,7 @@ package gremlingo
 
 import (
 	"crypto/tls"
+	"strings"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -120,19 +121,12 @@ func TestStrategy(t *testing.T) {
 		assert.Equal(t, int32(0), val)
 	})
 
-	t.Run("Test Bytecode generation for MatchAlgorithmStrategy", func(t *testing.T) {
-		g := getModernGraph(t, testNoAuthUrl, &AuthInfo{}, &tls.Config{})
-		defer g.remoteConnection.Close()
+	t.Run("Test GremlinLang generation for MatchAlgorithmStrategy", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
 
 		config := MatchAlgorithmStrategyConfig{MatchAlgorithm: "greedy"}
-		bytecode := g.WithStrategies(MatchAlgorithmStrategy(config)).bytecode
-		assert.Equal(t, 1, len(bytecode.sourceInstructions))
-		assert.Equal(t, 1, len(bytecode.sourceInstructions[0].arguments))
-		assert.Equal(t, "withStrategies", bytecode.sourceInstructions[0].operator)
-		assert.Equal(t, "org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy",
-			bytecode.sourceInstructions[0].arguments[0].(*traversalStrategy).name)
-		assert.Equal(t, map[string]interface{}{"matchAlgorithm": "greedy"},
-			bytecode.sourceInstructions[0].arguments[0].(*traversalStrategy).configuration)
+		gl := g.WithStrategies(MatchAlgorithmStrategy(config)).gremlinLang
+		assert.True(t, strings.Contains(gl.gremlin.String(), "withStrategies(new MatchAlgorithmStrategy(matchAlgorithm:\"greedy\"))"))
 	})
 
 	t.Run("Test read with AdjacentToIncidentStrategy", func(t *testing.T) {
@@ -413,4 +407,22 @@ func TestStrategy(t *testing.T) {
 		assert.Nil(t, err)
 		assert.Equal(t, int32(6), val)
 	})
+
+	t.Run("Test GremlinLang generation for simple custom strategies", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+
+		customStrategy := NewTraversalStrategy("CustomSingletonStrategy", nil)
+		gl := g.WithStrategies(customStrategy).gremlinLang
+		assert.True(t, strings.Contains(gl.gremlin.String(), "withStrategies(CustomSingletonStrategy)"))
+	})
+
+	t.Run("Test GremlinLang generation for config custom strategies", func(t *testing.T) {
+		g := NewGraphTraversalSource(nil, nil)
+
+		customStrategy := NewTraversalStrategy("CustomConfigurableStrategy",
+			map[string]interface{}{"stringKey": "string value", "intKey": 5, "booleanKey": true})
+		gl := g.WithStrategies(customStrategy).gremlinLang
+		assert.True(t, strings.Contains(gl.gremlin.String(),
+			"withStrategies(new CustomConfigurableStrategy(stringKey:\"string value\",intKey:5,booleanKey:true))"))
+	})
 }
diff --git a/gremlin-go/driver/traversal_test.go b/gremlin-go/driver/traversal_test.go
index 21cc75af1e..ad6491f6e2 100644
--- a/gremlin-go/driver/traversal_test.go
+++ b/gremlin-go/driver/traversal_test.go
@@ -31,7 +31,7 @@ import (
 func TestTraversal(t *testing.T) {
 
 	t.Run("Test clone traversal", func(t *testing.T) {
-		g := cloneGraphTraversalSource(&Graph{}, NewBytecode(nil), NewGremlinLang(nil), nil)
+		g := cloneGraphTraversalSource(&Graph{}, NewGremlinLang(nil), nil)
 		original := g.V().Out("created")
 		clone := original.Clone().Out("knows")
 		cloneClone := clone.Clone().Out("created")
@@ -56,7 +56,7 @@ func TestTraversal(t *testing.T) {
 	})
 
 	t.Run("Test traversal with bindings", func(t *testing.T) {
-		g := cloneGraphTraversalSource(&Graph{}, NewBytecode(nil), NewGremlinLang(nil), nil)
+		g := cloneGraphTraversalSource(&Graph{}, NewGremlinLang(nil), nil)
 		bytecode := g.V((&Bindings{}).Of("a", []int32{1, 2, 3})).
 			Out((&Bindings{}).Of("b", "created")).
 			Where(T__.In((&Bindings{}).Of("c", "created"), (&Bindings{}).Of("d", "knows")).