Merge pull request #93 from kortschak/nexter

Simplify the Nexter interface
This commit is contained in:
Barak Michener 2014-08-06 23:48:46 -04:00
commit 7cd740aa7b
33 changed files with 214 additions and 278 deletions

View file

@ -52,7 +52,7 @@ An important failure of MQL before was that it was never well-specified. Let's n
### New Iterators ### New Iterators
#### Limit Iterator #### Limit Iterator
The necessary component to make mid-query limit work. Acts as a limit on Next(), a passthrough on Contains(), and a limit on NextResult() The necessary component to make mid-query limit work. Acts as a limit on Next(), a passthrough on Contains(), and a limit on NextPath()
## Medium Term ## Medium Term

View file

@ -77,17 +77,21 @@ type Iterator interface {
// the iteration interface. // the iteration interface.
// //
// To get the full results of iteration, do the following: // To get the full results of iteration, do the following:
// while (!Next()): //
// emit result // for graph.Next(it) {
// while (!NextResult()): // val := it.Result()
// emit result // ... do things with val.
// for it.NextPath() {
// ... find other paths to iterate
// }
// }
// //
// All of them should set iterator.Last to be the last returned value, to // All of them should set iterator.Last to be the last returned value, to
// make results work. // make results work.
// //
// NextResult() advances iterators that may have more than one valid result, // NextPath() advances iterators that may have more than one valid result,
// from the bottom up. // from the bottom up.
NextResult() bool NextPath() bool
// Contains returns whether the value is within the set held by the iterator. // Contains returns whether the value is within the set held by the iterator.
Contains(Value) bool Contains(Value) bool
@ -135,22 +139,22 @@ type Iterator interface {
} }
type Nexter interface { type Nexter interface {
// Next() advances the iterator and returns the next valid result. Returns // Next advances the iterator to the next value, which will then be available through
// (<value>, true) or (nil, false) // the Result method. It returns false if no further advancement is possible.
Next() (Value, bool) Next() bool
Iterator Iterator
} }
// Next is a convenience function that conditionally calls the Next method // Next is a convenience function that conditionally calls the Next method
// of an Iterator if it is a Nexter. If the Iterator is not a Nexter, Next // of an Iterator if it is a Nexter. If the Iterator is not a Nexter, Next
// return a nil Value and false. // returns false.
func Next(it Iterator) (Value, bool) { func Next(it Iterator) bool {
if n, ok := it.(Nexter); ok { if n, ok := it.(Nexter); ok {
return n.Next() return n.Next()
} }
glog.Errorln("Nexting an un-nextable iterator") glog.Errorln("Nexting an un-nextable iterator")
return nil, false return false
} }
// Height is a convienence function to measure the height of an iterator tree. // Height is a convienence function to measure the height of an iterator tree.
@ -271,7 +275,7 @@ func NextLogIn(it Iterator) {
} }
} }
func NextLogOut(it Iterator, val Value, ok bool) (Value, bool) { func NextLogOut(it Iterator, val Value, ok bool) 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().String()), it.UID(), val) glog.V(4).Infof("%s %d NEXT IS %d", strings.ToUpper(it.Type().String()), it.UID(), val)
@ -279,5 +283,5 @@ func NextLogOut(it Iterator, val Value, ok bool) (Value, bool) {
glog.V(4).Infof("%s %d NEXT DONE", strings.ToUpper(it.Type().String()), it.UID()) glog.V(4).Infof("%s %d NEXT DONE", strings.ToUpper(it.Type().String()), it.UID())
} }
} }
return val, ok return ok
} }

View file

@ -87,7 +87,7 @@ func (it *Int64) DebugString(indent int) string {
// Next() on an Int64 all iterator is a simple incrementing counter. // Next() on an Int64 all iterator is a simple incrementing counter.
// Return the next integer, and mark it as the result. // Return the next integer, and mark it as the result.
func (it *Int64) Next() (graph.Value, bool) { func (it *Int64) Next() bool {
graph.NextLogIn(it) graph.NextLogIn(it)
if it.at == -1 { if it.at == -1 {
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, nil, false)
@ -110,7 +110,7 @@ func (it *Int64) Result() graph.Value {
return it.result return it.result
} }
func (it *Int64) NextResult() bool { func (it *Int64) NextPath() bool {
return false return false
} }

View file

@ -151,25 +151,20 @@ func (it *And) AddSubIterator(sub graph.Iterator) {
it.itCount++ it.itCount++
} }
// Returns the Next value from the And iterator. Because the And is the // Returns advances the And iterator. Because the And is the intersection of its
// intersection of its subiterators, it must choose one subiterator to produce a // subiterators, it must choose one subiterator to produce a candidate, and check
// candidate, and check this value against the subiterators. A productive choice // this value against the subiterators. A productive choice of primary iterator
// of primary iterator is therefore very important. // is therefore very important.
func (it *And) Next() (graph.Value, bool) { func (it *And) Next() bool {
graph.NextLogIn(it) graph.NextLogIn(it)
var curr graph.Value for graph.Next(it.primaryIt) {
var exists bool curr := it.primaryIt.Result()
for {
curr, exists = graph.Next(it.primaryIt)
if !exists {
return graph.NextLogOut(it, nil, false)
}
if it.subItsContain(curr) { if it.subItsContain(curr) {
it.result = curr it.result = curr
return graph.NextLogOut(it, curr, true) return graph.NextLogOut(it, curr, true)
} }
} }
panic("unreachable") return graph.NextLogOut(it, nil, false)
} }
func (it *And) Result() graph.Value { func (it *And) Result() graph.Value {
@ -236,15 +231,15 @@ func (it *And) Size() (int64, bool) {
return val, b return val, b
} }
// An And has no NextResult of its own -- that is, there are no other values // An And has no NextPath of its own -- that is, there are no other values
// which satisfy our previous result that are not the result itself. Our // which satisfy our previous result that are not the result itself. Our
// subiterators might, however, so just pass the call recursively. // subiterators might, however, so just pass the call recursively.
func (it *And) NextResult() bool { func (it *And) NextPath() bool {
if it.primaryIt.NextResult() { if it.primaryIt.NextPath() {
return true return true
} }
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
if sub.NextResult() { if sub.NextPath() {
return true return true
} }
} }

View file

@ -36,10 +36,10 @@ func TestTag(t *testing.T) {
t.Errorf("Cannot get tag back, got %s", out[0]) t.Errorf("Cannot get tag back, got %s", out[0])
} }
val, ok := and.Next() if !and.Next() {
if !ok {
t.Errorf("And did not next") t.Errorf("And did not next")
} }
val := and.Result()
if val != 234 { if val != 234 {
t.Errorf("Unexpected value") t.Errorf("Unexpected value")
} }
@ -76,18 +76,15 @@ func TestAndAndFixedIterators(t *testing.T) {
t.Error("not accurate") t.Error("not accurate")
} }
val, ok := and.Next() if !and.Next() || and.Result() != 3 {
if val != 3 || ok == false {
t.Error("Incorrect first value") t.Error("Incorrect first value")
} }
val, ok = and.Next() if !and.Next() || and.Result() != 4 {
if val != 4 || ok == false {
t.Error("Incorrect second value") t.Error("Incorrect second value")
} }
val, ok = and.Next() if and.Next() {
if ok {
t.Error("Too many values") t.Error("Too many values")
} }
@ -117,8 +114,7 @@ func TestNonOverlappingFixedIterators(t *testing.T) {
t.Error("not accurate") t.Error("not accurate")
} }
_, ok := and.Next() if and.Next() {
if ok {
t.Error("Too many values") t.Error("Too many values")
} }
@ -131,18 +127,15 @@ func TestAllIterators(t *testing.T) {
and.AddSubIterator(all2) and.AddSubIterator(all2)
and.AddSubIterator(all1) and.AddSubIterator(all1)
val, ok := and.Next() if !and.Next() || and.Result() != int64(4) {
if val.(int64) != 4 || ok == false {
t.Error("Incorrect first value") t.Error("Incorrect first value")
} }
val, ok = and.Next() if !and.Next() || and.Result() != int64(5) {
if val.(int64) != 5 || ok == false {
t.Error("Incorrect second value") t.Error("Incorrect second value")
} }
val, ok = and.Next() if and.Next() {
if ok {
t.Error("Too many values") t.Error("Too many values")
} }

View file

@ -135,8 +135,8 @@ func (it *Fixed) Contains(v graph.Value) bool {
return graph.ContainsLogOut(it, v, false) return graph.ContainsLogOut(it, v, false)
} }
// Return the next stored value from the iterator. // Next advances the iterator.
func (it *Fixed) Next() (graph.Value, bool) { func (it *Fixed) Next() bool {
graph.NextLogIn(it) graph.NextLogIn(it)
if it.lastIndex == len(it.values) { if it.lastIndex == len(it.values) {
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, nil, false)
@ -156,7 +156,7 @@ func (it *Fixed) Result() graph.Value {
return it.result return it.result
} }
func (it *Fixed) NextResult() bool { func (it *Fixed) NextPath() bool {
return false return false
} }

View file

@ -27,9 +27,9 @@ package iterator
// value to check, it means "Check all predicates that have this value for your // value to check, it means "Check all predicates that have this value for your
// direction against the subiterator." This would imply that there's more than // direction against the subiterator." This would imply that there's more than
// one possibility for the same Contains()ed value. While we could return the // one possibility for the same Contains()ed value. While we could return the
// number of options, it's simpler to return one, and then call NextResult() // number of options, it's simpler to return one, and then call NextPath()
// enough times to enumerate the options. (In fact, one could argue that the // enough times to enumerate the options. (In fact, one could argue that the
// raison d'etre for NextResult() is this iterator). // raison d'etre for NextPath() is this iterator).
// //
// Alternatively, can be seen as the dual of the LinksTo iterator. // Alternatively, can be seen as the dual of the LinksTo iterator.
@ -158,16 +158,13 @@ func (it *HasA) Contains(val graph.Value) bool {
// result iterator (a triple iterator based on the last checked value) and returns true if // result iterator (a triple iterator based on the last checked value) and returns true if
// another match is made. // another match is made.
func (it *HasA) NextContains() bool { func (it *HasA) NextContains() bool {
for { for graph.Next(it.resultIt) {
linkVal, ok := graph.Next(it.resultIt) link := it.resultIt.Result()
if !ok {
break
}
if glog.V(4) { if glog.V(4) {
glog.V(4).Infoln("Quad is", it.ts.Quad(linkVal)) glog.V(4).Infoln("Quad is", it.ts.Quad(link))
} }
if it.primaryIt.Contains(linkVal) { if it.primaryIt.Contains(link) {
it.result = it.ts.TripleDirection(linkVal, it.dir) it.result = it.ts.TripleDirection(link, it.dir)
return true return true
} }
} }
@ -175,33 +172,33 @@ func (it *HasA) NextContains() bool {
} }
// Get the next result that matches this branch. // Get the next result that matches this branch.
func (it *HasA) NextResult() bool { func (it *HasA) NextPath() bool {
// Order here is important. If the subiterator has a NextResult, then we // Order here is important. If the subiterator has a NextPath, then we
// need do nothing -- there is a next result, and we shouldn't move forward. // need do nothing -- there is a next result, and we shouldn't move forward.
// However, we then need to get the next result from our last Contains(). // However, we then need to get the next result from our last Contains().
// //
// The upshot is, the end of NextResult() bubbles up from the bottom of the // The upshot is, the end of NextPath() bubbles up from the bottom of the
// iterator tree up, and we need to respect that. // iterator tree up, and we need to respect that.
if it.primaryIt.NextResult() { if it.primaryIt.NextPath() {
return true return true
} }
return it.NextContains() return it.NextContains()
} }
// Get the next result from this iterator. This is simpler than Contains. We have a // Next advances the iterator. This is simpler than Contains. We have a
// subiterator we can get a value from, and we can take that resultant triple, // subiterator we can get a value from, and we can take that resultant triple,
// pull our direction out of it, and return that. // pull our direction out of it, and return that.
func (it *HasA) Next() (graph.Value, bool) { func (it *HasA) Next() bool {
graph.NextLogIn(it) graph.NextLogIn(it)
if it.resultIt != nil { if it.resultIt != nil {
it.resultIt.Close() it.resultIt.Close()
} }
it.resultIt = &Null{} it.resultIt = &Null{}
tID, ok := graph.Next(it.primaryIt) if !graph.Next(it.primaryIt) {
if !ok {
return graph.NextLogOut(it, 0, false) return graph.NextLogOut(it, 0, false)
} }
tID := it.primaryIt.Result()
name := it.ts.Quad(tID).Get(it.dir) name := it.ts.Quad(tID).Get(it.dir)
val := it.ts.ValueOf(name) val := it.ts.ValueOf(name)
it.result = val it.result = val

View file

@ -79,8 +79,8 @@ func (it *Null) DebugString(indent int) string {
return strings.Repeat(" ", indent) + "(null)" return strings.Repeat(" ", indent) + "(null)"
} }
func (it *Null) Next() (graph.Value, bool) { func (it *Null) Next() bool {
return nil, false return false
} }
func (it *Null) Result() graph.Value { func (it *Null) Result() graph.Value {
@ -95,7 +95,7 @@ func (it *Null) SubIterators() []graph.Iterator {
return nil return nil
} }
func (it *Null) NextResult() bool { func (it *Null) NextPath() bool {
return false return false
} }

View file

@ -153,24 +153,24 @@ func (it *LinksTo) Optimize() (graph.Iterator, bool) {
} }
// Next()ing a LinksTo operates as described above. // Next()ing a LinksTo operates as described above.
func (it *LinksTo) Next() (graph.Value, bool) { func (it *LinksTo) Next() bool {
graph.NextLogIn(it) graph.NextLogIn(it)
val, ok := graph.Next(it.nextIt) if graph.Next(it.nextIt) {
if !ok { it.result = it.nextIt.Result()
return graph.NextLogOut(it, it.nextIt, true)
}
// Subiterator is empty, get another one // Subiterator is empty, get another one
candidate, ok := graph.Next(it.primaryIt) if !graph.Next(it.primaryIt) {
if !ok {
// We're out of nodes in our subiterator, so we're done as well. // We're out of nodes in our subiterator, so we're done as well.
return graph.NextLogOut(it, 0, false) return graph.NextLogOut(it, 0, false)
} }
it.nextIt.Close() it.nextIt.Close()
it.nextIt = it.ts.TripleIterator(it.dir, candidate) it.nextIt = it.ts.TripleIterator(it.dir, it.primaryIt.Result())
// Recurse -- return the first in the next set. // Recurse -- return the first in the next set.
return it.Next() return it.Next()
} }
it.result = val
return graph.NextLogOut(it, val, ok)
}
func (it *LinksTo) Result() graph.Value { func (it *LinksTo) Result() graph.Value {
return it.result return it.result
@ -183,8 +183,8 @@ func (it *LinksTo) Close() {
} }
// We won't ever have a new result, but our subiterators might. // We won't ever have a new result, but our subiterators might.
func (it *LinksTo) NextResult() bool { func (it *LinksTo) NextPath() bool {
return it.primaryIt.NextResult() return it.primaryIt.NextPath()
} }
// Register the LinksTo. // Register the LinksTo.

View file

@ -33,10 +33,10 @@ func TestLinksTo(t *testing.T) {
} }
fixed.Add(val) fixed.Add(val)
lto := NewLinksTo(ts, fixed, quad.Object) lto := NewLinksTo(ts, fixed, quad.Object)
val, ok := lto.Next() if !lto.Next() {
if !ok {
t.Error("At least one triple matches the fixed object") t.Error("At least one triple matches the fixed object")
} }
val = lto.Result()
if val != 2 { if val != 2 {
t.Errorf("Quad index 2, such as %s, should match %s", ts.Quad(2), ts.Quad(val)) t.Errorf("Quad index 2, such as %s, should match %s", ts.Quad(2), ts.Quad(val))
} }

View file

@ -32,6 +32,10 @@ type result struct {
tags map[string]graph.Value tags map[string]graph.Value
} }
// Keyer provides a method for comparing types that are not otherwise comparable.
// The Key method must return a dynamic type that is comparable according to the
// Go language specification. The returned value must be unique for each receiver
// value.
type Keyer interface { type Keyer interface {
Key() interface{} Key() interface{}
} }
@ -179,7 +183,7 @@ func (it *Materialize) Stats() graph.IteratorStats {
} }
} }
func (it *Materialize) Next() (graph.Value, bool) { func (it *Materialize) Next() bool {
graph.NextLogIn(it) graph.NextLogIn(it)
if !it.hasRun { if !it.hasRun {
it.materializeSet() it.materializeSet()
@ -216,12 +220,12 @@ func (it *Materialize) Contains(v graph.Value) bool {
return graph.ContainsLogOut(it, v, false) return graph.ContainsLogOut(it, v, false)
} }
func (it *Materialize) NextResult() bool { func (it *Materialize) NextPath() bool {
if !it.hasRun { if !it.hasRun {
it.materializeSet() it.materializeSet()
} }
if it.aborted { if it.aborted {
return it.subIt.NextResult() return it.subIt.NextPath()
} }
it.subindex++ it.subindex++
@ -235,16 +239,13 @@ func (it *Materialize) NextResult() bool {
func (it *Materialize) materializeSet() { func (it *Materialize) materializeSet() {
i := 0 i := 0
for { for graph.Next(it.subIt) {
id, ok := graph.Next(it.subIt) i++
if !ok {
break
}
i += 1
if i > abortMaterializeAt { if i > abortMaterializeAt {
it.aborted = true it.aborted = true
break break
} }
id := it.subIt.Result()
val := id val := id
if h, ok := id.(Keyer); ok { if h, ok := id.(Keyer); ok {
val = h.Key() val = h.Key()
@ -257,7 +258,7 @@ func (it *Materialize) materializeSet() {
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
it.subIt.TagResults(tags) it.subIt.TagResults(tags)
it.values[index] = append(it.values[index], result{id: id, tags: tags}) it.values[index] = append(it.values[index], result{id: id, tags: tags})
for it.subIt.NextResult() == true { for it.subIt.NextPath() {
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
it.subIt.TagResults(tags) it.subIt.TagResults(tags)
it.values[index] = append(it.values[index], result{id: id, tags: tags}) it.values[index] = append(it.values[index], result{id: id, tags: tags})

View file

@ -51,8 +51,6 @@ func NewOptional(it graph.Iterator) *Optional {
} }
} }
func (it *Optional) CanNext() bool { return false }
func (it *Optional) UID() uint64 { func (it *Optional) UID() uint64 {
return it.uid return it.uid
} }
@ -88,9 +86,9 @@ func (it *Optional) Result() graph.Value {
// An optional iterator only has a next result if, (a) last time we checked // An optional iterator only has a next result if, (a) last time we checked
// we had any results whatsoever, and (b) there was another subresult in our // we had any results whatsoever, and (b) there was another subresult in our
// optional subbranch. // optional subbranch.
func (it *Optional) NextResult() bool { func (it *Optional) NextPath() bool {
if it.lastCheck { if it.lastCheck {
return it.subIt.NextResult() return it.subIt.NextPath()
} }
return false return false
} }

View file

@ -141,36 +141,35 @@ func (it *Or) AddSubIterator(sub graph.Iterator) {
it.itCount++ it.itCount++
} }
// Returns the Next value from the Or graph.iterator. Because the Or is the // Next advances the Or graph.iterator. Because the Or is the union of its
// union of its subiterators, it must produce from all subiterators -- unless // subiterators, it must produce from all subiterators -- unless it it
// it's shortcircuiting, in which case, it's the first one that returns anything. // shortcircuiting, in which case, it is the first one that returns anything.
func (it *Or) Next() (graph.Value, bool) { func (it *Or) Next() bool {
graph.NextLogIn(it) graph.NextLogIn(it)
var curr graph.Value var first bool
var exists bool
firstTime := false
for { for {
if it.currentIterator == -1 { if it.currentIterator == -1 {
it.currentIterator = 0 it.currentIterator = 0
firstTime = true first = true
} }
curIt := it.internalIterators[it.currentIterator] curIt := it.internalIterators[it.currentIterator]
curr, exists = graph.Next(curIt)
if !exists { if graph.Next(curIt) {
if it.isShortCircuiting && !firstTime { it.result = curIt.Result()
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, it.result, true)
}
if it.isShortCircuiting && !first {
break
} }
it.currentIterator++ it.currentIterator++
if it.currentIterator == it.itCount { if it.currentIterator == it.itCount {
break
}
}
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, nil, false)
} }
} else {
it.result = curr
return graph.NextLogOut(it, curr, true)
}
}
panic("unreachable")
}
func (it *Or) Result() graph.Value { func (it *Or) Result() graph.Value {
return it.result return it.result
@ -228,13 +227,13 @@ func (it *Or) Size() (int64, bool) {
return val, b return val, b
} }
// An Or has no NextResult of its own -- that is, there are no other values // An Or has no NextPath of its own -- that is, there are no other values
// which satisfy our previous result that are not the result itself. Our // which satisfy our previous result that are not the result itself. Our
// subiterators might, however, so just pass the call recursively. In the case of // subiterators might, however, so just pass the call recursively. In the case of
// shortcircuiting, only allow new results from the currently checked graph.iterator // shortcircuiting, only allow new results from the currently checked graph.iterator
func (it *Or) NextResult() bool { func (it *Or) NextPath() bool {
if it.currentIterator != -1 { if it.currentIterator != -1 {
return it.internalIterators[it.currentIterator].NextResult() return it.internalIterators[it.currentIterator].NextPath()
} }
return false return false
} }

View file

@ -23,12 +23,8 @@ import (
func iterated(it graph.Iterator) []int { func iterated(it graph.Iterator) []int {
var res []int var res []int
for { for graph.Next(it) {
val, ok := graph.Next(it) res = append(res, it.Result().(int))
if !ok {
break
}
res = append(res, val.(int))
} }
return res return res
} }

View file

@ -129,12 +129,8 @@ func (qs *queryShape) MakeNode(it graph.Iterator) *Node {
} }
case graph.Fixed: case graph.Fixed:
n.IsFixed = true n.IsFixed = true
for { for graph.Next(it) {
val, more := graph.Next(it) n.Values = append(n.Values, qs.ts.NameOf(it.Result()))
if !more {
break
}
n.Values = append(n.Values, qs.ts.NameOf(val))
} }
case graph.HasA: case graph.HasA:
hasa := it.(*HasA) hasa := it.(*HasA)

View file

@ -127,20 +127,15 @@ func (it *Comparison) Clone() graph.Iterator {
return out return out
} }
func (it *Comparison) Next() (graph.Value, bool) { func (it *Comparison) Next() bool {
var val graph.Value for graph.Next(it.subIt) {
var ok bool val := it.subIt.Result()
for {
val, ok = graph.Next(it.subIt)
if !ok {
return nil, false
}
if it.doComparison(val) { if it.doComparison(val) {
break
}
}
it.result = val it.result = val
return val, ok return true
}
}
return false
} }
// DEPRECATED // DEPRECATED
@ -152,9 +147,9 @@ func (it *Comparison) Result() graph.Value {
return it.result return it.result
} }
func (it *Comparison) NextResult() bool { func (it *Comparison) NextPath() bool {
for { for {
hasNext := it.subIt.NextResult() hasNext := it.subIt.NextPath()
if !hasNext { if !hasNext {
return false return false
} }

View file

@ -69,12 +69,8 @@ func TestValueComparison(t *testing.T) {
vc := NewComparison(simpleFixedIterator(), test.operator, test.operand, ts) vc := NewComparison(simpleFixedIterator(), test.operator, test.operand, ts)
var got []string var got []string
for { for vc.Next() {
val, ok := vc.Next() got = append(got, ts.NameOf(vc.Result()))
if !ok {
break
}
got = append(got, ts.NameOf(val))
} }
if !reflect.DeepEqual(got, test.expect) { if !reflect.DeepEqual(got, test.expect) {
t.Errorf("Failed to show %s, got:%q expect:%q", test.message, got, test.expect) t.Errorf("Failed to show %s, got:%q expect:%q", test.message, got, test.expect)

View file

@ -101,10 +101,10 @@ func (it *AllIterator) Clone() graph.Iterator {
return out return out
} }
func (it *AllIterator) Next() (graph.Value, bool) { func (it *AllIterator) Next() bool {
if !it.open { if !it.open {
it.result = nil it.result = nil
return nil, false return false
} }
var out []byte var out []byte
out = make([]byte, len(it.iter.Key())) out = make([]byte, len(it.iter.Key()))
@ -115,10 +115,10 @@ func (it *AllIterator) Next() (graph.Value, bool) {
} }
if !bytes.HasPrefix(out, it.prefix) { if !bytes.HasPrefix(out, it.prefix) {
it.Close() it.Close()
return nil, false return false
} }
it.result = Token(out) it.result = Token(out)
return it.result, true return true
} }
func (it *AllIterator) ResultTree() *graph.ResultTree { func (it *AllIterator) ResultTree() *graph.ResultTree {
@ -129,7 +129,7 @@ func (it *AllIterator) Result() graph.Value {
return it.result return it.result
} }
func (it *AllIterator) NextResult() bool { func (it *AllIterator) NextPath() bool {
return false return false
} }

View file

@ -117,19 +117,19 @@ func (it *Iterator) Close() {
} }
} }
func (it *Iterator) Next() (graph.Value, bool) { func (it *Iterator) Next() bool {
if it.iter == nil { if it.iter == nil {
it.result = nil it.result = nil
return nil, false return false
} }
if !it.open { if !it.open {
it.result = nil it.result = nil
return nil, false return false
} }
if !it.iter.Valid() { if !it.iter.Valid() {
it.result = nil it.result = nil
it.Close() it.Close()
return nil, false return false
} }
if bytes.HasPrefix(it.iter.Key(), it.nextPrefix) { if bytes.HasPrefix(it.iter.Key(), it.nextPrefix) {
out := make([]byte, len(it.iter.Key())) out := make([]byte, len(it.iter.Key()))
@ -139,11 +139,11 @@ func (it *Iterator) Next() (graph.Value, bool) {
if !ok { if !ok {
it.Close() it.Close()
} }
return Token(out), true return true
} }
it.Close() it.Close()
it.result = nil it.result = nil
return nil, false return false
} }
func (it *Iterator) ResultTree() *graph.ResultTree { func (it *Iterator) ResultTree() *graph.ResultTree {
@ -154,7 +154,7 @@ func (it *Iterator) Result() graph.Value {
return it.result return it.result
} }
func (it *Iterator) NextResult() bool { func (it *Iterator) NextPath() bool {
return false return false
} }

View file

@ -45,12 +45,8 @@ func makeTripleSet() []quad.Quad {
func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []quad.Quad { func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []quad.Quad {
var res ordered var res ordered
for { for graph.Next(it) {
val, ok := graph.Next(it) res = append(res, qs.Quad(it.Result()))
if !ok {
break
}
res = append(res, qs.Quad(val))
} }
sort.Sort(res) sort.Sort(res)
return res return res
@ -85,12 +81,8 @@ func (o ordered) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
func iteratedNames(qs graph.TripleStore, it graph.Iterator) []string { func iteratedNames(qs graph.TripleStore, it graph.Iterator) []string {
var res []string var res []string
for { for graph.Next(it) {
val, ok := graph.Next(it) res = append(res, qs.NameOf(it.Result()))
if !ok {
break
}
res = append(res, qs.NameOf(val))
} }
sort.Strings(res) sort.Strings(res)
return res return res
@ -266,8 +258,8 @@ func TestIterator(t *testing.T) {
it.Reset() it.Reset()
it = qs.TriplesAllIterator() it = qs.TriplesAllIterator()
edge, _ := graph.Next(it) graph.Next(it)
triple := qs.Quad(edge) triple := qs.Quad(it.Result())
set := makeTripleSet() set := makeTripleSet()
var ok bool var ok bool
for _, t := range set { for _, t := range set {

View file

@ -37,10 +37,10 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo
if primary.Type() == graph.Fixed { if primary.Type() == graph.Fixed {
size, _ := primary.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := graph.Next(primary) if !graph.Next(primary) {
if !ok { panic("unexpected size during optimize")
panic("Sizes lie")
} }
val := primary.Result()
newIt := ts.TripleIterator(it.Direction(), val) newIt := ts.TripleIterator(it.Direction(), val)
nt := newIt.Tagger() nt := newIt.Tagger()
nt.CopyFrom(it) nt.CopyFrom(it)

View file

@ -36,15 +36,13 @@ func (it *AllIterator) SubIterators() []graph.Iterator {
return nil return nil
} }
func (it *AllIterator) Next() (graph.Value, bool) { func (it *AllIterator) Next() bool {
next, out := it.Int64.Next() if !it.Int64.Next() {
if !out { return false
return next, out
} }
i64 := next.(int64) _, ok := it.ts.revIdMap[it.Int64.Result().(int64)]
_, ok := it.ts.revIdMap[i64]
if !ok { if !ok {
return it.Next() return it.Next()
} }
return next, out return true
} }

View file

@ -93,7 +93,7 @@ func (it *Iterator) Clone() graph.Iterator {
func (it *Iterator) Close() {} func (it *Iterator) Close() {}
func (it *Iterator) Next() (graph.Value, bool) { func (it *Iterator) Next() bool {
graph.NextLogIn(it) graph.NextLogIn(it)
if it.tree.Max() == nil || it.result == int64(it.tree.Max().(Int64)) { if it.tree.Max() == nil || it.result == int64(it.tree.Max().(Int64)) {
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, nil, false)
@ -111,7 +111,7 @@ func (it *Iterator) Result() graph.Value {
return it.result return it.result
} }
func (it *Iterator) NextResult() bool { func (it *Iterator) NextPath() bool {
return false return false
} }

View file

@ -132,11 +132,8 @@ func (ts *TripleStore) tripleExists(t quad.Quad) (bool, int64) {
} }
it := NewLlrbIterator(smallest_tree, "") it := NewLlrbIterator(smallest_tree, "")
for { for it.Next() {
val, ok := it.Next() val := it.Result()
if !ok {
break
}
if t == ts.triples[val.(int64)] { if t == ts.triples[val.(int64)] {
return true, val.(int64) return true, val.(int64)
} }

View file

@ -37,10 +37,10 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo
if primary.Type() == graph.Fixed { if primary.Type() == graph.Fixed {
size, _ := primary.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := graph.Next(primary) if !graph.Next(primary) {
if !ok { panic("unexpected size during optimize")
panic("Sizes lie")
} }
val := primary.Result()
newIt := ts.TripleIterator(it.Direction(), val) newIt := ts.TripleIterator(it.Direction(), val)
nt := newIt.Tagger() nt := newIt.Tagger()
nt.CopyFrom(it) nt.CopyFrom(it)

View file

@ -114,10 +114,10 @@ func TestIteratorsAndNextResultOrderA(t *testing.T) {
outerAnd.AddSubIterator(fixed) outerAnd.AddSubIterator(fixed)
outerAnd.AddSubIterator(hasa) outerAnd.AddSubIterator(hasa)
val, ok := outerAnd.Next() if !outerAnd.Next() {
if !ok {
t.Error("Expected one matching subtree") t.Error("Expected one matching subtree")
} }
val := outerAnd.Result()
if ts.NameOf(val) != "C" { if ts.NameOf(val) != "C" {
t.Errorf("Matching subtree should be %s, got %s", "barak", ts.NameOf(val)) t.Errorf("Matching subtree should be %s, got %s", "barak", ts.NameOf(val))
} }
@ -128,7 +128,7 @@ func TestIteratorsAndNextResultOrderA(t *testing.T) {
) )
for { for {
got = append(got, ts.NameOf(all.Result())) got = append(got, ts.NameOf(all.Result()))
if !outerAnd.NextResult() { if !outerAnd.NextPath() {
break break
} }
} }
@ -138,8 +138,7 @@ func TestIteratorsAndNextResultOrderA(t *testing.T) {
t.Errorf("Unexpected result, got:%q expect:%q", got, expect) t.Errorf("Unexpected result, got:%q expect:%q", got, expect)
} }
val, ok = outerAnd.Next() if outerAnd.Next() {
if ok {
t.Error("More than one possible top level output?") t.Error("More than one possible top level output?")
} }
} }
@ -190,8 +189,7 @@ func TestRemoveTriple(t *testing.T) {
hasa := iterator.NewHasA(ts, innerAnd, quad.Object) hasa := iterator.NewHasA(ts, innerAnd, quad.Object)
newIt, _ := hasa.Optimize() newIt, _ := hasa.Optimize()
_, ok := graph.Next(newIt) if graph.Next(newIt) {
if ok {
t.Error("E should not have any followers.") t.Error("E should not have any followers.")
} }
} }

View file

@ -138,7 +138,7 @@ func (it *Iterator) Clone() graph.Iterator {
return m return m
} }
func (it *Iterator) Next() (graph.Value, bool) { func (it *Iterator) Next() bool {
var result struct { var result struct {
Id string "_id" Id string "_id"
//Sub string "Sub" //Sub string "Sub"
@ -151,10 +151,10 @@ func (it *Iterator) Next() (graph.Value, bool) {
if err != nil { if err != nil {
glog.Errorln("Error Nexting Iterator: ", err) glog.Errorln("Error Nexting Iterator: ", err)
} }
return nil, false return false
} }
it.result = result.Id it.result = result.Id
return result.Id, true return true
} }
func (it *Iterator) ResultTree() *graph.ResultTree { func (it *Iterator) ResultTree() *graph.ResultTree {
@ -165,7 +165,7 @@ func (it *Iterator) Result() graph.Value {
return it.result return it.result
} }
func (it *Iterator) NextResult() bool { func (it *Iterator) NextPath() bool {
return false return false
} }

View file

@ -37,10 +37,10 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo
if primary.Type() == graph.Fixed { if primary.Type() == graph.Fixed {
size, _ := primary.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := graph.Next(primary) if !graph.Next(primary) {
if !ok { panic("unexpected size during optimize")
panic("Sizes lie")
} }
val := primary.Result()
newIt := ts.TripleIterator(it.Direction(), val) newIt := ts.TripleIterator(it.Direction(), val)
nt := newIt.Tagger() nt := newIt.Tagger()
nt.CopyFrom(it) nt.CopyFrom(it)

View file

@ -14,7 +14,10 @@
package graph package graph
import "fmt" import (
"bytes"
"fmt"
)
type ResultTree struct { type ResultTree struct {
result Value result Value
@ -26,14 +29,13 @@ func NewResultTree(result Value) *ResultTree {
} }
func (t *ResultTree) String() string { func (t *ResultTree) String() string {
base := fmt.Sprintf("(%d", t.result) var buf bytes.Buffer
if len(t.subtrees) != 0 { fmt.Fprintf(&buf, "(%d", t.result)
for _, sub := range t.subtrees { for _, sub := range t.subtrees {
base += fmt.Sprintf(" %s", sub) fmt.Fprintf(&buf, " %s", sub)
} }
} buf.WriteByte(')')
base += ")" return buf.String()
return base
} }
func (t *ResultTree) AddSubtree(sub *ResultTree) { func (t *ResultTree) AddSubtree(sub *ResultTree) {
@ -41,22 +43,15 @@ func (t *ResultTree) AddSubtree(sub *ResultTree) {
} }
func StringResultTreeEvaluator(it Nexter) string { func StringResultTreeEvaluator(it Nexter) string {
ok := true var buf bytes.Buffer
out := "" for it.Next() {
for { fmt.Fprintln(&buf, it.ResultTree())
_, ok = it.Next() for it.NextPath() {
if !ok { buf.WriteByte(' ')
break fmt.Fprintln(&buf, it.ResultTree())
}
out += it.ResultTree().String()
out += "\n"
for it.NextResult() == true {
out += " "
out += it.ResultTree().String()
out += "\n"
} }
} }
return out return buf.String()
} }
func PrintResultTreeEvaluator(it Nexter) { func PrintResultTreeEvaluator(it Nexter) {

View file

@ -153,8 +153,7 @@ func runIteratorToArray(it graph.Iterator, ses *Session, limit int) []map[string
return nil return nil
default: default:
} }
_, ok := graph.Next(it) if !graph.Next(it) {
if !ok {
break break
} }
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
@ -164,7 +163,7 @@ func runIteratorToArray(it graph.Iterator, ses *Session, limit int) []map[string
if limit >= 0 && count >= limit { if limit >= 0 && count >= limit {
break break
} }
for it.NextResult() == true { for it.NextPath() {
select { select {
case <-ses.kill: case <-ses.kill:
return nil return nil
@ -193,11 +192,10 @@ func runIteratorToArrayNoTags(it graph.Iterator, ses *Session, limit int) []stri
return nil return nil
default: default:
} }
val, ok := graph.Next(it) if !graph.Next(it) {
if !ok {
break break
} }
output = append(output, ses.ts.NameOf(val)) output = append(output, ses.ts.NameOf(it.Result()))
count++ count++
if limit >= 0 && count >= limit { if limit >= 0 && count >= limit {
break break
@ -216,8 +214,7 @@ func runIteratorWithCallback(it graph.Iterator, ses *Session, callback otto.Valu
return return
default: default:
} }
_, ok := graph.Next(it) if !graph.Next(it) {
if !ok {
break break
} }
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
@ -228,7 +225,7 @@ func runIteratorWithCallback(it graph.Iterator, ses *Session, callback otto.Valu
if limit >= 0 && count >= limit { if limit >= 0 && count >= limit {
break break
} }
for it.NextResult() == true { for it.NextPath() {
select { select {
case <-ses.kill: case <-ses.kill:
return return
@ -260,8 +257,7 @@ func runIteratorOnSession(it graph.Iterator, ses *Session) {
return return
default: default:
} }
_, ok := graph.Next(it) if !graph.Next(it) {
if !ok {
break break
} }
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
@ -269,7 +265,7 @@ func runIteratorOnSession(it graph.Iterator, ses *Session) {
if !ses.SendResult(&Result{actualResults: &tags}) { if !ses.SendResult(&Result{actualResults: &tags}) {
break break
} }
for it.NextResult() == true { for it.NextPath() {
select { select {
case <-ses.kill: case <-ses.kill:
return return

View file

@ -88,15 +88,11 @@ func (s *Session) ExecInput(input string, c chan interface{}, limit int) {
if glog.V(2) { if glog.V(2) {
glog.V(2).Infoln(it.DebugString(0)) glog.V(2).Infoln(it.DebugString(0))
} }
for { for graph.Next(it) {
_, ok := graph.Next(it)
if !ok {
break
}
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
it.TagResults(tags) it.TagResults(tags)
c <- tags c <- tags
for it.NextResult() == true { for it.NextPath() == true {
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
it.TagResults(tags) it.TagResults(tags)
c <- tags c <- tags

View file

@ -67,10 +67,10 @@ func TestMemstoreBackedSexp(t *testing.T) {
if it.Type() != test.typ { if it.Type() != test.typ {
t.Errorf("Incorrect type for %s, got:%q expect %q", test.message, it.Type(), test.expect) t.Errorf("Incorrect type for %s, got:%q expect %q", test.message, it.Type(), test.expect)
} }
got, ok := graph.Next(it) if !graph.Next(it) {
if !ok {
t.Errorf("Failed to %s", test.message) t.Errorf("Failed to %s", test.message)
} }
got := it.Result()
if expect := ts.ValueOf(test.expect); got != expect { if expect := ts.ValueOf(test.expect); got != expect {
t.Errorf("Incorrect result for %s, got:%v expect %v", test.message, got, expect) t.Errorf("Incorrect result for %s, got:%v expect %v", test.message, got, expect)
} }
@ -88,10 +88,10 @@ func TestTreeConstraintParse(t *testing.T) {
if it.Type() != graph.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 := graph.Next(it) if !graph.Next(it) {
if !ok {
t.Error("Got no results") t.Error("Got no results")
} }
out := it.Result()
if out != ts.ValueOf("i") { if out != ts.ValueOf("i") {
t.Errorf("Got %d, expected %d", out, ts.ValueOf("i")) t.Errorf("Got %d, expected %d", out, ts.ValueOf("i"))
} }
@ -105,8 +105,7 @@ func TestTreeConstraintTagParse(t *testing.T) {
"(:like\n" + "(:like\n" +
"($a (:is :good))))" "($a (:is :good))))"
it := BuildIteratorTreeForQuery(ts, query) it := BuildIteratorTreeForQuery(ts, query)
_, ok := graph.Next(it) if !graph.Next(it) {
if !ok {
t.Error("Got no results") t.Error("Got no results")
} }
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
@ -135,15 +134,14 @@ func TestMultipleConstraintParse(t *testing.T) {
if it.Type() != graph.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 := graph.Next(it) if !graph.Next(it) {
if !ok {
t.Error("Got no results") t.Error("Got no results")
} }
out := it.Result()
if out != ts.ValueOf("i") { if out != ts.ValueOf("i") {
t.Errorf("Got %d, expected %d", out, ts.ValueOf("i")) t.Errorf("Got %d, expected %d", out, ts.ValueOf("i"))
} }
_, ok = graph.Next(it) if graph.Next(it) {
if ok {
t.Error("Too many results") t.Error("Too many results")
} }
} }

View file

@ -77,11 +77,7 @@ func (s *Session) ExecInput(input string, out chan interface{}, limit int) {
fmt.Println(it.DebugString(0)) fmt.Println(it.DebugString(0))
} }
nResults := 0 nResults := 0
for { for graph.Next(it) {
_, ok := graph.Next(it)
if !ok {
break
}
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
it.TagResults(tags) it.TagResults(tags)
out <- &tags out <- &tags
@ -89,7 +85,7 @@ func (s *Session) ExecInput(input string, out chan interface{}, limit int) {
if nResults > limit && limit != -1 { if nResults > limit && limit != -1 {
break break
} }
for it.NextResult() == true { for it.NextPath() == true {
tags := make(map[string]graph.Value) tags := make(map[string]graph.Value)
it.TagResults(tags) it.TagResults(tags)
out <- &tags out <- &tags