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 <[email protected]>
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")).


Reply via email to