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
// Define the general iterator interface, as well as the Base iterator which all
// iterators can "inherit" from to get default iterator functionality.
// Define the general iterator interface.
import (
"strings"
@ -86,19 +85,10 @@ type Iterator interface {
// All of them should set iterator.Last to be the last returned value, to
// 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,
// from the bottom up.
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
// held by this iterator.
Check(Value) bool
@ -145,6 +135,25 @@ type Iterator interface {
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.
type FixedIterator interface {
Iterator

View file

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

View file

@ -22,10 +22,9 @@ import (
"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.
type And struct {
Base
uid uint64
tags graph.Tagger
internalIterators []graph.Iterator
@ -60,7 +59,7 @@ func (it *And) Tagger() *graph.Tagger {
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.
func (it *And) TagResults(dst map[string]graph.Value) {
for _, tag := range it.tags.Tags() {
@ -161,7 +160,7 @@ func (it *And) Next() (graph.Value, bool) {
var curr graph.Value
var exists bool
for {
curr, exists = it.primaryIt.Next()
curr, exists = graph.Next(it.primaryIt)
if !exists {
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
// else.
for _, it := range its {
if !it.CanNext() {
if _, canNext := it.(graph.Nexter); !canNext {
bad = append(bad, it)
continue
}
rootStats := it.Stats()
cost := rootStats.NextCost
for _, f := range its {
if !f.CanNext() {
if _, canNext := it.(graph.Nexter); !canNext {
continue
}
if f == it {
@ -177,7 +177,7 @@ func optimizeOrder(its []graph.Iterator) []graph.Iterator {
// ... push everyone else after...
for _, it := range its {
if !it.CanNext() {
if _, canNext := it.(graph.Nexter); !canNext {
continue
}
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
// an equality function.
type Fixed struct {
Base
uid uint64
tags graph.Tagger
values []graph.Value

View file

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

View file

@ -14,8 +14,7 @@
package iterator
// Define the general iterator interface, as well as the Base which all
// iterators can "inherit" from to get default iterator functionality.
// Define the general iterator interface.
import (
"strings"
@ -30,14 +29,6 @@ func NextUID() uint64 {
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.
// 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.
@ -88,8 +79,6 @@ func (it *Null) DebugString(indent int) string {
return strings.Repeat(" ", indent) + "(null)"
}
func (it *Null) CanNext() bool { return true }
func (it *Null) Next() (graph.Value, bool) {
return nil, false
}

View file

@ -40,7 +40,6 @@ import (
// for each node) the subiterator, and the direction the iterator comes from.
// `next_it` is the tempoarary iterator held per result in `primary_it`.
type LinksTo struct {
Base
uid uint64
tags graph.Tagger
ts graph.TripleStore
@ -155,10 +154,10 @@ func (it *LinksTo) Optimize() (graph.Iterator, bool) {
// Next()ing a LinksTo operates as described above.
func (it *LinksTo) Next() (graph.Value, bool) {
graph.NextLogIn(it)
val, ok := it.nextIt.Next()
val, ok := graph.Next(it.nextIt)
if !ok {
// Subiterator is empty, get another one
candidate, ok := it.primaryIt.Next()
candidate, ok := graph.Next(it.primaryIt)
if !ok {
// We're out of nodes in our subiterator, so we're done as well.
return graph.NextLogOut(it, 0, false)

View file

@ -30,8 +30,6 @@ import (
"fmt"
"strings"
"github.com/barakmich/glog"
"github.com/google/cayley/graph"
)
@ -78,16 +76,6 @@ func (it *Optional) Clone() graph.Iterator {
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
func (it *Optional) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -45,7 +45,7 @@ func makeTripleSet() []*graph.Triple {
func iteratedTriples(ts graph.TripleStore, it graph.Iterator) []*graph.Triple {
var res ordered
for {
val, ok := it.Next()
val, ok := graph.Next(it)
if !ok {
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 {
var res []string
for {
val, ok := it.Next()
val, ok := graph.Next(it)
if !ok {
break
}
@ -265,7 +265,7 @@ func TestIterator(t *testing.T) {
it.Reset()
it = ts.TriplesAllIterator()
edge, _ := it.Next()
edge, _ := graph.Next(it)
triple := ts.Triple(edge)
set := makeTripleSet()
var ok bool
@ -433,10 +433,10 @@ func TestOptimize(t *testing.T) {
t.Errorf("Optimized iteration does not match original")
}
oldIt.Next()
graph.Next(oldIt)
oldResults := make(map[string]graph.Value)
oldIt.TagResults(oldResults)
newIt.Next()
graph.Next(newIt)
newResults := make(map[string]graph.Value)
newIt.TagResults(newResults)
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 {
size, _ := primary.Size()
if size == 1 {
val, ok := primary.Next()
val, ok := graph.Next(primary)
if !ok {
panic("Sizes lie")
}

View file

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

View file

@ -37,7 +37,7 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo
if primary.Type() == graph.Fixed {
size, _ := primary.Size()
if size == 1 {
val, ok := primary.Next()
val, ok := graph.Next(primary)
if !ok {
panic("Sizes lie")
}

View file

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

View file

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

View file

@ -37,7 +37,7 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo
if primary.Type() == graph.Fixed {
size, _ := primary.Size()
if size == 1 {
val, ok := primary.Next()
val, ok := graph.Next(primary)
if !ok {
panic("Sizes lie")
}

View file

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

View file

@ -65,7 +65,7 @@ func TestMemstoreBackedSexp(t *testing.T) {
if it.Type() != test.typ {
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 {
t.Errorf("Failed to %s", test.message)
}
@ -86,7 +86,7 @@ func TestTreeConstraintParse(t *testing.T) {
if it.Type() != graph.And {
t.Error("Odd iterator tree. Got: %s", it.DebugString(0))
}
out, ok := it.Next()
out, ok := graph.Next(it)
if !ok {
t.Error("Got no results")
}
@ -103,7 +103,7 @@ func TestTreeConstraintTagParse(t *testing.T) {
"(:like\n" +
"($a (:is :good))))"
it := BuildIteratorTreeForQuery(ts, query)
_, ok := it.Next()
_, ok := graph.Next(it)
if !ok {
t.Error("Got no results")
}
@ -133,14 +133,14 @@ func TestMultipleConstraintParse(t *testing.T) {
if it.Type() != graph.And {
t.Error("Odd iterator tree. Got: %s", it.DebugString(0))
}
out, ok := it.Next()
out, ok := graph.Next(it)
if !ok {
t.Error("Got no results")
}
if out != ts.ValueOf("i") {
t.Errorf("Got %d, expected %d", out, ts.ValueOf("i"))
}
_, ok = it.Next()
_, ok = graph.Next(it)
if ok {
t.Error("Too many results")
}

View file

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

View file

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