From 8df21cd8d93282083bcf54311d5dec2c55fb1a8b Mon Sep 17 00:00:00 2001 From: kortschak Date: Tue, 26 Aug 2014 10:12:44 +0930 Subject: [PATCH] Refactor work out into worker type --- query/gremlin/build_iterator.go | 4 +- query/gremlin/environ.go | 112 ++++++++++++++++++----------- query/gremlin/finals.go | 154 +++++++++++++++++++++++----------------- query/gremlin/session.go | 105 ++++++++++----------------- query/gremlin/traversals.go | 52 +++++++------- 5 files changed, 224 insertions(+), 203 deletions(-) diff --git a/query/gremlin/build_iterator.go b/query/gremlin/build_iterator.go index c2a9108..02196f4 100644 --- a/query/gremlin/build_iterator.go +++ b/query/gremlin/build_iterator.go @@ -46,8 +46,8 @@ func stringsFrom(obj *otto.Object) []string { lengthValue, _ := obj.Get("length") length, _ := lengthValue.ToInteger() ulength := uint32(length) - for index := uint32(0); index < ulength; index += 1 { - name := strconv.FormatInt(int64(index), 10) + for i := uint32(0); i < ulength; i++ { + name := strconv.FormatInt(int64(i), 10) value, err := obj.Get(name) if err != nil || !value.IsString() { continue diff --git a/query/gremlin/environ.go b/query/gremlin/environ.go index cdbbdb4..6402bab 100644 --- a/query/gremlin/environ.go +++ b/query/gremlin/environ.go @@ -17,10 +17,80 @@ package gremlin // Builds a new Gremlin environment pointing at a session. import ( + "sync" + "github.com/barakmich/glog" "github.com/robertkrimen/otto" + + "github.com/google/cayley/graph" ) +type worker struct { + ts graph.TripleStore + env *otto.Otto + envLock sync.Mutex + + results chan interface{} + shape map[string]interface{} + + count int + limit int + + kill chan struct{} +} + +func newWorker(ts graph.TripleStore) *worker { + env := otto.New() + wk := &worker{ + ts: ts, + env: env, + limit: -1, + } + graph, _ := env.Object("graph = {}") + env.Run("g = graph") + + graph.Set("Vertex", func(call otto.FunctionCall) otto.Value { + call.Otto.Run("var out = {}") + out, err := call.Otto.Object("out") + if err != nil { + glog.Error(err.Error()) + return otto.TrueValue() + } + out.Set("_gremlin_type", "vertex") + args := argsOf(call) + if len(args) > 0 { + out.Set("string_args", args) + } + wk.embedTraversals(env, out) + wk.embedFinals(env, out) + return out.Value() + }) + env.Run("graph.V = graph.Vertex") + + graph.Set("Morphism", func(call otto.FunctionCall) otto.Value { + call.Otto.Run("var out = {}") + out, _ := call.Otto.Object("out") + out.Set("_gremlin_type", "morphism") + wk.embedTraversals(env, out) + return out.Value() + }) + env.Run("graph.M = graph.Morphism") + + graph.Set("Emit", func(call otto.FunctionCall) otto.Value { + value := call.Argument(0) + if value.IsDefined() { + wk.send(&Result{val: &value}) + } + return otto.NullValue() + }) + + return wk +} + +func (wk *worker) wantShape() bool { + return wk.shape != nil +} + func argsOf(call otto.FunctionCall) []string { var out []string for _, arg := range call.ArgumentList { @@ -48,45 +118,3 @@ func isVertexChain(obj *otto.Object) bool { } return false } - -func (s *Session) setup(env *otto.Otto) *otto.Otto { - graph, _ := env.Object("graph = {}") - env.Run("g = graph") - - graph.Set("Vertex", func(call otto.FunctionCall) otto.Value { - call.Otto.Run("var out = {}") - out, err := call.Otto.Object("out") - if err != nil { - glog.Error(err.Error()) - return otto.TrueValue() - } - out.Set("_gremlin_type", "vertex") - args := argsOf(call) - if len(args) > 0 { - out.Set("string_args", args) - } - s.embedTraversals(env, out) - s.embedFinals(env, out) - return out.Value() - }) - env.Run("graph.V = graph.Vertex") - - graph.Set("Morphism", func(call otto.FunctionCall) otto.Value { - call.Otto.Run("var out = {}") - out, _ := call.Otto.Object("out") - out.Set("_gremlin_type", "morphism") - s.embedTraversals(env, out) - return out.Value() - }) - env.Run("graph.M = graph.Morphism") - - graph.Set("Emit", func(call otto.FunctionCall) otto.Value { - value := call.Argument(0) - if value.IsDefined() { - s.SendResult(&Result{val: &value}) - } - return otto.NullValue() - }) - - return env -} diff --git a/query/gremlin/finals.go b/query/gremlin/finals.go index 1e9f6ed..c1d7ca7 100644 --- a/query/gremlin/finals.go +++ b/query/gremlin/finals.go @@ -26,45 +26,45 @@ import ( const TopResultTag = "id" -func (s *Session) embedFinals(env *otto.Otto, obj *otto.Object) { - obj.Set("All", s.allFunc(env, obj)) - obj.Set("GetLimit", s.limitFunc(env, obj)) - obj.Set("ToArray", s.toArrayFunc(env, obj, false)) - obj.Set("ToValue", s.toValueFunc(env, obj, false)) - obj.Set("TagArray", s.toArrayFunc(env, obj, true)) - obj.Set("TagValue", s.toValueFunc(env, obj, true)) - obj.Set("Map", s.mapFunc(env, obj)) - obj.Set("ForEach", s.mapFunc(env, obj)) +func (wk *worker) embedFinals(env *otto.Otto, obj *otto.Object) { + obj.Set("All", wk.allFunc(env, obj)) + obj.Set("GetLimit", wk.limitFunc(env, obj)) + obj.Set("ToArray", wk.toArrayFunc(env, obj, false)) + obj.Set("ToValue", wk.toValueFunc(env, obj, false)) + obj.Set("TagArray", wk.toArrayFunc(env, obj, true)) + obj.Set("TagValue", wk.toValueFunc(env, obj, true)) + obj.Set("Map", wk.mapFunc(env, obj)) + obj.Set("ForEach", wk.mapFunc(env, obj)) } -func (s *Session) allFunc(env *otto.Otto, obj *otto.Object) func(otto.FunctionCall) otto.Value { +func (wk *worker) allFunc(env *otto.Otto, obj *otto.Object) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { - it := buildIteratorTree(obj, s.ts) + it := buildIteratorTree(obj, wk.ts) it.Tagger().Add(TopResultTag) - s.limit = -1 - s.count = 0 - s.runIterator(it) + wk.limit = -1 + wk.count = 0 + wk.runIterator(it) return otto.NullValue() } } -func (s *Session) limitFunc(env *otto.Otto, obj *otto.Object) func(otto.FunctionCall) otto.Value { +func (wk *worker) limitFunc(env *otto.Otto, obj *otto.Object) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { if len(call.ArgumentList) > 0 { limitVal, _ := call.Argument(0).ToInteger() - it := buildIteratorTree(obj, s.ts) + it := buildIteratorTree(obj, wk.ts) it.Tagger().Add(TopResultTag) - s.limit = int(limitVal) - s.count = 0 - s.runIterator(it) + wk.limit = int(limitVal) + wk.count = 0 + wk.runIterator(it) } return otto.NullValue() } } -func (s *Session) toArrayFunc(env *otto.Otto, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value { +func (wk *worker) toArrayFunc(env *otto.Otto, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { - it := buildIteratorTree(obj, s.ts) + it := buildIteratorTree(obj, wk.ts) it.Tagger().Add(TopResultTag) limit := -1 if len(call.ArgumentList) > 0 { @@ -74,10 +74,10 @@ func (s *Session) toArrayFunc(env *otto.Otto, obj *otto.Object, withTags bool) f var val otto.Value var err error if !withTags { - array := s.runIteratorToArrayNoTags(it, limit) + array := wk.runIteratorToArrayNoTags(it, limit) val, err = call.Otto.ToValue(array) } else { - array := s.runIteratorToArray(it, limit) + array := wk.runIteratorToArray(it, limit) val, err = call.Otto.ToValue(array) } @@ -89,21 +89,21 @@ func (s *Session) toArrayFunc(env *otto.Otto, obj *otto.Object, withTags bool) f } } -func (s *Session) toValueFunc(env *otto.Otto, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value { +func (wk *worker) toValueFunc(env *otto.Otto, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { - it := buildIteratorTree(obj, s.ts) + it := buildIteratorTree(obj, wk.ts) it.Tagger().Add(TopResultTag) limit := 1 var val otto.Value var err error if !withTags { - array := s.runIteratorToArrayNoTags(it, limit) + array := wk.runIteratorToArrayNoTags(it, limit) if len(array) < 1 { return otto.NullValue() } val, err = call.Otto.ToValue(array[0]) } else { - array := s.runIteratorToArray(it, limit) + array := wk.runIteratorToArray(it, limit) if len(array) < 1 { return otto.NullValue() } @@ -119,9 +119,9 @@ func (s *Session) toValueFunc(env *otto.Otto, obj *otto.Object, withTags bool) f } } -func (s *Session) mapFunc(env *otto.Otto, obj *otto.Object) func(otto.FunctionCall) otto.Value { +func (wk *worker) mapFunc(env *otto.Otto, obj *otto.Object) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { - it := buildIteratorTree(obj, s.ts) + it := buildIteratorTree(obj, wk.ts) it.Tagger().Add(TopResultTag) limit := -1 if len(call.ArgumentList) == 0 { @@ -132,26 +132,26 @@ func (s *Session) mapFunc(env *otto.Otto, obj *otto.Object) func(otto.FunctionCa limitParsed, _ := call.Argument(0).ToInteger() limit = int(limitParsed) } - s.runIteratorWithCallback(it, callback, call, limit) + wk.runIteratorWithCallback(it, callback, call, limit) return otto.NullValue() } } -func (s *Session) tagsToValueMap(m map[string]graph.Value) map[string]string { +func (wk *worker) tagsToValueMap(m map[string]graph.Value) map[string]string { outputMap := make(map[string]string) for k, v := range m { - outputMap[k] = s.ts.NameOf(v) + outputMap[k] = wk.ts.NameOf(v) } return outputMap } -func (s *Session) runIteratorToArray(it graph.Iterator, limit int) []map[string]string { +func (wk *worker) runIteratorToArray(it graph.Iterator, limit int) []map[string]string { output := make([]map[string]string, 0) - count := 0 + n := 0 it, _ = it.Optimize() for { select { - case <-s.kill: + case <-wk.kill: return nil default: } @@ -160,22 +160,22 @@ func (s *Session) runIteratorToArray(it graph.Iterator, limit int) []map[string] } tags := make(map[string]graph.Value) it.TagResults(tags) - output = append(output, s.tagsToValueMap(tags)) - count++ - if limit >= 0 && count >= limit { + output = append(output, wk.tagsToValueMap(tags)) + n++ + if limit >= 0 && n >= limit { break } for it.NextPath() { select { - case <-s.kill: + case <-wk.kill: return nil default: } tags := make(map[string]graph.Value) it.TagResults(tags) - output = append(output, s.tagsToValueMap(tags)) - count++ - if limit >= 0 && count >= limit { + output = append(output, wk.tagsToValueMap(tags)) + n++ + if limit >= 0 && n >= limit { break } } @@ -184,22 +184,22 @@ func (s *Session) runIteratorToArray(it graph.Iterator, limit int) []map[string] return output } -func (s *Session) runIteratorToArrayNoTags(it graph.Iterator, limit int) []string { +func (wk *worker) runIteratorToArrayNoTags(it graph.Iterator, limit int) []string { output := make([]string, 0) - count := 0 + n := 0 it, _ = it.Optimize() for { select { - case <-s.kill: + case <-wk.kill: return nil default: } if !graph.Next(it) { break } - output = append(output, s.ts.NameOf(it.Result())) - count++ - if limit >= 0 && count >= limit { + output = append(output, wk.ts.NameOf(it.Result())) + n++ + if limit >= 0 && n >= limit { break } } @@ -207,13 +207,13 @@ func (s *Session) runIteratorToArrayNoTags(it graph.Iterator, limit int) []strin return output } -func (s *Session) runIteratorWithCallback(it graph.Iterator, callback otto.Value, this otto.FunctionCall, limit int) { - count := 0 +func (wk *worker) runIteratorWithCallback(it graph.Iterator, callback otto.Value, this otto.FunctionCall, limit int) { + n := 0 it, _ = it.Optimize() glog.V(2).Infoln(it.DebugString(0)) for { select { - case <-s.kill: + case <-wk.kill: return default: } @@ -222,24 +222,24 @@ func (s *Session) runIteratorWithCallback(it graph.Iterator, callback otto.Value } tags := make(map[string]graph.Value) it.TagResults(tags) - val, _ := this.Otto.ToValue(s.tagsToValueMap(tags)) + val, _ := this.Otto.ToValue(wk.tagsToValueMap(tags)) val, _ = callback.Call(this.This, val) - count++ - if limit >= 0 && count >= limit { + n++ + if limit >= 0 && n >= limit { break } for it.NextPath() { select { - case <-s.kill: + case <-wk.kill: return default: } tags := make(map[string]graph.Value) it.TagResults(tags) - val, _ := this.Otto.ToValue(s.tagsToValueMap(tags)) + val, _ := this.Otto.ToValue(wk.tagsToValueMap(tags)) val, _ = callback.Call(this.This, val) - count++ - if limit >= 0 && count >= limit { + n++ + if limit >= 0 && n >= limit { break } } @@ -247,16 +247,40 @@ func (s *Session) runIteratorWithCallback(it graph.Iterator, callback otto.Value it.Close() } -func (s *Session) runIterator(it graph.Iterator) { - if s.wantShape { - iterator.OutputQueryShapeForIterator(it, s.ts, s.shape) +func (wk *worker) send(r *Result) bool { + if wk.limit >= 0 && wk.limit == wk.count { + return false + } + wk.envLock.Lock() + kill := wk.kill + wk.envLock.Unlock() + select { + case <-kill: + return false + default: + } + if wk.results != nil { + wk.results <- r + wk.count++ + if wk.limit >= 0 && wk.limit == wk.count { + return false + } else { + return true + } + } + return false +} + +func (wk *worker) runIterator(it graph.Iterator) { + if wk.wantShape() { + iterator.OutputQueryShapeForIterator(it, wk.ts, wk.shape) return } it, _ = it.Optimize() glog.V(2).Infoln(it.DebugString(0)) for { select { - case <-s.kill: + case <-wk.kill: return default: } @@ -265,18 +289,18 @@ func (s *Session) runIterator(it graph.Iterator) { } tags := make(map[string]graph.Value) it.TagResults(tags) - if !s.SendResult(&Result{actualResults: &tags}) { + if !wk.send(&Result{actualResults: &tags}) { break } for it.NextPath() { select { - case <-s.kill: + case <-wk.kill: return default: } tags := make(map[string]graph.Value) it.TagResults(tags) - if !s.SendResult(&Result{actualResults: &tags}) { + if !wk.send(&Result{actualResults: &tags}) { break } } diff --git a/query/gremlin/session.go b/query/gremlin/session.go index 30cd913..beac46f 100644 --- a/query/gremlin/session.go +++ b/query/gremlin/session.go @@ -18,7 +18,6 @@ import ( "errors" "fmt" "sort" - "sync" "time" "github.com/robertkrimen/otto" @@ -31,32 +30,27 @@ import ( var ErrKillTimeout = errors.New("query timed out") type Session struct { - ts graph.TripleStore - results chan interface{} - env *otto.Otto - envLock sync.Mutex + ts graph.TripleStore + + wk *worker + timeout time.Duration + script *otto.Script + persist *otto.Otto + debug bool - limit int - count int dataOutput []interface{} - wantShape bool - shape map[string]interface{} - err error - script *otto.Script - kill chan struct{} - timeout time.Duration - emptyEnv *otto.Otto + + err error } func NewSession(ts graph.TripleStore, timeout time.Duration, persist bool) *Session { g := Session{ ts: ts, - limit: -1, + wk: newWorker(ts), timeout: timeout, } - g.env = g.setup(otto.New()) if persist { - g.emptyEnv = g.env + g.persist = g.wk.env } return &g } @@ -74,15 +68,14 @@ func (s *Session) ToggleDebug() { func (s *Session) GetQuery(input string, out chan map[string]interface{}) { defer close(out) - s.shape = make(map[string]interface{}) - s.wantShape = true - s.env.Run(input) - out <- s.shape - s.shape = nil + s.wk.shape = make(map[string]interface{}) + s.wk.env.Run(input) + out <- s.wk.shape + s.wk.shape = nil } func (s *Session) InputParses(input string) (query.ParseResult, error) { - script, err := s.env.Compile("", input) + script, err := s.wk.env.Compile("", input) if err != nil { return query.ParseFail, err } @@ -90,30 +83,6 @@ func (s *Session) InputParses(input string) (query.ParseResult, error) { return query.Parsed, nil } -func (s *Session) SendResult(r *Result) bool { - if s.limit >= 0 && s.limit == s.count { - return false - } - s.envLock.Lock() - kill := s.kill - s.envLock.Unlock() - select { - case <-kill: - return false - default: - } - if s.results != nil { - s.results <- r - s.count++ - if s.limit >= 0 && s.limit == s.count { - return false - } else { - return true - } - } - return false -} - func (s *Session) runUnsafe(input interface{}) (otto.Value, error) { defer func() { if r := recover(); r != nil { @@ -126,7 +95,7 @@ func (s *Session) runUnsafe(input interface{}) (otto.Value, error) { }() // Use buffered chan to prevent blocking. - s.env.Interrupt = make(chan func(), 1) + s.wk.env.Interrupt = make(chan func(), 1) ready := make(chan struct{}) done := make(chan struct{}) @@ -138,27 +107,27 @@ func (s *Session) runUnsafe(input interface{}) (otto.Value, error) { case <-done: return default: - close(s.kill) - s.envLock.Lock() - defer s.envLock.Unlock() - s.kill = nil - if s.env != nil { - s.env.Interrupt <- func() { + close(s.wk.kill) + s.wk.envLock.Lock() + defer s.wk.envLock.Unlock() + s.wk.kill = nil + if s.wk.env != nil { + s.wk.env.Interrupt <- func() { panic(ErrKillTimeout) } - s.env = s.emptyEnv + s.wk.env = s.persist } return } }() } - s.envLock.Lock() - env := s.env - if s.kill == nil { - s.kill = make(chan struct{}) + s.wk.envLock.Lock() + env := s.wk.env + if s.wk.kill == nil { + s.wk.kill = make(chan struct{}) } - s.envLock.Unlock() + s.wk.envLock.Unlock() close(ready) out, err := env.Run(input) close(done) @@ -168,7 +137,7 @@ func (s *Session) runUnsafe(input interface{}) (otto.Value, error) { func (s *Session) ExecInput(input string, out chan interface{}, limit int) { defer close(out) s.err = nil - s.results = out + s.wk.results = out var err error var value otto.Value if s.script == nil { @@ -181,11 +150,11 @@ func (s *Session) ExecInput(input string, out chan interface{}, limit int) { err: err, val: &value, } - s.results = nil + s.wk.results = nil s.script = nil - s.envLock.Lock() - s.env = s.emptyEnv - s.envLock.Unlock() + s.wk.envLock.Lock() + s.wk.env = s.persist + s.wk.envLock.Unlock() } func (s *Session) ToText(result interface{}) string { @@ -273,9 +242,9 @@ func (s *Session) GetJson() ([]interface{}, error) { if s.err != nil { return nil, s.err } - s.envLock.Lock() - kill := s.kill - s.envLock.Unlock() + s.wk.envLock.Lock() + kill := s.wk.kill + s.wk.envLock.Unlock() select { case <-kill: return nil, ErrKillTimeout diff --git a/query/gremlin/traversals.go b/query/gremlin/traversals.go index ff258dc..a013b01 100644 --- a/query/gremlin/traversals.go +++ b/query/gremlin/traversals.go @@ -21,26 +21,26 @@ import ( "github.com/robertkrimen/otto" ) -func (s *Session) embedTraversals(env *otto.Otto, obj *otto.Object) { - obj.Set("In", gremlinFunc("in", obj, env, s)) - obj.Set("Out", gremlinFunc("out", obj, env, s)) - obj.Set("Is", gremlinFunc("is", obj, env, s)) - obj.Set("Both", gremlinFunc("both", obj, env, s)) - obj.Set("Follow", gremlinFunc("follow", obj, env, s)) - obj.Set("FollowR", gremlinFollowR("followr", obj, env, s)) - obj.Set("And", gremlinFunc("and", obj, env, s)) - obj.Set("Intersect", gremlinFunc("and", obj, env, s)) - obj.Set("Union", gremlinFunc("or", obj, env, s)) - obj.Set("Or", gremlinFunc("or", obj, env, s)) - obj.Set("Back", gremlinBack("back", obj, env, s)) - obj.Set("Tag", gremlinFunc("tag", obj, env, s)) - obj.Set("As", gremlinFunc("tag", obj, env, s)) - obj.Set("Has", gremlinFunc("has", obj, env, s)) - obj.Set("Save", gremlinFunc("save", obj, env, s)) - obj.Set("SaveR", gremlinFunc("saver", obj, env, s)) +func (wk *worker) embedTraversals(env *otto.Otto, obj *otto.Object) { + obj.Set("In", wk.gremlinFunc("in", obj, env)) + obj.Set("Out", wk.gremlinFunc("out", obj, env)) + obj.Set("Is", wk.gremlinFunc("is", obj, env)) + obj.Set("Both", wk.gremlinFunc("both", obj, env)) + obj.Set("Follow", wk.gremlinFunc("follow", obj, env)) + obj.Set("FollowR", wk.gremlinFollowR("followr", obj, env)) + obj.Set("And", wk.gremlinFunc("and", obj, env)) + obj.Set("Intersect", wk.gremlinFunc("and", obj, env)) + obj.Set("Union", wk.gremlinFunc("or", obj, env)) + obj.Set("Or", wk.gremlinFunc("or", obj, env)) + obj.Set("Back", wk.gremlinBack("back", obj, env)) + obj.Set("Tag", wk.gremlinFunc("tag", obj, env)) + obj.Set("As", wk.gremlinFunc("tag", obj, env)) + obj.Set("Has", wk.gremlinFunc("has", obj, env)) + obj.Set("Save", wk.gremlinFunc("save", obj, env)) + obj.Set("SaveR", wk.gremlinFunc("saver", obj, env)) } -func gremlinFunc(kind string, prev *otto.Object, env *otto.Otto, ses *Session) func(otto.FunctionCall) otto.Value { +func (wk *worker) gremlinFunc(kind string, prev *otto.Object, env *otto.Otto) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { call.Otto.Run("var out = {}") out, _ := call.Otto.Object("out") @@ -51,15 +51,15 @@ func gremlinFunc(kind string, prev *otto.Object, env *otto.Otto, ses *Session) f if len(args) > 0 { out.Set("string_args", args) } - ses.embedTraversals(env, out) + wk.embedTraversals(env, out) if isVertexChain(call.This.Object()) { - ses.embedFinals(env, out) + wk.embedFinals(env, out) } return out.Value() } } -func gremlinBack(kind string, prev *otto.Object, env *otto.Otto, ses *Session) func(otto.FunctionCall) otto.Value { +func (wk *worker) gremlinBack(kind string, prev *otto.Object, env *otto.Otto) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { call.Otto.Run("var out = {}") out, _ := call.Otto.Object("out") @@ -78,16 +78,16 @@ func gremlinBack(kind string, prev *otto.Object, env *otto.Otto, ses *Session) f } out.Set("_gremlin_prev", thisObj) out.Set("_gremlin_back_chain", otherChain) - ses.embedTraversals(env, out) + wk.embedTraversals(env, out) if isVertexChain(call.This.Object()) { - ses.embedFinals(env, out) + wk.embedFinals(env, out) } return out.Value() } } -func gremlinFollowR(kind string, prev *otto.Object, env *otto.Otto, ses *Session) func(otto.FunctionCall) otto.Value { +func (wk *worker) gremlinFollowR(kind string, prev *otto.Object, env *otto.Otto) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { call.Otto.Run("var out = {}") out, _ := call.Otto.Object("out") @@ -107,9 +107,9 @@ func gremlinFollowR(kind string, prev *otto.Object, env *otto.Otto, ses *Session newChain, _ := reverseGremlinChainTo(call.Otto, arg.Object(), "") out.Set("_gremlin_prev", prev) out.Set("_gremlin_followr", newChain) - ses.embedTraversals(env, out) + wk.embedTraversals(env, out) if isVertexChain(call.This.Object()) { - ses.embedFinals(env, out) + wk.embedFinals(env, out) } return out.Value() }