Enumerate iterator types

This commit is contained in:
kortschak 2014-07-03 09:57:31 +09:30
parent 1f67913ed9
commit cd46452b63
23 changed files with 145 additions and 68 deletions

View file

@ -19,6 +19,7 @@ package graph
import ( import (
"strings" "strings"
"sync"
"github.com/barakmich/glog" "github.com/barakmich/glog"
) )
@ -96,7 +97,7 @@ type Iterator interface {
// Returns a string relating to what the function of the iterator is. By // Returns a string relating to what the function of the iterator is. By
// knowing the names of the iterators, we can devise optimization strategies. // knowing the names of the iterators, we can devise optimization strategies.
Type() string Type() Type
// Optimizes an iterator. Can replace the iterator, or merely move things // Optimizes an iterator. Can replace the iterator, or merely move things
// around internally. if it chooses to replace it with a better iterator, // around internally. if it chooses to replace it with a better iterator,
@ -126,20 +127,73 @@ type IteratorStats struct {
Size int64 Size int64
} }
type Type int
const (
Invalid Type = iota
All
And
Or
HasA
LinksTo
Comparison
Null
Fixed
Not
Optional
)
var (
lock sync.Mutex
// These strings must be kept in order consistent with the Type const block above.
types = []string{
"invalid",
"all",
"and",
"or",
"hasa",
"linksto",
"comparison",
"null",
"fixed",
"not",
"optional",
}
)
func Register(name string) Type {
lock.Lock()
defer lock.Unlock()
for i, t := range types {
if t == name {
return Type(i)
}
}
types = append(types, name)
return Type(len(types) - 1)
}
func (t Type) String() string {
if t < 0 || int(t) >= len(types) {
return "illegal-type"
}
return types[t]
}
// Utility logging functions for when an iterator gets called Next upon, or Check upon, as // Utility logging functions for when an iterator gets called Next upon, or Check upon, as
// well as what they return. Highly useful for tracing the execution path of a query. // well as what they return. Highly useful for tracing the execution path of a query.
func CheckLogIn(it Iterator, val Value) { func CheckLogIn(it Iterator, val Value) {
if glog.V(4) { if glog.V(4) {
glog.V(4).Infof("%s %d CHECK %d", strings.ToUpper(it.Type()), it.UID(), val) glog.V(4).Infof("%s %d CHECK %d", strings.ToUpper(it.Type().String()), it.UID(), val)
} }
} }
func CheckLogOut(it Iterator, val Value, good bool) bool { func CheckLogOut(it Iterator, val Value, good bool) bool {
if glog.V(4) { if glog.V(4) {
if good { if good {
glog.V(4).Infof("%s %d CHECK %d GOOD", strings.ToUpper(it.Type()), it.UID(), val) glog.V(4).Infof("%s %d CHECK %d GOOD", strings.ToUpper(it.Type().String()), it.UID(), val)
} else { } else {
glog.V(4).Infof("%s %d CHECK %d BAD", strings.ToUpper(it.Type()), it.UID(), val) glog.V(4).Infof("%s %d CHECK %d BAD", strings.ToUpper(it.Type().String()), it.UID(), val)
} }
} }
return good return good
@ -147,16 +201,16 @@ func CheckLogOut(it Iterator, val Value, good bool) bool {
func NextLogIn(it Iterator) { func NextLogIn(it Iterator) {
if glog.V(4) { if glog.V(4) {
glog.V(4).Infof("%s %d NEXT", strings.ToUpper(it.Type()), it.UID()) glog.V(4).Infof("%s %d NEXT", strings.ToUpper(it.Type().String()), it.UID())
} }
} }
func NextLogOut(it Iterator, val Value, ok bool) (Value, bool) { func NextLogOut(it Iterator, val Value, ok bool) (Value, bool) {
if glog.V(4) { if glog.V(4) {
if ok { if ok {
glog.V(4).Infof("%s %d NEXT IS %d", strings.ToUpper(it.Type()), it.UID(), val) glog.V(4).Infof("%s %d NEXT IS %d", strings.ToUpper(it.Type().String()), it.UID(), val)
} else { } else {
glog.V(4).Infof("%s %d NEXT DONE", strings.ToUpper(it.Type()), it.UID()) glog.V(4).Infof("%s %d NEXT DONE", strings.ToUpper(it.Type().String()), it.UID())
} }
} }
return val, ok return val, ok

View file

@ -101,7 +101,7 @@ func (it *Int64) Check(tsv graph.Value) bool {
// The type of this iterator is an "all". This is important, as it puts it in // The type of this iterator is an "all". This is important, as it puts it in
// the class of "all iterators. // the class of "all iterators.
func (it *Int64) Type() string { return "all" } func (it *Int64) Type() graph.Type { return graph.All }
// There's nothing to optimize about this little iterator. // There's nothing to optimize about this little iterator.
func (it *Int64) Optimize() (graph.Iterator, bool) { return it, false } func (it *Int64) Optimize() (graph.Iterator, bool) { return it, false }

View file

@ -246,4 +246,4 @@ func (it *And) Close() {
} }
// Register this as an "and" iterator. // Register this as an "and" iterator.
func (it *And) Type() string { return "and" } func (it *And) Type() graph.Type { return graph.And }

View file

@ -134,7 +134,7 @@ func (_ *And) optimizeReplacement(its []graph.Iterator) graph.Iterator {
func optimizeOrder(its []graph.Iterator) []graph.Iterator { func optimizeOrder(its []graph.Iterator) []graph.Iterator {
var ( var (
// bad contains iterators that can't be (efficiently) nexted, such as // bad contains iterators that can't be (efficiently) nexted, such as
// "optional" or "not". Separate them out and tack them on at the end. // graph.Optional or graph.Not. Separate them out and tack them on at the end.
out, bad []graph.Iterator out, bad []graph.Iterator
best graph.Iterator best graph.Iterator
bestCost = int64(1 << 62) bestCost = int64(1 << 62)
@ -257,25 +257,25 @@ func optimizeSubIterators(its []graph.Iterator) []graph.Iterator {
// Check a list of iterators for any Null iterators. // Check a list of iterators for any Null iterators.
func hasAnyNullIterators(its []graph.Iterator) bool { func hasAnyNullIterators(its []graph.Iterator) bool {
for _, it := range its { for _, it := range its {
if it.Type() == "null" { if it.Type() == graph.Null {
return true return true
} }
} }
return false return false
} }
// There are two "not-useful" iterators -- namely "null" which returns // There are two "not-useful" iterators -- namely graph.Null which returns
// nothing, and "all" which returns everything. Particularly, we want // nothing, and graph.All which returns everything. Particularly, we want
// to see if we're intersecting with a bunch of "all" iterators, and, // to see if we're intersecting with a bunch of graph.All iterators, and,
// if we are, then we have only one useful iterator. // if we are, then we have only one useful iterator.
func hasOneUsefulIterator(its []graph.Iterator) graph.Iterator { func hasOneUsefulIterator(its []graph.Iterator) graph.Iterator {
usefulCount := 0 usefulCount := 0
var usefulIt graph.Iterator var usefulIt graph.Iterator
for _, it := range its { for _, it := range its {
switch it.Type() { switch it.Type() {
case "null", "all": case graph.Null, graph.All:
continue continue
case "optional": case graph.Optional:
// Optional is weird -- it's not useful, but we can't optimize // Optional is weird -- it's not useful, but we can't optimize
// away from it. Therefore, we skip this optimization // away from it. Therefore, we skip this optimization
// if we see one. // if we see one.

View file

@ -21,6 +21,8 @@ import (
"reflect" "reflect"
"sort" "sort"
"testing" "testing"
"github.com/google/cayley/graph"
) )
func TestIteratorPromotion(t *testing.T) { func TestIteratorPromotion(t *testing.T) {
@ -37,7 +39,7 @@ func TestIteratorPromotion(t *testing.T) {
if !changed { if !changed {
t.Error("Iterator didn't optimize") t.Error("Iterator didn't optimize")
} }
if newIt.Type() != "fixed" { if newIt.Type() != graph.Fixed {
t.Error("Expected fixed iterator") t.Error("Expected fixed iterator")
} }
tagsExpected := []string{"a", "b", "c"} tagsExpected := []string{"a", "b", "c"}
@ -58,7 +60,7 @@ func TestNullIteratorAnd(t *testing.T) {
if !changed { if !changed {
t.Error("Didn't change") t.Error("Didn't change")
} }
if newIt.Type() != "null" { if newIt.Type() != graph.Null {
t.Error("Expected null iterator, got ", newIt.Type()) t.Error("Expected null iterator, got ", newIt.Type())
} }
} }

View file

@ -99,9 +99,7 @@ func (it *Fixed) DebugString(indent int) string {
} }
// Register this iterator as a Fixed iterator. // Register this iterator as a Fixed iterator.
func (it *Fixed) Type() string { func (it *Fixed) Type() graph.Type { return graph.Fixed }
return "fixed"
}
// Check if the passed value is equal to one of the values stored in the iterator. // Check if the passed value is equal to one of the values stored in the iterator.
func (it *Fixed) Check(v graph.Value) bool { func (it *Fixed) Check(v graph.Value) bool {

View file

@ -91,7 +91,7 @@ func (it *HasA) Optimize() (graph.Iterator, bool) {
newPrimary, changed := it.primaryIt.Optimize() newPrimary, changed := it.primaryIt.Optimize()
if changed { if changed {
it.primaryIt = newPrimary it.primaryIt = newPrimary
if it.primaryIt.Type() == "null" { if it.primaryIt.Type() == graph.Null {
return it.primaryIt, true return it.primaryIt, true
} }
} }
@ -220,4 +220,4 @@ func (it *HasA) Close() {
} }
// Register this iterator as a HasA. // Register this iterator as a HasA.
func (it *HasA) Type() string { return "hasa" } func (it *HasA) Type() graph.Type { return graph.HasA }

View file

@ -174,7 +174,7 @@ func NewNull() *Null {
func (it *Null) Clone() graph.Iterator { return NewNull() } func (it *Null) Clone() graph.Iterator { return NewNull() }
// Name the null iterator. // Name the null iterator.
func (it *Null) Type() string { return "null" } func (it *Null) Type() graph.Type { return graph.Null }
// A good iterator will close itself when it returns true. // A good iterator will close itself when it returns true.
// Null has nothing it needs to do. // Null has nothing it needs to do.

View file

@ -118,7 +118,7 @@ func (it *LinksTo) Optimize() (graph.Iterator, bool) {
newPrimary, changed := it.primaryIt.Optimize() newPrimary, changed := it.primaryIt.Optimize()
if changed { if changed {
it.primaryIt = newPrimary it.primaryIt = newPrimary
if it.primaryIt.Type() == "null" { if it.primaryIt.Type() == graph.Null {
it.nextIt.Close() it.nextIt.Close()
return it.primaryIt, true return it.primaryIt, true
} }
@ -166,7 +166,7 @@ func (it *LinksTo) NextResult() bool {
} }
// Register the LinksTo. // Register the LinksTo.
func (it *LinksTo) Type() string { return "linksto" } func (it *LinksTo) Type() graph.Type { return graph.LinksTo }
// Return a guess as to how big or costly it is to next the iterator. // Return a guess as to how big or costly it is to next the iterator.
func (it *LinksTo) Stats() graph.IteratorStats { func (it *LinksTo) Stats() graph.IteratorStats {

View file

@ -104,7 +104,7 @@ func (it *Optional) TagResults(dst map[string]graph.Value) {
} }
// Registers the optional iterator. // Registers the optional iterator.
func (it *Optional) Type() string { return "optional" } func (it *Optional) Type() graph.Type { return graph.Optional }
// Prints the optional and it's subiterator. // Prints the optional and it's subiterator.
func (it *Optional) DebugString(indent int) string { func (it *Optional) DebugString(indent int) string {

View file

@ -281,4 +281,4 @@ func (it *Or) Stats() graph.IteratorStats {
} }
// Register this as an "or" graph.iterator. // Register this as an "or" graph.iterator.
func (it *Or) Type() string { return "or" } func (it *Or) Type() graph.Type { return graph.Or }

View file

@ -115,18 +115,18 @@ func (qs *queryShape) MakeNode(it graph.Iterator) *Node {
} }
switch it.Type() { switch it.Type() {
case "and": case graph.And:
for _, sub := range it.SubIterators() { for _, sub := range it.SubIterators() {
qs.nodeId++ qs.nodeId++
newNode := qs.MakeNode(sub) newNode := qs.MakeNode(sub)
if sub.Type() != "or" { if sub.Type() != graph.Or {
qs.StealNode(&n, newNode) qs.StealNode(&n, newNode)
} else { } else {
qs.AddNode(newNode) qs.AddNode(newNode)
qs.AddLink(&Link{n.Id, newNode.Id, 0, 0}) qs.AddLink(&Link{n.Id, newNode.Id, 0, 0})
} }
} }
case "fixed": case graph.Fixed:
n.IsFixed = true n.IsFixed = true
for { for {
val, more := it.Next() val, more := it.Next()
@ -135,25 +135,25 @@ func (qs *queryShape) MakeNode(it graph.Iterator) *Node {
} }
n.Values = append(n.Values, qs.ts.NameOf(val)) n.Values = append(n.Values, qs.ts.NameOf(val))
} }
case "hasa": case graph.HasA:
hasa := it.(*HasA) hasa := it.(*HasA)
qs.PushHasa(n.Id, hasa.dir) qs.PushHasa(n.Id, hasa.dir)
qs.nodeId++ qs.nodeId++
newNode := qs.MakeNode(hasa.primaryIt) newNode := qs.MakeNode(hasa.primaryIt)
qs.AddNode(newNode) qs.AddNode(newNode)
qs.RemoveHasa() qs.RemoveHasa()
case "or": case graph.Or:
for _, sub := range it.SubIterators() { for _, sub := range it.SubIterators() {
qs.nodeId++ qs.nodeId++
newNode := qs.MakeNode(sub) newNode := qs.MakeNode(sub)
if sub.Type() == "or" { if sub.Type() == graph.Or {
qs.StealNode(&n, newNode) qs.StealNode(&n, newNode)
} else { } else {
qs.AddNode(newNode) qs.AddNode(newNode)
qs.AddLink(&Link{n.Id, newNode.Id, 0, 0}) qs.AddLink(&Link{n.Id, newNode.Id, 0, 0})
} }
} }
case "linksto": case graph.LinksTo:
n.IsLinkNode = true n.IsLinkNode = true
lto := it.(*LinksTo) lto := it.(*LinksTo)
qs.nodeId++ qs.nodeId++
@ -167,15 +167,15 @@ func (qs *queryShape) MakeNode(it graph.Iterator) *Node {
} else { } else {
qs.AddLink(&Link{newNode.Id, hasaID, 0, n.Id}) qs.AddLink(&Link{newNode.Id, hasaID, 0, n.Id})
} }
} else if lto.primaryIt.Type() == "fixed" { } else if lto.primaryIt.Type() == graph.Fixed {
qs.StealNode(&n, newNode) qs.StealNode(&n, newNode)
} else { } else {
qs.AddNode(newNode) qs.AddNode(newNode)
} }
case "optional": case graph.Optional:
// Unsupported, for the moment // Unsupported, for the moment
fallthrough fallthrough
case "all": case graph.All:
} }
return &n return &n
} }

View file

@ -162,7 +162,7 @@ func (it *Comparison) TagResults(dst map[string]graph.Value) {
} }
// Registers the value-comparison iterator. // Registers the value-comparison iterator.
func (it *Comparison) Type() string { return "value-comparison" } func (it *Comparison) Type() graph.Type { return graph.Comparison }
// Prints the value-comparison and its subiterator. // Prints the value-comparison and its subiterator.
func (it *Comparison) DebugString(indent int) string { func (it *Comparison) DebugString(indent int) string {

View file

@ -118,8 +118,8 @@ func (it *AllIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s tags: %v leveldb size:%d %s %p)", strings.Repeat(" ", indent), it.Type(), it.Tags(), size, it.dir, it) return fmt.Sprintf("%s(%s tags: %v leveldb size:%d %s %p)", strings.Repeat(" ", indent), it.Type(), it.Tags(), size, it.dir, it)
} }
func (it *AllIterator) Type() string { return "all" } func (it *AllIterator) Type() graph.Type { return graph.All }
func (it *AllIterator) Sorted() bool { return false } func (it *AllIterator) Sorted() bool { return false }
func (it *AllIterator) Optimize() (graph.Iterator, bool) { func (it *AllIterator) Optimize() (graph.Iterator, bool) {
return it, false return it, false

View file

@ -195,8 +195,16 @@ func (it *Iterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s %d tags: %v dir: %s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.UID(), it.Tags(), it.dir, size, it.ts.NameOf(it.checkId)) return fmt.Sprintf("%s(%s %d tags: %v dir: %s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.UID(), it.Tags(), it.dir, size, it.ts.NameOf(it.checkId))
} }
func (it *Iterator) Type() string { return "leveldb" } var levelDBType graph.Type
func (it *Iterator) Sorted() bool { return false }
func init() {
levelDBType = graph.Register("leveldb")
}
func Type() graph.Type { return levelDBType }
func (it *Iterator) Type() graph.Type { return levelDBType }
func (it *Iterator) Sorted() bool { return false }
func (it *Iterator) Optimize() (graph.Iterator, bool) { func (it *Iterator) Optimize() (graph.Iterator, bool) {
return it, false return it, false

View file

@ -160,7 +160,7 @@ func TestIterator(t *testing.T) {
size, accurate := it.Size() size, accurate := it.Size()
So(size, ShouldBeBetween, 0, 20) So(size, ShouldBeBetween, 0, 20)
So(accurate, ShouldBeFalse) So(accurate, ShouldBeFalse)
So(it.Type(), ShouldEqual, "all") So(it.Type(), ShouldEqual, graph.All)
re_it, ok := it.Optimize() re_it, ok := it.Optimize()
So(ok, ShouldBeFalse) So(ok, ShouldBeFalse)
So(re_it, ShouldPointTo, it) So(re_it, ShouldPointTo, it)
@ -209,7 +209,7 @@ func TestIterator(t *testing.T) {
size, accurate := it.Size() size, accurate := it.Size()
So(size, ShouldBeBetween, 0, 20) So(size, ShouldBeBetween, 0, 20)
So(accurate, ShouldBeFalse) So(accurate, ShouldBeFalse)
So(it.Type(), ShouldEqual, "all") So(it.Type(), ShouldEqual, graph.All)
re_it, ok := it.Optimize() re_it, ok := it.Optimize()
So(ok, ShouldBeFalse) So(ok, ShouldBeFalse)
So(re_it, ShouldPointTo, it) So(re_it, ShouldPointTo, it)
@ -407,7 +407,7 @@ func TestOptimize(t *testing.T) {
oldIt := lto.Clone() oldIt := lto.Clone()
newIt, ok := lto.Optimize() newIt, ok := lto.Optimize()
So(ok, ShouldBeTrue) So(ok, ShouldBeTrue)
So(newIt.Type(), ShouldEqual, "leveldb") So(newIt.Type(), ShouldEqual, Type())
Convey("Containing the right things", func() { Convey("Containing the right things", func() {
afterOp := extractTripleFromIterator(ts, newIt) afterOp := extractTripleFromIterator(ts, newIt)

View file

@ -21,7 +21,7 @@ import (
func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) { func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) {
switch it.Type() { switch it.Type() {
case "linksto": case graph.LinksTo:
return ts.optimizeLinksTo(it.(*iterator.LinksTo)) return ts.optimizeLinksTo(it.(*iterator.LinksTo))
} }
@ -34,7 +34,7 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo
return it, false return it, false
} }
primary := subs[0] primary := subs[0]
if primary.Type() == "fixed" { if primary.Type() == graph.Fixed {
size, _ := primary.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := primary.Next() val, ok := primary.Next()

View file

@ -101,12 +101,18 @@ func (it *Iterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s tags:%s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.Tags(), size, it.data) return fmt.Sprintf("%s(%s tags:%s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.Tags(), size, it.data)
} }
func (it *Iterator) Type() string { var memType graph.Type
return "llrb"
} func init() {
func (it *Iterator) Sorted() bool { memType = graph.Register("llrb")
return true
} }
func Type() graph.Type { return memType }
func (it *Iterator) Type() graph.Type { return memType }
func (it *Iterator) Sorted() bool { return true }
func (it *Iterator) Optimize() (graph.Iterator, bool) { func (it *Iterator) Optimize() (graph.Iterator, bool) {
return it, false return it, false
} }

View file

@ -21,7 +21,7 @@ import (
func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) { func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) {
switch it.Type() { switch it.Type() {
case "linksto": case graph.LinksTo:
return ts.optimizeLinksTo(it.(*iterator.LinksTo)) return ts.optimizeLinksTo(it.(*iterator.LinksTo))
} }
@ -34,7 +34,7 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo
return it, false return it, false
} }
primary := subs[0] primary := subs[0]
if primary.Type() == "fixed" { if primary.Type() == graph.Fixed {
size, _ := primary.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := primary.Next() val, ok := primary.Next()

View file

@ -105,7 +105,7 @@ func TestLinksToOptimization(t *testing.T) {
if !changed { if !changed {
t.Error("Iterator didn't change") t.Error("Iterator didn't change")
} }
if newIt.Type() != "llrb" { if newIt.Type() != Type() {
t.Fatal("Didn't swap out to LLRB") t.Fatal("Didn't swap out to LLRB")
} }
v := newIt.(*Iterator) v := newIt.(*Iterator)

View file

@ -157,12 +157,21 @@ func (it *Iterator) Size() (int64, bool) {
return it.size, true return it.size, true
} }
func (it *Iterator) Type() string { var mongoType graph.Type
if it.isAll {
return "all" func init() {
} mongoType = graph.Register("mongo")
return "mongo"
} }
func Type() graph.Type { return mongoType }
func (it *Iterator) Type() graph.Type {
if it.isAll {
return graph.All
}
return mongoType
}
func (it *Iterator) Sorted() bool { return true } func (it *Iterator) Sorted() bool { return true }
func (it *Iterator) Optimize() (graph.Iterator, bool) { return it, false } func (it *Iterator) Optimize() (graph.Iterator, bool) { return it, false }

View file

@ -21,7 +21,7 @@ import (
func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) { func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) {
switch it.Type() { switch it.Type() {
case "linksto": case graph.LinksTo:
return ts.optimizeLinksTo(it.(*iterator.LinksTo)) return ts.optimizeLinksTo(it.(*iterator.LinksTo))
} }
@ -34,7 +34,7 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo
return it, false return it, false
} }
primary := subs[0] primary := subs[0]
if primary.Type() == "fixed" { if primary.Type() == graph.Fixed {
size, _ := primary.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := primary.Next() val, ok := primary.Next()

View file

@ -36,7 +36,7 @@ func TestParseSexpWithMemstore(t *testing.T) {
Convey("It should parse an empty query", func() { Convey("It should parse an empty query", func() {
it := BuildIteratorTreeForQuery(ts, "()") it := BuildIteratorTreeForQuery(ts, "()")
So(it.Type(), ShouldEqual, "null") So(it.Type(), ShouldEqual, graph.Null)
}) })
Convey("It should get a single triple linkage", func() { Convey("It should get a single triple linkage", func() {
@ -44,7 +44,7 @@ func TestParseSexpWithMemstore(t *testing.T) {
query := "($a (:can \"win\"))" query := "($a (:can \"win\"))"
So(len(query), ShouldEqual, 17) So(len(query), ShouldEqual, 17)
it := BuildIteratorTreeForQuery(ts, query) it := BuildIteratorTreeForQuery(ts, query)
So(it.Type(), ShouldEqual, "and") So(it.Type(), ShouldEqual, graph.And)
out, ok := it.Next() out, ok := it.Next()
So(ok, ShouldBeTrue) So(ok, ShouldBeTrue)
So(out, ShouldEqual, ts.ValueOf("i")) So(out, ShouldEqual, ts.ValueOf("i"))
@ -54,7 +54,7 @@ func TestParseSexpWithMemstore(t *testing.T) {
ts.AddTriple(&graph.Triple{"i", "can", "win", ""}) ts.AddTriple(&graph.Triple{"i", "can", "win", ""})
query := "(\"i\" (:can $a))" query := "(\"i\" (:can $a))"
it := BuildIteratorTreeForQuery(ts, query) it := BuildIteratorTreeForQuery(ts, query)
So(it.Type(), ShouldEqual, "and") So(it.Type(), ShouldEqual, graph.And)
out, ok := it.Next() out, ok := it.Next()
So(ok, ShouldBeTrue) So(ok, ShouldBeTrue)
So(out, ShouldEqual, ts.ValueOf("i")) So(out, ShouldEqual, ts.ValueOf("i"))
@ -71,7 +71,7 @@ func TestTreeConstraintParse(t *testing.T) {
"(:like\n" + "(:like\n" +
"($a (:is :good))))" "($a (:is :good))))"
it := BuildIteratorTreeForQuery(ts, query) it := BuildIteratorTreeForQuery(ts, query)
if it.Type() != "and" { if it.Type() != graph.And {
t.Error("Odd iterator tree. Got: %s", it.DebugString(0)) t.Error("Odd iterator tree. Got: %s", it.DebugString(0))
} }
out, ok := it.Next() out, ok := it.Next()
@ -112,7 +112,7 @@ func TestMultipleConstraintParse(t *testing.T) {
"(:like :beer)\n" + "(:like :beer)\n" +
"(:like \"food\"))" "(:like \"food\"))"
it := BuildIteratorTreeForQuery(ts, query) it := BuildIteratorTreeForQuery(ts, query)
if it.Type() != "and" { if it.Type() != graph.And {
t.Error("Odd iterator tree. Got: %s", it.DebugString(0)) t.Error("Odd iterator tree. Got: %s", it.DebugString(0))
} }
out, ok := it.Next() out, ok := it.Next()