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 22:29:22 UTC
(tinkerpop) branch go-opts-strat-gl-updates updated (97a6b9d663 -> 0877458a9c)
This is an automated email from the ASF dual-hosted git repository.
xiazcy pushed a change to branch go-opts-strat-gl-updates
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
discard 97a6b9d663 Updated datetime, added GValue, updated Options strategy, removed unneeded strategy package name, added NewTraversalStrategy to allow custom strategies.
add b3de4474a2 revert map to not support ordered map derser, updated set, added marker class
add ee2a512426 clean up go mod
new 0877458a9c Updated datetime, added GValue, updated Options strategy, removed unneeded strategy package name, added NewTraversalStrategy to allow custom strategies.
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (97a6b9d663)
\
N -- N -- N refs/heads/go-opts-strat-gl-updates (0877458a9c)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
gremlin-go/driver/graphBinary.go | 152 ++++++++-------------
gremlin-go/driver/graphBinary_test.go | 34 +++--
.../driver/{resources/resources.go => marker.go} | 32 +++--
gremlin-go/driver/serializer.go | 9 +-
gremlin-go/go.mod | 4 -
gremlin-go/go.sum | 9 --
6 files changed, 104 insertions(+), 136 deletions(-)
copy gremlin-go/driver/{resources/resources.go => marker.go} (66%)
(tinkerpop) 01/01: Updated datetime, added GValue, updated Options strategy, removed unneeded strategy package name, added NewTraversalStrategy to allow custom strategies.
Posted by xi...@apache.org.
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 0877458a9c3b931a3e828e0436b650cbbd8ebb8e
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")).