Base nexting on interface satisfaction

This is done unsubtlely at the moment and there is plenty of room for
optimisation of assertion location to prevent repeated reasserting as is
done now.
This commit is contained in:
kortschak 2014-07-30 16:06:46 +09:30
parent b498a06a7b
commit d6f94be514
27 changed files with 57 additions and 82 deletions

View file

@ -14,8 +14,7 @@
package graph package graph
// Define the general iterator interface, as well as the Base iterator which all // Define the general iterator interface.
// iterators can "inherit" from to get default iterator functionality.
import ( import (
"strings" "strings"
@ -86,19 +85,10 @@ type Iterator interface {
// 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.
// //
// Next() advances the iterator and returns the next valid result. Returns
// (<value>, true) or (nil, false)
Next() (Value, bool)
// NextResult() advances iterators that may have more than one valid result, // NextResult() advances iterators that may have more than one valid result,
// from the bottom up. // from the bottom up.
NextResult() bool NextResult() bool
// Return whether this iterator is reliably nextable. Most iterators are.
// However, some iterators, like "not" are, by definition, the whole database
// except themselves. Next() on these is unproductive, if impossible.
CanNext() bool
// Check(), given a value, returns whether or not that value is within the set // Check(), given a value, returns whether or not that value is within the set
// held by this iterator. // held by this iterator.
Check(Value) bool Check(Value) bool
@ -145,6 +135,25 @@ type Iterator interface {
UID() uint64 UID() uint64
} }
type Nexter interface {
// Next() advances the iterator and returns the next valid result. Returns
// (<value>, true) or (nil, false)
Next() (Value, bool)
Iterator
}
// 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
// return a nil Value and false.
func Next(it Iterator) (Value, bool) {
if n, ok := it.(Nexter); ok {
return n.Next()
}
glog.Errorln("Nexting an un-nextable iterator")
return nil, false
}
// FixedIterator wraps iterators that are modifiable by addition of fixed value sets. // FixedIterator wraps iterators that are modifiable by addition of fixed value sets.
type FixedIterator interface { type FixedIterator interface {
Iterator Iterator

View file

@ -31,7 +31,6 @@ import (
// An All iterator across a range of int64 values, from `max` to `min`. // An All iterator across a range of int64 values, from `max` to `min`.
type Int64 struct { type Int64 struct {
Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
max, min int64 max, min int64

View file

@ -22,10 +22,9 @@ import (
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
) )
// The And iterator. Consists of a Base and a number of subiterators, the primary of which will // The And iterator. Consists of a number of subiterators, the primary of which will
// be Next()ed if next is called. // be Next()ed if next is called.
type And struct { type And struct {
Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
internalIterators []graph.Iterator internalIterators []graph.Iterator
@ -60,7 +59,7 @@ func (it *And) Tagger() *graph.Tagger {
return &it.tags return &it.tags
} }
// Overrides Base TagResults, as it needs to add it's own results and // An extended TagResults, as it needs to add it's own results and
// recurse down it's subiterators. // recurse down it's subiterators.
func (it *And) TagResults(dst map[string]graph.Value) { func (it *And) TagResults(dst map[string]graph.Value) {
for _, tag := range it.tags.Tags() { for _, tag := range it.tags.Tags() {
@ -161,7 +160,7 @@ func (it *And) Next() (graph.Value, bool) {
var curr graph.Value var curr graph.Value
var exists bool var exists bool
for { for {
curr, exists = it.primaryIt.Next() curr, exists = graph.Next(it.primaryIt)
if !exists { if !exists {
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, nil, false)
} }

View file

@ -145,14 +145,14 @@ func optimizeOrder(its []graph.Iterator) []graph.Iterator {
// all of it's contents, and to Check() each of those against everyone // all of it's contents, and to Check() each of those against everyone
// else. // else.
for _, it := range its { for _, it := range its {
if !it.CanNext() { if _, canNext := it.(graph.Nexter); !canNext {
bad = append(bad, it) bad = append(bad, it)
continue continue
} }
rootStats := it.Stats() rootStats := it.Stats()
cost := rootStats.NextCost cost := rootStats.NextCost
for _, f := range its { for _, f := range its {
if !f.CanNext() { if _, canNext := it.(graph.Nexter); !canNext {
continue continue
} }
if f == it { if f == it {
@ -177,7 +177,7 @@ func optimizeOrder(its []graph.Iterator) []graph.Iterator {
// ... push everyone else after... // ... push everyone else after...
for _, it := range its { for _, it := range its {
if !it.CanNext() { if _, canNext := it.(graph.Nexter); !canNext {
continue continue
} }
if it != best { if it != best {

View file

@ -30,7 +30,6 @@ import (
// A Fixed iterator consists of it's values, an index (where it is in the process of Next()ing) and // A Fixed iterator consists of it's values, an index (where it is in the process of Next()ing) and
// an equality function. // an equality function.
type Fixed struct { type Fixed struct {
Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
values []graph.Value values []graph.Value

View file

@ -46,7 +46,6 @@ import (
// a primary subiterator, a direction in which the triples for that subiterator point, // a primary subiterator, a direction in which the triples for that subiterator point,
// and a temporary holder for the iterator generated on Check(). // and a temporary holder for the iterator generated on Check().
type HasA struct { type HasA struct {
Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
ts graph.TripleStore ts graph.TripleStore
@ -159,7 +158,7 @@ func (it *HasA) Check(val graph.Value) bool {
// another match is made. // another match is made.
func (it *HasA) GetCheckResult() bool { func (it *HasA) GetCheckResult() bool {
for { for {
linkVal, ok := it.resultIt.Next() linkVal, ok := graph.Next(it.resultIt)
if !ok { if !ok {
break break
} }
@ -198,7 +197,7 @@ func (it *HasA) Next() (graph.Value, bool) {
} }
it.resultIt = &Null{} it.resultIt = &Null{}
tID, ok := it.primaryIt.Next() tID, ok := graph.Next(it.primaryIt)
if !ok { if !ok {
return graph.NextLogOut(it, 0, false) return graph.NextLogOut(it, 0, false)
} }

View file

@ -14,8 +14,7 @@
package iterator package iterator
// Define the general iterator interface, as well as the Base which all // Define the general iterator interface.
// iterators can "inherit" from to get default iterator functionality.
import ( import (
"strings" "strings"
@ -30,14 +29,6 @@ func NextUID() uint64 {
return atomic.AddUint64(&nextIteratorID, 1) - 1 return atomic.AddUint64(&nextIteratorID, 1) - 1
} }
// The Base iterator is the iterator other iterators inherit from to get some
// default functionality.
type Base struct {
}
// Accessor
func (it *Base) CanNext() bool { return true }
// Here we define the simplest iterator -- the Null iterator. It contains nothing. // Here we define the simplest iterator -- the Null iterator. It contains nothing.
// It is the empty set. Often times, queries that contain one of these match nothing, // It is the empty set. Often times, queries that contain one of these match nothing,
// so it's important to give it a special iterator. // so it's important to give it a special iterator.
@ -88,8 +79,6 @@ func (it *Null) DebugString(indent int) string {
return strings.Repeat(" ", indent) + "(null)" return strings.Repeat(" ", indent) + "(null)"
} }
func (it *Null) CanNext() bool { return true }
func (it *Null) Next() (graph.Value, bool) { func (it *Null) Next() (graph.Value, bool) {
return nil, false return nil, false
} }

View file

@ -40,7 +40,6 @@ import (
// for each node) the subiterator, and the direction the iterator comes from. // for each node) the subiterator, and the direction the iterator comes from.
// `next_it` is the tempoarary iterator held per result in `primary_it`. // `next_it` is the tempoarary iterator held per result in `primary_it`.
type LinksTo struct { type LinksTo struct {
Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
ts graph.TripleStore ts graph.TripleStore
@ -155,10 +154,10 @@ 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() (graph.Value, bool) {
graph.NextLogIn(it) graph.NextLogIn(it)
val, ok := it.nextIt.Next() val, ok := graph.Next(it.nextIt)
if !ok { if !ok {
// Subiterator is empty, get another one // Subiterator is empty, get another one
candidate, ok := it.primaryIt.Next() candidate, ok := graph.Next(it.primaryIt)
if !ok { 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)

View file

@ -30,8 +30,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/barakmich/glog"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
) )
@ -78,16 +76,6 @@ func (it *Optional) Clone() graph.Iterator {
return out return out
} }
// FIXME(kortschak) When we create a Nexter interface the
// following two methods need to go away.
// Nexting the iterator is unsupported -- error and return an empty set.
// (As above, a reasonable alternative would be to Next() an all iterator)
func (it *Optional) Next() (graph.Value, bool) {
glog.Errorln("Nexting an un-nextable iterator")
return nil, false
}
// DEPRECATED // DEPRECATED
func (it *Optional) ResultTree() *graph.ResultTree { func (it *Optional) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result()) return graph.NewResultTree(it.Result())

View file

@ -29,7 +29,6 @@ import (
) )
type Or struct { type Or struct {
Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
isShortCircuiting bool isShortCircuiting bool
@ -156,7 +155,7 @@ func (it *Or) Next() (graph.Value, bool) {
firstTime = true firstTime = true
} }
curIt := it.internalIterators[it.currentIterator] curIt := it.internalIterators[it.currentIterator]
curr, exists = curIt.Next() curr, exists = graph.Next(curIt)
if !exists { if !exists {
if it.isShortCircuiting && !firstTime { if it.isShortCircuiting && !firstTime {
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, nil, false)

View file

@ -24,7 +24,7 @@ import (
func iterated(it graph.Iterator) []int { func iterated(it graph.Iterator) []int {
var res []int var res []int
for { for {
val, ok := it.Next() val, ok := graph.Next(it)
if !ok { if !ok {
break break
} }

View file

@ -129,7 +129,7 @@ func (qs *queryShape) MakeNode(it graph.Iterator) *Node {
case graph.Fixed: case graph.Fixed:
n.IsFixed = true n.IsFixed = true
for { for {
val, more := it.Next() val, more := graph.Next(it)
if !more { if !more {
break break
} }

View file

@ -46,7 +46,6 @@ const (
) )
type Comparison struct { type Comparison struct {
Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
subIt graph.Iterator subIt graph.Iterator
@ -132,7 +131,7 @@ func (it *Comparison) Next() (graph.Value, bool) {
var val graph.Value var val graph.Value
var ok bool var ok bool
for { for {
val, ok = it.subIt.Next() val, ok = graph.Next(it.subIt)
if !ok { if !ok {
return nil, false return nil, false
} }

View file

@ -27,7 +27,6 @@ import (
) )
type AllIterator struct { type AllIterator struct {
iterator.Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
prefix []byte prefix []byte

View file

@ -27,7 +27,6 @@ import (
) )
type Iterator struct { type Iterator struct {
iterator.Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
nextPrefix []byte nextPrefix []byte

View file

@ -45,7 +45,7 @@ func makeTripleSet() []*graph.Triple {
func iteratedTriples(ts graph.TripleStore, it graph.Iterator) []*graph.Triple { func iteratedTriples(ts graph.TripleStore, it graph.Iterator) []*graph.Triple {
var res ordered var res ordered
for { for {
val, ok := it.Next() val, ok := graph.Next(it)
if !ok { if !ok {
break break
} }
@ -85,7 +85,7 @@ func (o ordered) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
func iteratedNames(ts graph.TripleStore, it graph.Iterator) []string { func iteratedNames(ts graph.TripleStore, it graph.Iterator) []string {
var res []string var res []string
for { for {
val, ok := it.Next() val, ok := graph.Next(it)
if !ok { if !ok {
break break
} }
@ -265,7 +265,7 @@ func TestIterator(t *testing.T) {
it.Reset() it.Reset()
it = ts.TriplesAllIterator() it = ts.TriplesAllIterator()
edge, _ := it.Next() edge, _ := graph.Next(it)
triple := ts.Triple(edge) triple := ts.Triple(edge)
set := makeTripleSet() set := makeTripleSet()
var ok bool var ok bool
@ -433,10 +433,10 @@ func TestOptimize(t *testing.T) {
t.Errorf("Optimized iteration does not match original") t.Errorf("Optimized iteration does not match original")
} }
oldIt.Next() graph.Next(oldIt)
oldResults := make(map[string]graph.Value) oldResults := make(map[string]graph.Value)
oldIt.TagResults(oldResults) oldIt.TagResults(oldResults)
newIt.Next() graph.Next(newIt)
newResults := make(map[string]graph.Value) newResults := make(map[string]graph.Value)
newIt.TagResults(newResults) newIt.TagResults(newResults)
if !reflect.DeepEqual(newResults, oldResults) { if !reflect.DeepEqual(newResults, oldResults) {

View file

@ -37,7 +37,7 @@ 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 := primary.Next() val, ok := graph.Next(primary)
if !ok { if !ok {
panic("Sizes lie") panic("Sizes lie")
} }

View file

@ -26,7 +26,6 @@ import (
) )
type Iterator struct { type Iterator struct {
iterator.Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
tree *llrb.LLRB tree *llrb.LLRB

View file

@ -37,7 +37,7 @@ 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 := primary.Next() val, ok := graph.Next(primary)
if !ok { if !ok {
panic("Sizes lie") panic("Sizes lie")
} }

View file

@ -189,7 +189,7 @@ func TestRemoveTriple(t *testing.T) {
hasa := iterator.NewHasA(ts, innerAnd, graph.Object) hasa := iterator.NewHasA(ts, innerAnd, graph.Object)
newIt, _ := hasa.Optimize() newIt, _ := hasa.Optimize()
_, ok := newIt.Next() _, ok := graph.Next(newIt)
if ok { if ok {
t.Error("E should not have any followers.") t.Error("E should not have any followers.")
} }

View file

@ -27,7 +27,6 @@ import (
) )
type Iterator struct { type Iterator struct {
iterator.Base
uid uint64 uid uint64
tags graph.Tagger tags graph.Tagger
ts *TripleStore ts *TripleStore

View file

@ -37,7 +37,7 @@ 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 := primary.Next() val, ok := graph.Next(primary)
if !ok { if !ok {
panic("Sizes lie") panic("Sizes lie")
} }

View file

@ -40,7 +40,7 @@ func (t *ResultTree) AddSubtree(sub *ResultTree) {
t.subtrees = append(t.subtrees, sub) t.subtrees = append(t.subtrees, sub)
} }
func StringResultTreeEvaluator(it Iterator) string { func StringResultTreeEvaluator(it Nexter) string {
ok := true ok := true
out := "" out := ""
for { for {
@ -59,6 +59,6 @@ func StringResultTreeEvaluator(it Iterator) string {
return out return out
} }
func PrintResultTreeEvaluator(it Iterator) { func PrintResultTreeEvaluator(it Nexter) {
fmt.Print(StringResultTreeEvaluator(it)) fmt.Print(StringResultTreeEvaluator(it))
} }

View file

@ -65,7 +65,7 @@ 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 := it.Next() got, ok := graph.Next(it)
if !ok { if !ok {
t.Errorf("Failed to %s", test.message) t.Errorf("Failed to %s", test.message)
} }
@ -86,7 +86,7 @@ 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 := it.Next() out, ok := graph.Next(it)
if !ok { if !ok {
t.Error("Got no results") t.Error("Got no results")
} }
@ -103,7 +103,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 := it.Next() _, ok := graph.Next(it)
if !ok { if !ok {
t.Error("Got no results") t.Error("Got no results")
} }
@ -133,14 +133,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 := it.Next() out, ok := graph.Next(it)
if !ok { if !ok {
t.Error("Got no results") t.Error("Got no results")
} }
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 = it.Next() _, ok = graph.Next(it)
if ok { if ok {
t.Error("Too many results") t.Error("Too many results")
} }

View file

@ -77,7 +77,7 @@ func (s *Session) ExecInput(input string, out chan interface{}, limit int) {
} }
nResults := 0 nResults := 0
for { for {
_, ok := it.Next() _, ok := graph.Next(it)
if !ok { if !ok {
break break
} }

View file

@ -151,7 +151,7 @@ func runIteratorToArray(it graph.Iterator, ses *Session, limit int) []map[string
if ses.doHalt { if ses.doHalt {
return nil return nil
} }
_, ok := it.Next() _, ok := graph.Next(it)
if !ok { if !ok {
break break
} }
@ -187,7 +187,7 @@ func runIteratorToArrayNoTags(it graph.Iterator, ses *Session, limit int) []stri
if ses.doHalt { if ses.doHalt {
return nil return nil
} }
val, ok := it.Next() val, ok := graph.Next(it)
if !ok { if !ok {
break break
} }
@ -208,7 +208,7 @@ func runIteratorWithCallback(it graph.Iterator, ses *Session, callback otto.Valu
if ses.doHalt { if ses.doHalt {
return return
} }
_, ok := it.Next() _, ok := graph.Next(it)
if !ok { if !ok {
break break
} }
@ -249,7 +249,7 @@ func runIteratorOnSession(it graph.Iterator, ses *Session) {
if ses.doHalt { if ses.doHalt {
return return
} }
_, ok := it.Next() _, ok := graph.Next(it)
if !ok { if !ok {
break break
} }

View file

@ -88,7 +88,7 @@ func (s *Session) ExecInput(input string, c chan interface{}, limit int) {
glog.V(2).Infoln(it.DebugString(0)) glog.V(2).Infoln(it.DebugString(0))
} }
for { for {
_, ok := it.Next() _, ok := graph.Next(it)
if !ok { if !ok {
break break
} }