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")).