First implementations of loop and not operator for Cayley Gremlin.
This commit is contained in:
parent
1e62aaf374
commit
d3bc8c1736
6 changed files with 524 additions and 0 deletions
|
|
@ -206,6 +206,7 @@ const (
|
||||||
Not
|
Not
|
||||||
Optional
|
Optional
|
||||||
Materialize
|
Materialize
|
||||||
|
Loop
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -227,6 +228,7 @@ var (
|
||||||
"not",
|
"not",
|
||||||
"optional",
|
"optional",
|
||||||
"materialize",
|
"materialize",
|
||||||
|
"loop",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
91
graph/iterator/entrypoint_iterator.go
Normal file
91
graph/iterator/entrypoint_iterator.go
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
package iterator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/cayley/graph"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EntryPoint struct {
|
||||||
|
uid uint64
|
||||||
|
primaryIt graph.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEntryPoint(it graph.Iterator) *EntryPoint {
|
||||||
|
return &EntryPoint{
|
||||||
|
uid: NextUID(),
|
||||||
|
primaryIt: it,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) UID() uint64 {
|
||||||
|
return it.uid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Tagger() *graph.Tagger {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) TagResults(dst map[string]graph.Value) {
|
||||||
|
it.primaryIt.TagResults(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Contains(val graph.Value) bool {
|
||||||
|
return it.primaryIt.Contains(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Clone() graph.Iterator {
|
||||||
|
return NewEntryPoint(it.primaryIt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Type() graph.Type { return it.primaryIt.Type() }
|
||||||
|
|
||||||
|
func (it *EntryPoint) Reset() {
|
||||||
|
it.primaryIt.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) SetIterator(iterator graph.Iterator) {
|
||||||
|
it.primaryIt = iterator
|
||||||
|
it.primaryIt.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Next() bool {
|
||||||
|
return graph.Next(it.primaryIt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Result() graph.Value {
|
||||||
|
return it.primaryIt.Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) NextPath() bool {
|
||||||
|
return it.primaryIt.NextPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Stats() graph.IteratorStats {
|
||||||
|
return it.primaryIt.Stats()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Size() (int64, bool) {
|
||||||
|
return it.Stats().Size, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Optimize() (graph.Iterator, bool) {
|
||||||
|
return it, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) SubIterators() []graph.Iterator {
|
||||||
|
return []graph.Iterator{it.primaryIt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) DebugString(indent int) string {
|
||||||
|
return fmt.Sprintf("todo")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *EntryPoint) Close() {
|
||||||
|
it.primaryIt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEPRECATED
|
||||||
|
func (it *EntryPoint) ResultTree() *graph.ResultTree {
|
||||||
|
return graph.NewResultTree(it.Result())
|
||||||
|
}
|
||||||
218
graph/iterator/loop_iterator.go
Normal file
218
graph/iterator/loop_iterator.go
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
package iterator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/cayley/graph"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Loop struct {
|
||||||
|
uid uint64
|
||||||
|
tags graph.Tagger
|
||||||
|
ts graph.TripleStore
|
||||||
|
baseIt graph.Iterator
|
||||||
|
loopIt graph.Iterator
|
||||||
|
loopEntryIt *EntryPoint
|
||||||
|
filterIt graph.Iterator
|
||||||
|
filterEntryIt *EntryPoint
|
||||||
|
result graph.Value
|
||||||
|
runstats graph.IteratorStats
|
||||||
|
prevValuesIt graph.FixedIterator
|
||||||
|
loops int
|
||||||
|
bounded bool
|
||||||
|
loopsCompleted int
|
||||||
|
finished bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoop(ts graph.TripleStore, baseIt, loopIt, filterIt graph.Iterator, loopEntryIt, filterEntryIt *EntryPoint, loops int, bounded bool) *Loop {
|
||||||
|
return &Loop{
|
||||||
|
uid: NextUID(),
|
||||||
|
ts: ts,
|
||||||
|
baseIt: baseIt,
|
||||||
|
loopEntryIt: loopEntryIt,
|
||||||
|
loopIt: loopIt,
|
||||||
|
filterEntryIt: filterEntryIt,
|
||||||
|
filterIt: filterIt,
|
||||||
|
prevValuesIt: ts.FixedIterator(),
|
||||||
|
loops: loops,
|
||||||
|
bounded: bounded,
|
||||||
|
loopsCompleted: 0,
|
||||||
|
finished: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) UID() uint64 {
|
||||||
|
return it.uid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Tagger() *graph.Tagger {
|
||||||
|
return &it.tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) TagResults(dst map[string]graph.Value) {
|
||||||
|
for _, tag := range it.tags.Tags() {
|
||||||
|
dst[tag] = it.Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
for tag, value := range it.tags.Fixed() {
|
||||||
|
dst[tag] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
it.baseIt.TagResults(dst)
|
||||||
|
it.loopIt.TagResults(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Contains(val graph.Value) bool {
|
||||||
|
graph.ContainsLogIn(it, val)
|
||||||
|
if it.loopIt.Contains(val) {
|
||||||
|
return graph.ContainsLogOut(it, val, true)
|
||||||
|
}
|
||||||
|
return graph.ContainsLogOut(it, val, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Clone() graph.Iterator {
|
||||||
|
out := NewLoop(it.ts, it.baseIt, it.loopIt, it.filterIt, it.loopEntryIt, it.filterEntryIt, it.loops, it.bounded)
|
||||||
|
out.tags.CopyFrom(it)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Type() graph.Type { return graph.Loop }
|
||||||
|
|
||||||
|
func (it *Loop) Reset() {
|
||||||
|
// Reset the iterators
|
||||||
|
it.baseIt.Reset()
|
||||||
|
it.loopEntryIt.SetIterator(it.baseIt)
|
||||||
|
it.loopIt.Reset()
|
||||||
|
it.prevValuesIt.Close()
|
||||||
|
it.prevValuesIt = it.ts.FixedIterator()
|
||||||
|
|
||||||
|
// Reset the state
|
||||||
|
it.loopsCompleted = 0
|
||||||
|
it.finished = false
|
||||||
|
it.result = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) advanceLoop() {
|
||||||
|
// Set the loop iterator to feed from the previous iteration results
|
||||||
|
it.loopEntryIt.SetIterator(it.prevValuesIt)
|
||||||
|
|
||||||
|
// Reset the loop iterator - will also clean the values in the underlying fixed iterator.
|
||||||
|
it.loopIt.Reset()
|
||||||
|
|
||||||
|
it.filterIt.Reset()
|
||||||
|
|
||||||
|
// Increment the completed loops count
|
||||||
|
it.loopsCompleted += 1
|
||||||
|
|
||||||
|
// Mark the loop as finished - no more results can be expected.
|
||||||
|
// Either the number of loops has been executed, or there are
|
||||||
|
// no more expandable nodes.
|
||||||
|
if size, _ := it.prevValuesIt.Size(); (it.bounded && it.loopsCompleted >= it.loops) || size == 0 {
|
||||||
|
it.finished = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean the set of values seen in the previous loop
|
||||||
|
it.prevValuesIt = it.ts.FixedIterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) checkFilter(value graph.Value) bool {
|
||||||
|
fixed := it.ts.FixedIterator()
|
||||||
|
fixed.Add(value)
|
||||||
|
|
||||||
|
it.filterEntryIt.SetIterator(fixed)
|
||||||
|
it.filterIt.Reset()
|
||||||
|
|
||||||
|
//fmt.Println("Before add value")
|
||||||
|
//it.filterEntryIt.Add(value)
|
||||||
|
//fmt.Println("After add value")
|
||||||
|
|
||||||
|
//fmt.Println("Before next")
|
||||||
|
answer := graph.Next(it.filterIt)
|
||||||
|
//fmt.Println("After next")
|
||||||
|
return answer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Next() bool {
|
||||||
|
graph.NextLogIn(it)
|
||||||
|
it.runstats.Next += 1
|
||||||
|
|
||||||
|
return it.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) next() bool {
|
||||||
|
// Check if the loop has any more results
|
||||||
|
if it.finished {
|
||||||
|
return graph.NextLogOut(it, nil, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if found := graph.Next(it.loopIt); !found {
|
||||||
|
// A value has not been found, try looping again
|
||||||
|
it.advanceLoop()
|
||||||
|
return it.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
if it.checkFilter(it.loopIt.Result()) {
|
||||||
|
// A value has been found.
|
||||||
|
it.result = it.loopIt.Result()
|
||||||
|
it.prevValuesIt.Add(it.result)
|
||||||
|
it.runstats.ContainsNext += 1
|
||||||
|
|
||||||
|
return graph.NextLogOut(it, it.result, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Result() graph.Value {
|
||||||
|
return it.result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) NextPath() bool {
|
||||||
|
return it.loopIt.NextPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Stats() graph.IteratorStats {
|
||||||
|
subitStats := it.loopIt.Stats()
|
||||||
|
// TODO(barakmich): These should really come from the triplestore itself
|
||||||
|
fanoutFactor := int64(20)
|
||||||
|
checkConstant := int64(1)
|
||||||
|
nextConstant := int64(2)
|
||||||
|
return graph.IteratorStats{
|
||||||
|
NextCost: nextConstant + subitStats.NextCost,
|
||||||
|
ContainsCost: checkConstant + subitStats.ContainsCost,
|
||||||
|
Size: fanoutFactor * subitStats.Size,
|
||||||
|
Next: it.runstats.Next,
|
||||||
|
Contains: it.runstats.Contains,
|
||||||
|
ContainsNext: it.runstats.ContainsNext,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Size() (int64, bool) {
|
||||||
|
return it.Stats().Size, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
func (it *Loop) Optimize() (graph.Iterator, bool) {
|
||||||
|
return it, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) SubIterators() []graph.Iterator {
|
||||||
|
return []graph.Iterator{it.baseIt, it.loopIt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) DebugString(indent int) string {
|
||||||
|
return fmt.Sprintf("%s(%s %d \n%s)",
|
||||||
|
strings.Repeat(" ", indent),
|
||||||
|
it.Type(), it.UID(), it.baseIt.DebugString(indent+4))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Loop) Close() {
|
||||||
|
it.baseIt.Close()
|
||||||
|
it.loopIt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEPRECATED
|
||||||
|
func (it *Loop) ResultTree() *graph.ResultTree {
|
||||||
|
return graph.NewResultTree(it.Result())
|
||||||
|
}
|
||||||
138
graph/iterator/not_iterator.go
Normal file
138
graph/iterator/not_iterator.go
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
package iterator
|
||||||
|
|
||||||
|
import "github.com/google/cayley/graph"
|
||||||
|
|
||||||
|
type Not struct {
|
||||||
|
uid uint64
|
||||||
|
tags graph.Tagger
|
||||||
|
primaryIt graph.Iterator
|
||||||
|
forbiddenIt graph.Iterator
|
||||||
|
result graph.Value
|
||||||
|
runstats graph.IteratorStats
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNot(primaryIt, forbiddenIt graph.Iterator) *Not {
|
||||||
|
return &Not{
|
||||||
|
uid: NextUID(),
|
||||||
|
primaryIt: primaryIt,
|
||||||
|
forbiddenIt: forbiddenIt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) UID() uint64 {
|
||||||
|
return it.uid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Reset() {
|
||||||
|
it.result = nil
|
||||||
|
it.primaryIt.Reset()
|
||||||
|
it.forbiddenIt.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Tagger() *graph.Tagger {
|
||||||
|
return &it.tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) TagResults(dst map[string]graph.Value) {
|
||||||
|
for _, tag := range it.tags.Tags() {
|
||||||
|
dst[tag] = it.Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
for tag, value := range it.tags.Fixed() {
|
||||||
|
dst[tag] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if it.primaryIt != nil {
|
||||||
|
it.primaryIt.TagResults(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Clone() graph.Iterator {
|
||||||
|
not := NewNot(it.primaryIt.Clone(), it.forbiddenIt.Clone())
|
||||||
|
not.tags.CopyFrom(it)
|
||||||
|
return not
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) SubIterators() []graph.Iterator {
|
||||||
|
return []graph.Iterator{it.primaryIt, it.forbiddenIt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) ResultTree() *graph.ResultTree {
|
||||||
|
tree := graph.NewResultTree(it.Result())
|
||||||
|
tree.AddSubtree(it.primaryIt.ResultTree())
|
||||||
|
tree.AddSubtree(it.forbiddenIt.ResultTree())
|
||||||
|
return tree
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) DebugString(indent int) string {
|
||||||
|
return "todo"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Next() bool {
|
||||||
|
graph.NextLogIn(it)
|
||||||
|
it.runstats.Next += 1
|
||||||
|
for graph.Next(it.primaryIt) {
|
||||||
|
if curr := it.primaryIt.Result(); !it.forbiddenIt.Contains(curr) {
|
||||||
|
it.result = curr
|
||||||
|
it.runstats.ContainsNext += 1
|
||||||
|
return graph.NextLogOut(it, curr, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return graph.NextLogOut(it, nil, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Result() graph.Value {
|
||||||
|
return it.result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Contains(val graph.Value) bool {
|
||||||
|
graph.ContainsLogIn(it, val)
|
||||||
|
it.runstats.Contains += 1
|
||||||
|
|
||||||
|
mainGood := it.primaryIt.Contains(val)
|
||||||
|
if mainGood {
|
||||||
|
mainGood = !it.forbiddenIt.Contains(val)
|
||||||
|
}
|
||||||
|
return graph.ContainsLogOut(it, val, mainGood)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) NextPath() bool {
|
||||||
|
if it.primaryIt.NextPath() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return it.forbiddenIt.NextPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Close() {
|
||||||
|
it.primaryIt.Close()
|
||||||
|
it.forbiddenIt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Type() graph.Type { return graph.Not }
|
||||||
|
|
||||||
|
func (it *Not) Optimize() (graph.Iterator, bool) {
|
||||||
|
//it.forbiddenIt = NewMaterialize(it.forbiddenIt)
|
||||||
|
return it, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Stats() graph.IteratorStats {
|
||||||
|
subitStats := it.primaryIt.Stats()
|
||||||
|
// TODO(barakmich): These should really come from the triplestore itself
|
||||||
|
fanoutFactor := int64(20)
|
||||||
|
checkConstant := int64(1)
|
||||||
|
nextConstant := int64(2)
|
||||||
|
return graph.IteratorStats{
|
||||||
|
NextCost: nextConstant + subitStats.NextCost,
|
||||||
|
ContainsCost: checkConstant + subitStats.ContainsCost,
|
||||||
|
Size: fanoutFactor * subitStats.Size,
|
||||||
|
Next: it.runstats.Next,
|
||||||
|
Contains: it.runstats.Contains,
|
||||||
|
ContainsNext: it.runstats.ContainsNext,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Not) Size() (int64, bool) {
|
||||||
|
return it.Stats().Size, false
|
||||||
|
}
|
||||||
|
|
@ -298,6 +298,79 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
|
||||||
it = buildIteratorTreeHelper(arg.Object(), ts, subIt)
|
it = buildIteratorTreeHelper(arg.Object(), ts, subIt)
|
||||||
case "in":
|
case "in":
|
||||||
it = buildInOutIterator(obj, ts, subIt, true)
|
it = buildInOutIterator(obj, ts, subIt, true)
|
||||||
|
case "not":
|
||||||
|
// Not is implemented as the difference between the primary iterator
|
||||||
|
// and the iterator chain of (primaryIt, follow, followR).
|
||||||
|
// Build the follow iterator
|
||||||
|
arg, _ := obj.Get("_gremlin_values")
|
||||||
|
firstArg, _ := arg.Object().Get("0")
|
||||||
|
if isVertexChain(firstArg.Object()) {
|
||||||
|
return iterator.NewNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the followR iterator
|
||||||
|
revArg, _ := obj.Get("_gremlin_followr")
|
||||||
|
if isVertexChain(revArg.Object()) {
|
||||||
|
return iterator.NewNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
followIt := buildIteratorTreeHelper(firstArg.Object(), ts, subIt)
|
||||||
|
forbiddenIt := buildIteratorTreeHelper(revArg.Object(), ts, followIt)
|
||||||
|
|
||||||
|
it = iterator.NewNot(subIt, forbiddenIt)
|
||||||
|
case "loop":
|
||||||
|
arg, _ := obj.Get("_gremlin_values")
|
||||||
|
firstArg, _ := arg.Object().Get("0")
|
||||||
|
secondArg, _ := arg.Object().Get("1")
|
||||||
|
thirdArg, _ := arg.Object().Get("2")
|
||||||
|
|
||||||
|
// Parse the loop iterating sequence
|
||||||
|
// Check if the first argument is a vertex chain
|
||||||
|
if isVertexChain(firstArg.Object()) {
|
||||||
|
return iterator.NewNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
loopEntryIt := iterator.NewEntryPoint(subIt)
|
||||||
|
loopIt := buildIteratorTreeHelper(firstArg.Object(), ts, loopEntryIt)
|
||||||
|
|
||||||
|
// Parse the number of loops to execute
|
||||||
|
noLoops := 0
|
||||||
|
bounded := false
|
||||||
|
if secondArg.IsNumber() {
|
||||||
|
if no, err := secondArg.ToInteger(); err == nil {
|
||||||
|
noLoops = int(no)
|
||||||
|
bounded = true
|
||||||
|
} else {
|
||||||
|
return iterator.NewNull()
|
||||||
|
}
|
||||||
|
} else if secondArg.IsBoolean() {
|
||||||
|
if boolVal, err := secondArg.ToBoolean(); err == nil && boolVal {
|
||||||
|
bounded = false
|
||||||
|
} else {
|
||||||
|
return iterator.NewNull()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thirdArg = secondArg
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the number of loops is negative, the loop is unbounded
|
||||||
|
if noLoops <= 0 {
|
||||||
|
bounded = false
|
||||||
|
} else {
|
||||||
|
bounded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
filterEntryIt := iterator.NewEntryPoint(nil)
|
||||||
|
var filterIt graph.Iterator
|
||||||
|
if thirdArg.IsNull() || thirdArg.IsUndefined() {
|
||||||
|
filterIt = filterEntryIt
|
||||||
|
} else if isVertexChain(thirdArg.Object()) {
|
||||||
|
return iterator.NewNull()
|
||||||
|
} else {
|
||||||
|
filterIt = buildIteratorTreeHelper(thirdArg.Object(), ts, filterEntryIt)
|
||||||
|
}
|
||||||
|
|
||||||
|
it = iterator.NewLoop(ts, subIt, loopIt, filterIt, loopEntryIt, filterEntryIt, noLoops, bounded)
|
||||||
}
|
}
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ func (wk *worker) embedTraversals(env *otto.Otto, obj *otto.Object) {
|
||||||
obj.Set("Has", wk.gremlinFunc("has", obj, env))
|
obj.Set("Has", wk.gremlinFunc("has", obj, env))
|
||||||
obj.Set("Save", wk.gremlinFunc("save", obj, env))
|
obj.Set("Save", wk.gremlinFunc("save", obj, env))
|
||||||
obj.Set("SaveR", wk.gremlinFunc("saver", obj, env))
|
obj.Set("SaveR", wk.gremlinFunc("saver", obj, env))
|
||||||
|
obj.Set("Loop", gremlinFunc("loop", obj, env, ses))
|
||||||
|
obj.Set("Not", gremlinFollowR("not", obj, env, ses))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wk *worker) gremlinFunc(kind string, prev *otto.Object, env *otto.Otto) func(otto.FunctionCall) otto.Value {
|
func (wk *worker) gremlinFunc(kind string, prev *otto.Object, env *otto.Otto) func(otto.FunctionCall) otto.Value {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue