Merge branch 'declassify' into tip

Conflicts:
	graph/iterator/hasa_iterator.go
	graph/iterator/linksto_iterator.go
	graph/iterator/query_shape_test.go
	graph/leveldb/all_iterator.go
	graph/leveldb/iterator.go
	graph/leveldb/leveldb_test.go
	graph/memstore/triplestore_test.go
	graph/mongo/iterator.go
This commit is contained in:
kortschak 2014-07-30 16:40:37 +09:30
commit 2dbbd17fe1
34 changed files with 823 additions and 457 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"
@ -24,18 +23,46 @@ import (
"github.com/barakmich/glog" "github.com/barakmich/glog"
) )
type Tagger struct {
tags []string
fixedTags map[string]Value
}
// Adds a tag to the iterator.
func (t *Tagger) Add(tag string) {
t.tags = append(t.tags, tag)
}
func (t *Tagger) AddFixed(tag string, value Value) {
if t.fixedTags == nil {
t.fixedTags = make(map[string]Value)
}
t.fixedTags[tag] = value
}
// Returns the tags. The returned value must not be mutated.
func (t *Tagger) Tags() []string {
return t.tags
}
// Returns the fixed tags. The returned value must not be mutated.
func (t *Tagger) Fixed() map[string]Value {
return t.fixedTags
}
func (t *Tagger) CopyFrom(src Iterator) {
for _, tag := range src.Tagger().Tags() {
t.Add(tag)
}
for k, v := range src.Tagger().Fixed() {
t.AddFixed(k, v)
}
}
type Iterator interface { type Iterator interface {
// Tags are the way we handle results. By adding a tag to an iterator, we can Tagger() *Tagger
// "name" it, in a sense, and at each step of iteration, get a named result.
// TagResults() is therefore the handy way of walking an iterator tree and
// getting the named results.
//
// Tag Accessors.
AddTag(string)
Tags() []string
AddFixedTag(string, Value)
FixedTags() map[string]Value
CopyTagsFrom(Iterator)
// Fills a tag-to-result-value map. // Fills a tag-to-result-value map.
TagResults(map[string]Value) TagResults(map[string]Value)
@ -58,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
@ -114,7 +132,26 @@ type Iterator interface {
Close() Close()
// UID returns the unique identifier of the iterator. // UID returns the unique identifier of the iterator.
UID() uintptr 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.

View file

@ -31,19 +31,25 @@ 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
tags graph.Tagger
max, min int64 max, min int64
at int64 at int64
result graph.Value
} }
// Creates a new Int64 with the given range. // Creates a new Int64 with the given range.
func NewInt64(min, max int64) *Int64 { func NewInt64(min, max int64) *Int64 {
var all Int64 return &Int64{
BaseInit(&all.Base) uid: NextUID(),
all.max = max min: min,
all.min = min max: max,
all.at = min at: min,
return &all }
}
func (it *Int64) UID() uint64 {
return it.uid
} }
// Start back at the beginning // Start back at the beginning
@ -55,13 +61,28 @@ func (it *Int64) Close() {}
func (it *Int64) Clone() graph.Iterator { func (it *Int64) Clone() graph.Iterator {
out := NewInt64(it.min, it.max) out := NewInt64(it.min, it.max)
out.CopyTagsFrom(it) out.tags.CopyFrom(it)
return out return out
} }
func (it *Int64) Tagger() *graph.Tagger {
return &it.tags
}
// Fill the map based on the tags assigned to this iterator.
func (it *Int64) TagResults(dst map[string]graph.Value) {
for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
}
// Prints the All iterator as just an "all". // Prints the All iterator as just an "all".
func (it *Int64) DebugString(indent int) string { func (it *Int64) DebugString(indent int) string {
return fmt.Sprintf("%s(%s tags: %v)", strings.Repeat(" ", indent), it.Type(), it.Tags()) return fmt.Sprintf("%s(%s tags: %v)", strings.Repeat(" ", indent), it.Type(), it.tags.Tags())
} }
// Next() on an Int64 all iterator is a simple incrementing counter. // Next() on an Int64 all iterator is a simple incrementing counter.
@ -76,10 +97,28 @@ func (it *Int64) Next() (graph.Value, bool) {
if it.at > it.max { if it.at > it.max {
it.at = -1 it.at = -1
} }
it.Last = val it.result = val
return graph.NextLogOut(it, val, true) return graph.NextLogOut(it, val, true)
} }
// DEPRECATED
func (it *Int64) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())
}
func (it *Int64) Result() graph.Value {
return it.result
}
func (it *Int64) NextResult() bool {
return false
}
// No sub-iterators.
func (it *Int64) SubIterators() []graph.Iterator {
return nil
}
// The number of elements in an Int64 is the size of the range. // The number of elements in an Int64 is the size of the range.
// The size is exact. // The size is exact.
func (it *Int64) Size() (int64, bool) { func (it *Int64) Size() (int64, bool) {
@ -93,7 +132,7 @@ func (it *Int64) Check(tsv graph.Value) bool {
graph.CheckLogIn(it, tsv) graph.CheckLogIn(it, tsv)
v := tsv.(int64) v := tsv.(int64)
if it.min <= v && v <= it.max { if it.min <= v && v <= it.max {
it.Last = v it.result = v
return graph.CheckLogOut(it, v, true) return graph.CheckLogOut(it, v, true)
} }
return graph.CheckLogOut(it, v, false) return graph.CheckLogOut(it, v, false)

View file

@ -22,23 +22,28 @@ 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
tags graph.Tagger
internalIterators []graph.Iterator internalIterators []graph.Iterator
itCount int itCount int
primaryIt graph.Iterator primaryIt graph.Iterator
checkList []graph.Iterator checkList []graph.Iterator
result graph.Value
} }
// Creates a new And iterator. // Creates a new And iterator.
func NewAnd() *And { func NewAnd() *And {
var and And return &And{
BaseInit(&and.Base) uid: NextUID(),
and.internalIterators = make([]graph.Iterator, 0, 20) internalIterators: make([]graph.Iterator, 0, 20),
and.checkList = nil }
return &and }
func (it *And) UID() uint64 {
return it.uid
} }
// Reset all internal iterators // Reset all internal iterators
@ -50,10 +55,33 @@ func (it *And) Reset() {
it.checkList = nil it.checkList = nil
} }
func (it *And) Tagger() *graph.Tagger {
return &it.tags
}
// 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() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
if it.primaryIt != nil {
it.primaryIt.TagResults(dst)
}
for _, sub := range it.internalIterators {
sub.TagResults(dst)
}
}
func (it *And) Clone() graph.Iterator { func (it *And) Clone() graph.Iterator {
and := NewAnd() and := NewAnd()
and.AddSubIterator(it.primaryIt.Clone()) and.AddSubIterator(it.primaryIt.Clone())
and.CopyTagsFrom(it) and.tags.CopyFrom(it)
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
and.AddSubIterator(sub.Clone()) and.AddSubIterator(sub.Clone())
} }
@ -71,18 +99,6 @@ func (it *And) SubIterators() []graph.Iterator {
return iters return iters
} }
// Overrides Base 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) {
it.Base.TagResults(dst)
if it.primaryIt != nil {
it.primaryIt.TagResults(dst)
}
for _, sub := range it.internalIterators {
sub.TagResults(dst)
}
}
// DEPRECATED Returns the ResultTree for this iterator, recurses to it's subiterators. // DEPRECATED Returns the ResultTree for this iterator, recurses to it's subiterators.
func (it *And) ResultTree() *graph.ResultTree { func (it *And) ResultTree() *graph.ResultTree {
tree := graph.NewResultTree(it.Result()) tree := graph.NewResultTree(it.Result())
@ -101,7 +117,7 @@ func (it *And) DebugString(indent int) string {
total += fmt.Sprintf("%d:\n%s\n", i, sub.DebugString(indent+4)) total += fmt.Sprintf("%d:\n%s\n", i, sub.DebugString(indent+4))
} }
var tags string var tags string
for _, k := range it.Tags() { for _, k := range it.tags.Tags() {
tags += fmt.Sprintf("%s;", k) tags += fmt.Sprintf("%s;", k)
} }
spaces := strings.Repeat(" ", indent+2) spaces := strings.Repeat(" ", indent+2)
@ -144,16 +160,20 @@ 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)
} }
if it.checkSubIts(curr) { if it.checkSubIts(curr) {
it.Last = curr it.result = curr
return graph.NextLogOut(it, curr, true) return graph.NextLogOut(it, curr, true)
} }
} }
panic("Somehow broke out of Next() loop in And") panic("unreachable")
}
func (it *And) Result() graph.Value {
return it.result
} }
// Checks a value against the non-primary iterators, in order. // Checks a value against the non-primary iterators, in order.
@ -177,7 +197,7 @@ func (it *And) checkCheckList(val graph.Value) bool {
} }
} }
if ok { if ok {
it.Last = val it.result = val
} }
return graph.CheckLogOut(it, val, ok) return graph.CheckLogOut(it, val, ok)
} }
@ -196,7 +216,7 @@ func (it *And) Check(val graph.Value) bool {
if !othersGood { if !othersGood {
return graph.CheckLogOut(it, val, false) return graph.CheckLogOut(it, val, false)
} }
it.Last = val it.result = val
return graph.CheckLogOut(it, val, true) return graph.CheckLogOut(it, val, true)
} }

View file

@ -82,7 +82,7 @@ func (it *And) Optimize() (graph.Iterator, bool) {
} }
// Move the tags hanging on us (like any good replacement). // Move the tags hanging on us (like any good replacement).
newAnd.CopyTagsFrom(it) newAnd.tags.CopyFrom(it)
newAnd.optimizeCheck() newAnd.optimizeCheck()
@ -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 {
@ -213,11 +213,11 @@ func (it *And) optimizeCheck() {
func (it *And) getSubTags() map[string]struct{} { func (it *And) getSubTags() map[string]struct{} {
tags := make(map[string]struct{}) tags := make(map[string]struct{})
for _, sub := range it.SubIterators() { for _, sub := range it.SubIterators() {
for _, tag := range sub.Tags() { for _, tag := range sub.Tagger().Tags() {
tags[tag] = struct{}{} tags[tag] = struct{}{}
} }
} }
for _, tag := range it.Tags() { for _, tag := range it.tags.Tags() {
tags[tag] = struct{}{} tags[tag] = struct{}{}
} }
return tags return tags
@ -227,13 +227,14 @@ func (it *And) getSubTags() map[string]struct{} {
// src itself, and moves them to dst. // src itself, and moves them to dst.
func moveTagsTo(dst graph.Iterator, src *And) { func moveTagsTo(dst graph.Iterator, src *And) {
tags := src.getSubTags() tags := src.getSubTags()
for _, tag := range dst.Tags() { for _, tag := range dst.Tagger().Tags() {
if _, ok := tags[tag]; ok { if _, ok := tags[tag]; ok {
delete(tags, tag) delete(tags, tag)
} }
} }
dt := dst.Tagger()
for k := range tags { for k := range tags {
dst.AddTag(k) dt.Add(k)
} }
} }

View file

@ -32,9 +32,9 @@ func TestIteratorPromotion(t *testing.T) {
a := NewAnd() a := NewAnd()
a.AddSubIterator(all) a.AddSubIterator(all)
a.AddSubIterator(fixed) a.AddSubIterator(fixed)
all.AddTag("a") all.Tagger().Add("a")
fixed.AddTag("b") fixed.Tagger().Add("b")
a.AddTag("c") a.Tagger().Add("c")
newIt, changed := a.Optimize() newIt, changed := a.Optimize()
if !changed { if !changed {
t.Error("Iterator didn't optimize") t.Error("Iterator didn't optimize")
@ -43,7 +43,7 @@ func TestIteratorPromotion(t *testing.T) {
t.Error("Expected fixed iterator") t.Error("Expected fixed iterator")
} }
tagsExpected := []string{"a", "b", "c"} tagsExpected := []string{"a", "b", "c"}
tags := newIt.Tags() tags := newIt.Tagger().Tags()
sort.Strings(tags) sort.Strings(tags)
if !reflect.DeepEqual(tags, tagsExpected) { if !reflect.DeepEqual(tags, tagsExpected) {
t.Fatal("Tags don't match") t.Fatal("Tags don't match")
@ -67,9 +67,9 @@ func TestNullIteratorAnd(t *testing.T) {
func TestReorderWithTag(t *testing.T) { func TestReorderWithTag(t *testing.T) {
all := NewInt64(100, 300) all := NewInt64(100, 300)
all.AddTag("good") all.Tagger().Add("good")
all2 := NewInt64(1, 30000) all2 := NewInt64(1, 30000)
all2.AddTag("slow") all2.Tagger().Add("slow")
a := NewAnd() a := NewAnd()
// Make all2 the default iterator // Make all2 the default iterator
a.AddSubIterator(all2) a.AddSubIterator(all2)
@ -82,7 +82,7 @@ func TestReorderWithTag(t *testing.T) {
expectedTags := []string{"good", "slow"} expectedTags := []string{"good", "slow"}
tagsOut := make([]string, 0) tagsOut := make([]string, 0)
for _, sub := range newIt.SubIterators() { for _, sub := range newIt.SubIterators() {
for _, x := range sub.Tags() { for _, x := range sub.Tagger().Tags() {
tagsOut = append(tagsOut, x) tagsOut = append(tagsOut, x)
} }
} }
@ -93,9 +93,9 @@ func TestReorderWithTag(t *testing.T) {
func TestAndStatistics(t *testing.T) { func TestAndStatistics(t *testing.T) {
all := NewInt64(100, 300) all := NewInt64(100, 300)
all.AddTag("good") all.Tagger().Add("good")
all2 := NewInt64(1, 30000) all2 := NewInt64(1, 30000)
all2.AddTag("slow") all2.Tagger().Add("slow")
a := NewAnd() a := NewAnd()
// Make all2 the default iterator // Make all2 the default iterator
a.AddSubIterator(all2) a.AddSubIterator(all2)

View file

@ -24,11 +24,11 @@ import (
func TestTag(t *testing.T) { func TestTag(t *testing.T) {
fix1 := newFixed() fix1 := newFixed()
fix1.Add(234) fix1.Add(234)
fix1.AddTag("foo") fix1.Tagger().Add("foo")
and := NewAnd() and := NewAnd()
and.AddSubIterator(fix1) and.AddSubIterator(fix1)
and.AddTag("bar") and.Tagger().Add("bar")
out := fix1.Tags() out := fix1.Tagger().Tags()
if len(out) != 1 { if len(out) != 1 {
t.Errorf("Expected length 1, got %d", len(out)) t.Errorf("Expected length 1, got %d", len(out))
} }

View file

@ -30,10 +30,12 @@ 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
tags graph.Tagger
values []graph.Value values []graph.Value
lastIndex int lastIndex int
cmp Equality cmp Equality
result graph.Value
} }
// Define the signature of an equality function. // Define the signature of an equality function.
@ -54,12 +56,15 @@ func newFixed() *Fixed {
// Creates a new Fixed iterator with a custom comparitor. // Creates a new Fixed iterator with a custom comparitor.
func NewFixedIteratorWithCompare(compareFn Equality) *Fixed { func NewFixedIteratorWithCompare(compareFn Equality) *Fixed {
var it Fixed return &Fixed{
BaseInit(&it.Base) uid: NextUID(),
it.values = make([]graph.Value, 0, 20) values: make([]graph.Value, 0, 20),
it.lastIndex = 0 cmp: compareFn,
it.cmp = compareFn }
return &it }
func (it *Fixed) UID() uint64 {
return it.uid
} }
func (it *Fixed) Reset() { func (it *Fixed) Reset() {
@ -68,12 +73,26 @@ func (it *Fixed) Reset() {
func (it *Fixed) Close() {} func (it *Fixed) Close() {}
func (it *Fixed) Tagger() *graph.Tagger {
return &it.tags
}
func (it *Fixed) TagResults(dst map[string]graph.Value) {
for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
}
func (it *Fixed) Clone() graph.Iterator { func (it *Fixed) Clone() graph.Iterator {
out := NewFixedIteratorWithCompare(it.cmp) out := NewFixedIteratorWithCompare(it.cmp)
for _, val := range it.values { for _, val := range it.values {
out.Add(val) out.Add(val)
} }
out.CopyTagsFrom(it) out.tags.CopyFrom(it)
return out return out
} }
@ -92,7 +111,7 @@ func (it *Fixed) DebugString(indent int) string {
return fmt.Sprintf("%s(%s tags: %s Size: %d id0: %d)", return fmt.Sprintf("%s(%s tags: %s Size: %d id0: %d)",
strings.Repeat(" ", indent), strings.Repeat(" ", indent),
it.Type(), it.Type(),
it.FixedTags(), it.tags.Fixed(),
len(it.values), len(it.values),
value, value,
) )
@ -109,7 +128,7 @@ func (it *Fixed) Check(v graph.Value) bool {
graph.CheckLogIn(it, v) graph.CheckLogIn(it, v)
for _, x := range it.values { for _, x := range it.values {
if it.cmp(x, v) { if it.cmp(x, v) {
it.Last = x it.result = x
return graph.CheckLogOut(it, v, true) return graph.CheckLogOut(it, v, true)
} }
} }
@ -123,11 +142,29 @@ func (it *Fixed) Next() (graph.Value, bool) {
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, nil, false)
} }
out := it.values[it.lastIndex] out := it.values[it.lastIndex]
it.Last = out it.result = out
it.lastIndex++ it.lastIndex++
return graph.NextLogOut(it, out, true) return graph.NextLogOut(it, out, true)
} }
// DEPRECATED
func (it *Fixed) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())
}
func (it *Fixed) Result() graph.Value {
return it.result
}
func (it *Fixed) NextResult() bool {
return false
}
// No sub-iterators.
func (it *Fixed) SubIterators() []graph.Iterator {
return nil
}
// Optimize() for a Fixed iterator is simple. Returns a Null iterator if it's empty // Optimize() for a Fixed iterator is simple. Returns a Null iterator if it's empty
// (so that other iterators upstream can treat this as null) or there is no // (so that other iterators upstream can treat this as null) or there is no
// optimization. // optimization.

View file

@ -47,22 +47,28 @@ 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
tags graph.Tagger
ts graph.TripleStore ts graph.TripleStore
primaryIt graph.Iterator primaryIt graph.Iterator
dir quad.Direction dir quad.Direction
resultIt graph.Iterator resultIt graph.Iterator
result graph.Value
} }
// Construct a new HasA iterator, given the triple subiterator, and the triple // Construct a new HasA iterator, given the triple subiterator, and the triple
// direction for which it stands. // direction for which it stands.
func NewHasA(ts graph.TripleStore, subIt graph.Iterator, d quad.Direction) *HasA { func NewHasA(ts graph.TripleStore, subIt graph.Iterator, d quad.Direction) *HasA {
var hasa HasA return &HasA{
BaseInit(&hasa.Base) uid: NextUID(),
hasa.ts = ts ts: ts,
hasa.primaryIt = subIt primaryIt: subIt,
hasa.dir = d dir: d,
return &hasa }
}
func (it *HasA) UID() uint64 {
return it.uid
} }
// Return our sole subiterator. // Return our sole subiterator.
@ -77,9 +83,13 @@ func (it *HasA) Reset() {
} }
} }
func (it *HasA) Tagger() *graph.Tagger {
return &it.tags
}
func (it *HasA) Clone() graph.Iterator { func (it *HasA) Clone() graph.Iterator {
out := NewHasA(it.ts, it.primaryIt.Clone(), it.dir) out := NewHasA(it.ts, it.primaryIt.Clone(), it.dir)
out.CopyTagsFrom(it) out.tags.CopyFrom(it)
return out return out
} }
@ -101,7 +111,14 @@ func (it *HasA) Optimize() (graph.Iterator, bool) {
// Pass the TagResults down the chain. // Pass the TagResults down the chain.
func (it *HasA) TagResults(dst map[string]graph.Value) { func (it *HasA) TagResults(dst map[string]graph.Value) {
it.Base.TagResults(dst) for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
it.primaryIt.TagResults(dst) it.primaryIt.TagResults(dst)
} }
@ -115,7 +132,7 @@ func (it *HasA) ResultTree() *graph.ResultTree {
// Print some information about this iterator. // Print some information about this iterator.
func (it *HasA) DebugString(indent int) string { func (it *HasA) DebugString(indent int) string {
var tags string var tags string
for _, k := range it.Tags() { for _, k := range it.tags.Tags() {
tags += fmt.Sprintf("%s;", k) tags += fmt.Sprintf("%s;", k)
} }
return fmt.Sprintf("%s(%s %d tags:%s direction:%s\n%s)", strings.Repeat(" ", indent), it.Type(), it.UID(), tags, it.dir, it.primaryIt.DebugString(indent+4)) return fmt.Sprintf("%s(%s %d tags:%s direction:%s\n%s)", strings.Repeat(" ", indent), it.Type(), it.UID(), tags, it.dir, it.primaryIt.DebugString(indent+4))
@ -142,7 +159,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
} }
@ -150,7 +167,7 @@ func (it *HasA) GetCheckResult() bool {
glog.V(4).Infoln("Quad is", it.ts.Quad(linkVal)) glog.V(4).Infoln("Quad is", it.ts.Quad(linkVal))
} }
if it.primaryIt.Check(linkVal) { if it.primaryIt.Check(linkVal) {
it.Last = it.ts.TripleDirection(linkVal, it.dir) it.result = it.ts.TripleDirection(linkVal, it.dir)
return true return true
} }
} }
@ -181,16 +198,20 @@ 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)
} }
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.Last = val it.result = val
return graph.NextLogOut(it, val, true) return graph.NextLogOut(it, val, true)
} }
func (it *HasA) Result() graph.Value {
return it.result
}
// GetStats() returns the statistics on the HasA iterator. This is curious. Next // GetStats() returns the statistics on the HasA iterator. This is curious. Next
// cost is easy, it's an extra call or so on top of the subiterator Next cost. // cost is easy, it's an extra call or so on top of the subiterator Next cost.
// CheckCost involves going to the graph.TripleStore, iterating out values, and hoping // CheckCost involves going to the graph.TripleStore, iterating out values, and hoping
@ -222,3 +243,7 @@ func (it *HasA) Close() {
// Register this iterator as a HasA. // Register this iterator as a HasA.
func (it *HasA) Type() graph.Type { return graph.HasA } func (it *HasA) Type() graph.Type { return graph.HasA }
func (it *HasA) Size() (int64, bool) {
return 0, true
}

View file

@ -14,161 +14,55 @@
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 (
"fmt"
"strings" "strings"
"sync/atomic" "sync/atomic"
"github.com/barakmich/glog"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
) )
var nextIteratorID uintptr var nextIteratorID uint64
func nextID() uintptr { func NextUID() uint64 {
return atomic.AddUintptr(&nextIteratorID, 1) - 1 return atomic.AddUint64(&nextIteratorID, 1) - 1
} }
// The Base iterator is the iterator other iterators inherit from to get some // Here we define the simplest iterator -- the Null iterator. It contains nothing.
// default functionality.
type Base struct {
Last graph.Value
tags []string
fixedTags map[string]graph.Value
canNext bool
uid uintptr
}
// Called by subclases.
func BaseInit(it *Base) {
// Your basic iterator is nextable
it.canNext = true
if glog.V(2) {
it.uid = nextID()
}
}
func (it *Base) UID() uintptr {
return it.uid
}
// Adds a tag to the iterator. Most iterators don't need to override.
func (it *Base) AddTag(tag string) {
if it.tags == nil {
it.tags = make([]string, 0)
}
it.tags = append(it.tags, tag)
}
func (it *Base) AddFixedTag(tag string, value graph.Value) {
if it.fixedTags == nil {
it.fixedTags = make(map[string]graph.Value)
}
it.fixedTags[tag] = value
}
// Returns the tags.
func (it *Base) Tags() []string {
return it.tags
}
func (it *Base) FixedTags() map[string]graph.Value {
return it.fixedTags
}
func (it *Base) CopyTagsFrom(other_it graph.Iterator) {
for _, tag := range other_it.Tags() {
it.AddTag(tag)
}
for k, v := range other_it.FixedTags() {
it.AddFixedTag(k, v)
}
}
// Prints a silly debug string. Most classes override.
func (it *Base) DebugString(indent int) string {
return fmt.Sprintf("%s(base)", strings.Repeat(" ", indent))
}
// Nothing in a base iterator.
func (it *Base) Check(v graph.Value) bool {
return false
}
// Base iterators should never appear in a tree if they are, select against
// them.
func (it *Base) Stats() graph.IteratorStats {
return graph.IteratorStats{100000, 100000, 100000}
}
// DEPRECATED
func (it *Base) ResultTree() *graph.ResultTree {
tree := graph.NewResultTree(it.Result())
return tree
}
// Nothing in a base iterator.
func (it *Base) Next() (graph.Value, bool) {
return nil, false
}
func (it *Base) NextResult() bool {
return false
}
// Returns the last result of an iterator.
func (it *Base) Result() graph.Value {
return it.Last
}
// If you're empty and you know it, clap your hands.
func (it *Base) Size() (int64, bool) {
return 0, true
}
// No subiterators. Only those with subiterators need to do anything here.
func (it *Base) SubIterators() []graph.Iterator {
return nil
}
// Accessor
func (it *Base) CanNext() bool { return it.canNext }
// Fill the map based on the tags assigned to this iterator. Default
// functionality works well for most iterators.
func (it *Base) TagResults(dst map[string]graph.Value) {
for _, tag := range it.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.FixedTags() {
dst[tag] = value
}
}
// Nothing to clean up.
// func (it *Base) Close() {}
func (it *Null) Close() {}
func (it *Base) Reset() {}
// Here we define the simplest base 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.
type Null struct { type Null struct {
Base uid uint64
tags graph.Tagger
} }
// Fairly useless New function. // Fairly useless New function.
func NewNull() *Null { func NewNull() *Null {
return &Null{} return &Null{uid: NextUID()}
}
func (it *Null) UID() uint64 {
return it.uid
}
func (it *Null) Tagger() *graph.Tagger {
return &it.tags
}
// Fill the map based on the tags assigned to this iterator.
func (it *Null) TagResults(dst map[string]graph.Value) {
for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
}
func (it *Null) Check(graph.Value) bool {
return false
} }
func (it *Null) Clone() graph.Iterator { return NewNull() } func (it *Null) Clone() graph.Iterator { return NewNull() }
@ -185,6 +79,34 @@ func (it *Null) DebugString(indent int) string {
return strings.Repeat(" ", indent) + "(null)" return strings.Repeat(" ", indent) + "(null)"
} }
func (it *Null) Next() (graph.Value, bool) {
return nil, false
}
func (it *Null) Result() graph.Value {
return nil
}
func (it *Null) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())
}
func (it *Null) SubIterators() []graph.Iterator {
return nil
}
func (it *Null) NextResult() bool {
return false
}
func (it *Null) Size() (int64, bool) {
return 0, true
}
func (it *Null) Reset() {}
func (it *Null) Close() {}
// A null iterator costs nothing. Use it! // A null iterator costs nothing. Use it!
func (it *Null) Stats() graph.IteratorStats { func (it *Null) Stats() graph.IteratorStats {
return graph.IteratorStats{} return graph.IteratorStats{}

View file

@ -41,23 +41,29 @@ 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
tags graph.Tagger
ts graph.TripleStore ts graph.TripleStore
primaryIt graph.Iterator primaryIt graph.Iterator
dir quad.Direction dir quad.Direction
nextIt graph.Iterator nextIt graph.Iterator
result graph.Value
} }
// Construct a new LinksTo iterator around a direction and a subiterator of // Construct a new LinksTo iterator around a direction and a subiterator of
// nodes. // nodes.
func NewLinksTo(ts graph.TripleStore, it graph.Iterator, d quad.Direction) *LinksTo { func NewLinksTo(ts graph.TripleStore, it graph.Iterator, d quad.Direction) *LinksTo {
var lto LinksTo return &LinksTo{
BaseInit(&lto.Base) uid: NextUID(),
lto.ts = ts ts: ts,
lto.primaryIt = it primaryIt: it,
lto.dir = d dir: d,
lto.nextIt = &Null{} nextIt: &Null{},
return &lto }
}
func (it *LinksTo) UID() uint64 {
return it.uid
} }
func (it *LinksTo) Reset() { func (it *LinksTo) Reset() {
@ -68,9 +74,13 @@ func (it *LinksTo) Reset() {
it.nextIt = &Null{} it.nextIt = &Null{}
} }
func (it *LinksTo) Tagger() *graph.Tagger {
return &it.tags
}
func (it *LinksTo) Clone() graph.Iterator { func (it *LinksTo) Clone() graph.Iterator {
out := NewLinksTo(it.ts, it.primaryIt.Clone(), it.dir) out := NewLinksTo(it.ts, it.primaryIt.Clone(), it.dir)
out.CopyTagsFrom(it) out.tags.CopyFrom(it)
return out return out
} }
@ -79,7 +89,14 @@ func (it *LinksTo) Direction() quad.Direction { return it.dir }
// Tag these results, and our subiterator's results. // Tag these results, and our subiterator's results.
func (it *LinksTo) TagResults(dst map[string]graph.Value) { func (it *LinksTo) TagResults(dst map[string]graph.Value) {
it.Base.TagResults(dst) for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
it.primaryIt.TagResults(dst) it.primaryIt.TagResults(dst)
} }
@ -103,7 +120,7 @@ func (it *LinksTo) Check(val graph.Value) bool {
graph.CheckLogIn(it, val) graph.CheckLogIn(it, val)
node := it.ts.TripleDirection(val, it.dir) node := it.ts.TripleDirection(val, it.dir)
if it.primaryIt.Check(node) { if it.primaryIt.Check(node) {
it.Last = val it.result = val
return graph.CheckLogOut(it, val, true) return graph.CheckLogOut(it, val, true)
} }
return graph.CheckLogOut(it, val, false) return graph.CheckLogOut(it, val, false)
@ -138,10 +155,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)
@ -151,10 +168,14 @@ func (it *LinksTo) Next() (graph.Value, bool) {
// Recurse -- return the first in the next set. // Recurse -- return the first in the next set.
return it.Next() return it.Next()
} }
it.Last = val it.result = val
return graph.NextLogOut(it, val, ok) return graph.NextLogOut(it, val, ok)
} }
func (it *LinksTo) Result() graph.Value {
return it.result
}
// Close our subiterators. // Close our subiterators.
func (it *LinksTo) Close() { func (it *LinksTo) Close() {
it.nextIt.Close() it.nextIt.Close()
@ -182,3 +203,7 @@ func (it *LinksTo) Stats() graph.IteratorStats {
Size: fanoutFactor * subitStats.Size, Size: fanoutFactor * subitStats.Size,
} }
} }
func (it *LinksTo) Size() (int64, bool) {
return 0, true
}

View file

@ -30,26 +30,31 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/barakmich/glog"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
) )
// An optional iterator has the subconstraint iterator we wish to be optional // An optional iterator has the sub-constraint iterator we wish to be optional
// and whether the last check we received was true or false. // and whether the last check we received was true or false.
type Optional struct { type Optional struct {
Base uid uint64
tags graph.Tagger
subIt graph.Iterator subIt graph.Iterator
lastCheck bool lastCheck bool
result graph.Value
} }
// Creates a new optional iterator. // Creates a new optional iterator.
func NewOptional(it graph.Iterator) *Optional { func NewOptional(it graph.Iterator) *Optional {
var o Optional return &Optional{
BaseInit(&o.Base) uid: NextUID(),
o.canNext = false subIt: it,
o.subIt = it }
return &o }
func (it *Optional) CanNext() bool { return false }
func (it *Optional) UID() uint64 {
return it.uid
} }
func (it *Optional) Reset() { func (it *Optional) Reset() {
@ -61,17 +66,23 @@ func (it *Optional) Close() {
it.subIt.Close() it.subIt.Close()
} }
func (it *Optional) Tagger() *graph.Tagger {
return &it.tags
}
func (it *Optional) Clone() graph.Iterator { func (it *Optional) Clone() graph.Iterator {
out := NewOptional(it.subIt.Clone()) out := NewOptional(it.subIt.Clone())
out.CopyTagsFrom(it) out.tags.CopyFrom(it)
return out return out
} }
// Nexting the iterator is unsupported -- error and return an empty set. // DEPRECATED
// (As above, a reasonable alternative would be to Next() an all iterator) func (it *Optional) ResultTree() *graph.ResultTree {
func (it *Optional) Next() (graph.Value, bool) { return graph.NewResultTree(it.Result())
glog.Errorln("Nexting an un-nextable iterator") }
return nil, false
func (it *Optional) Result() graph.Value {
return it.result
} }
// 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
@ -84,13 +95,18 @@ func (it *Optional) NextResult() bool {
return false return false
} }
// No subiterators.
func (it *Optional) SubIterators() []graph.Iterator {
return nil
}
// Check() is the real hack of this iterator. It always returns true, regardless // Check() is the real hack of this iterator. It always returns true, regardless
// of whether the subiterator matched. But we keep track of whether the subiterator // of whether the subiterator matched. But we keep track of whether the subiterator
// matched for results purposes. // matched for results purposes.
func (it *Optional) Check(val graph.Value) bool { func (it *Optional) Check(val graph.Value) bool {
checked := it.subIt.Check(val) checked := it.subIt.Check(val)
it.lastCheck = checked it.lastCheck = checked
it.Last = val it.result = val
return true return true
} }
@ -111,7 +127,7 @@ func (it *Optional) DebugString(indent int) string {
return fmt.Sprintf("%s(%s tags:%s\n%s)", return fmt.Sprintf("%s(%s tags:%s\n%s)",
strings.Repeat(" ", indent), strings.Repeat(" ", indent),
it.Type(), it.Type(),
it.Tags(), it.tags.Tags(),
it.subIt.DebugString(indent+4)) it.subIt.DebugString(indent+4))
} }
@ -135,3 +151,8 @@ func (it *Optional) Stats() graph.IteratorStats {
Size: subStats.Size, Size: subStats.Size,
} }
} }
// If you're empty and you know it, clap your hands.
func (it *Optional) Size() (int64, bool) {
return 0, true
}

View file

@ -29,29 +29,34 @@ import (
) )
type Or struct { type Or struct {
Base uid uint64
tags graph.Tagger
isShortCircuiting bool isShortCircuiting bool
internalIterators []graph.Iterator internalIterators []graph.Iterator
itCount int itCount int
currentIterator int currentIterator int
result graph.Value
} }
func NewOr() *Or { func NewOr() *Or {
var or Or return &Or{
BaseInit(&or.Base) uid: NextUID(),
or.internalIterators = make([]graph.Iterator, 0, 20) internalIterators: make([]graph.Iterator, 0, 20),
or.isShortCircuiting = false currentIterator: -1,
or.currentIterator = -1 }
return &or
} }
func NewShortCircuitOr() *Or { func NewShortCircuitOr() *Or {
var or Or return &Or{
BaseInit(&or.Base) uid: NextUID(),
or.internalIterators = make([]graph.Iterator, 0, 20) internalIterators: make([]graph.Iterator, 0, 20),
or.isShortCircuiting = true isShortCircuiting: true,
or.currentIterator = -1 currentIterator: -1,
return &or }
}
func (it *Or) UID() uint64 {
return it.uid
} }
// Reset all internal iterators // Reset all internal iterators
@ -62,6 +67,10 @@ func (it *Or) Reset() {
it.currentIterator = -1 it.currentIterator = -1
} }
func (it *Or) Tagger() *graph.Tagger {
return &it.tags
}
func (it *Or) Clone() graph.Iterator { func (it *Or) Clone() graph.Iterator {
var or *Or var or *Or
if it.isShortCircuiting { if it.isShortCircuiting {
@ -72,7 +81,7 @@ func (it *Or) Clone() graph.Iterator {
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
or.AddSubIterator(sub.Clone()) or.AddSubIterator(sub.Clone())
} }
or.CopyTagsFrom(it) or.tags.CopyFrom(it)
return or return or
} }
@ -84,7 +93,14 @@ func (it *Or) SubIterators() []graph.Iterator {
// Overrides BaseIterator TagResults, as it needs to add it's own results and // Overrides BaseIterator TagResults, as it needs to add it's own results and
// recurse down it's subiterators. // recurse down it's subiterators.
func (it *Or) TagResults(dst map[string]graph.Value) { func (it *Or) TagResults(dst map[string]graph.Value) {
it.Base.TagResults(dst) for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
it.internalIterators[it.currentIterator].TagResults(dst) it.internalIterators[it.currentIterator].TagResults(dst)
} }
@ -105,7 +121,7 @@ func (it *Or) DebugString(indent int) string {
total += fmt.Sprintf("%d:\n%s\n", i, sub.DebugString(indent+4)) total += fmt.Sprintf("%d:\n%s\n", i, sub.DebugString(indent+4))
} }
var tags string var tags string
for _, k := range it.Tags() { for _, k := range it.tags.Tags() {
tags += fmt.Sprintf("%s;", k) tags += fmt.Sprintf("%s;", k)
} }
spaces := strings.Repeat(" ", indent+2) spaces := strings.Repeat(" ", indent+2)
@ -139,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)
@ -149,11 +165,15 @@ func (it *Or) Next() (graph.Value, bool) {
return graph.NextLogOut(it, nil, false) return graph.NextLogOut(it, nil, false)
} }
} else { } else {
it.Last = curr it.result = curr
return graph.NextLogOut(it, curr, true) return graph.NextLogOut(it, curr, true)
} }
} }
panic("Somehow broke out of Next() loop in Or") panic("unreachable")
}
func (it *Or) Result() graph.Value {
return it.result
} }
// Checks a value against the iterators, in order. // Checks a value against the iterators, in order.
@ -176,7 +196,7 @@ func (it *Or) Check(val graph.Value) bool {
if !anyGood { if !anyGood {
return graph.CheckLogOut(it, val, false) return graph.CheckLogOut(it, val, false)
} }
it.Last = val it.result = val
return graph.CheckLogOut(it, val, true) return graph.CheckLogOut(it, val, true)
} }
@ -247,7 +267,7 @@ func (it *Or) Optimize() (graph.Iterator, bool) {
} }
// Move the tags hanging on us (like any good replacement). // Move the tags hanging on us (like any good replacement).
newOr.CopyTagsFrom(it) newOr.tags.CopyFrom(it)
// And close ourselves but not our subiterators -- some may still be alive in // And close ourselves but not our subiterators -- some may still be alive in
// the new And (they were unchanged upon calling Optimize() on them, at the // the new And (they were unchanged upon calling Optimize() on them, at the

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

@ -108,10 +108,10 @@ func (qs *queryShape) StealNode(left *Node, right *Node) {
func (qs *queryShape) MakeNode(it graph.Iterator) *Node { func (qs *queryShape) MakeNode(it graph.Iterator) *Node {
n := Node{Id: qs.nodeId} n := Node{Id: qs.nodeId}
for _, tag := range it.Tags() { for _, tag := range it.Tagger().Tags() {
n.Tags = append(n.Tags, tag) n.Tags = append(n.Tags, tag)
} }
for k, _ := range it.FixedTags() { for k, _ := range it.Tagger().Fixed() {
n.Tags = append(n.Tags, k) n.Tags = append(n.Tags, k)
} }
@ -130,7 +130,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

@ -27,7 +27,7 @@ func hasaWithTag(ts graph.TripleStore, tag string, target string) *HasA {
obj := ts.FixedIterator() obj := ts.FixedIterator()
obj.Add(ts.ValueOf(target)) obj.Add(ts.ValueOf(target))
obj.AddTag(tag) obj.Tagger().Add(tag)
and.AddSubIterator(NewLinksTo(ts, obj, quad.Object)) and.AddSubIterator(NewLinksTo(ts, obj, quad.Object))
pred := ts.FixedIterator() pred := ts.FixedIterator()
@ -49,7 +49,7 @@ func TestQueryShape(t *testing.T) {
// Given a single linkage iterator's shape. // Given a single linkage iterator's shape.
hasa := hasaWithTag(ts, "tag", "cool") hasa := hasaWithTag(ts, "tag", "cool")
hasa.AddTag("top") hasa.Tagger().Add("top")
shape := make(map[string]interface{}) shape := make(map[string]interface{})
OutputQueryShapeForIterator(hasa, ts, shape) OutputQueryShapeForIterator(hasa, ts, shape)
@ -94,11 +94,11 @@ func TestQueryShape(t *testing.T) {
andInternal := NewAnd() andInternal := NewAnd()
hasa1 := hasaWithTag(ts, "tag1", "cool") hasa1 := hasaWithTag(ts, "tag1", "cool")
hasa1.AddTag("hasa1") hasa1.Tagger().Add("hasa1")
andInternal.AddSubIterator(hasa1) andInternal.AddSubIterator(hasa1)
hasa2 := hasaWithTag(ts, "tag2", "fun") hasa2 := hasaWithTag(ts, "tag2", "fun")
hasa2.AddTag("hasa2") hasa2.Tagger().Add("hasa2")
andInternal.AddSubIterator(hasa2) andInternal.AddSubIterator(hasa2)
pred := ts.FixedIterator() pred := ts.FixedIterator()

View file

@ -46,21 +46,27 @@ const (
) )
type Comparison struct { type Comparison struct {
Base uid uint64
tags graph.Tagger
subIt graph.Iterator subIt graph.Iterator
op Operator op Operator
val interface{} val interface{}
ts graph.TripleStore ts graph.TripleStore
result graph.Value
} }
func NewComparison(sub graph.Iterator, op Operator, val interface{}, ts graph.TripleStore) *Comparison { func NewComparison(sub graph.Iterator, op Operator, val interface{}, ts graph.TripleStore) *Comparison {
var vc Comparison return &Comparison{
BaseInit(&vc.Base) uid: NextUID(),
vc.subIt = sub subIt: sub,
vc.op = op op: op,
vc.val = val val: val,
vc.ts = ts ts: ts,
return &vc }
}
func (it *Comparison) UID() uint64 {
return it.uid
} }
// Here's the non-boilerplate part of the ValueComparison iterator. Given a value // Here's the non-boilerplate part of the ValueComparison iterator. Given a value
@ -111,9 +117,13 @@ func (it *Comparison) Reset() {
it.subIt.Reset() it.subIt.Reset()
} }
func (it *Comparison) Tagger() *graph.Tagger {
return &it.tags
}
func (it *Comparison) Clone() graph.Iterator { func (it *Comparison) Clone() graph.Iterator {
out := NewComparison(it.subIt.Clone(), it.op, it.val, it.ts) out := NewComparison(it.subIt.Clone(), it.op, it.val, it.ts)
out.CopyTagsFrom(it) out.tags.CopyFrom(it)
return out return out
} }
@ -121,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
} }
@ -129,10 +139,19 @@ func (it *Comparison) Next() (graph.Value, bool) {
break break
} }
} }
it.Last = val it.result = val
return val, ok return val, ok
} }
// DEPRECATED
func (it *Comparison) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())
}
func (it *Comparison) Result() graph.Value {
return it.result
}
func (it *Comparison) NextResult() bool { func (it *Comparison) NextResult() bool {
for { for {
hasNext := it.subIt.NextResult() hasNext := it.subIt.NextResult()
@ -143,10 +162,15 @@ func (it *Comparison) NextResult() bool {
return true return true
} }
} }
it.Last = it.subIt.Result() it.result = it.subIt.Result()
return true return true
} }
// No subiterators.
func (it *Comparison) SubIterators() []graph.Iterator {
return nil
}
func (it *Comparison) Check(val graph.Value) bool { func (it *Comparison) Check(val graph.Value) bool {
if !it.doComparison(val) { if !it.doComparison(val) {
return false return false
@ -157,7 +181,14 @@ func (it *Comparison) Check(val graph.Value) bool {
// If we failed the check, then the subiterator should not contribute to the result // If we failed the check, then the subiterator should not contribute to the result
// set. Otherwise, go ahead and tag it. // set. Otherwise, go ahead and tag it.
func (it *Comparison) TagResults(dst map[string]graph.Value) { func (it *Comparison) TagResults(dst map[string]graph.Value) {
it.Base.TagResults(dst) for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
it.subIt.TagResults(dst) it.subIt.TagResults(dst)
} }
@ -188,3 +219,7 @@ func (it *Comparison) Optimize() (graph.Iterator, bool) {
func (it *Comparison) Stats() graph.IteratorStats { func (it *Comparison) Stats() graph.IteratorStats {
return it.subIt.Stats() return it.subIt.Stats()
} }
func (it *Comparison) Size() (int64, bool) {
return 0, true
}

View file

@ -28,33 +28,47 @@ import (
) )
type AllIterator struct { type AllIterator struct {
iterator.Base uid uint64
tags graph.Tagger
prefix []byte prefix []byte
dir quad.Direction dir quad.Direction
open bool open bool
iter ldbit.Iterator iter ldbit.Iterator
ts *TripleStore ts *TripleStore
ro *opt.ReadOptions ro *opt.ReadOptions
result graph.Value
} }
func NewAllIterator(prefix string, d quad.Direction, ts *TripleStore) *AllIterator { func NewAllIterator(prefix string, d quad.Direction, ts *TripleStore) *AllIterator {
var it AllIterator opts := &opt.ReadOptions{
iterator.BaseInit(&it.Base) DontFillCache: true,
it.ro = &opt.ReadOptions{} }
it.ro.DontFillCache = true
it.iter = ts.db.NewIterator(nil, it.ro) it := AllIterator{
it.prefix = []byte(prefix) uid: iterator.NextUID(),
it.dir = d ro: opts,
it.open = true iter: ts.db.NewIterator(nil, opts),
it.ts = ts prefix: []byte(prefix),
dir: d,
open: true,
ts: ts,
}
it.iter.Seek(it.prefix) it.iter.Seek(it.prefix)
if !it.iter.Valid() { if !it.iter.Valid() {
// FIXME(kortschak) What are the semantics here? Is this iterator usable?
// If not, we should return nil *Iterator and an error.
it.open = false it.open = false
it.iter.Release() it.iter.Release()
} }
return &it return &it
} }
func (it *AllIterator) UID() uint64 {
return it.uid
}
func (it *AllIterator) Reset() { func (it *AllIterator) Reset() {
if !it.open { if !it.open {
it.iter = it.ts.db.NewIterator(nil, it.ro) it.iter = it.ts.db.NewIterator(nil, it.ro)
@ -67,15 +81,29 @@ func (it *AllIterator) Reset() {
} }
} }
func (it *AllIterator) Tagger() *graph.Tagger {
return &it.tags
}
func (it *AllIterator) TagResults(dst map[string]graph.Value) {
for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
}
func (it *AllIterator) Clone() graph.Iterator { func (it *AllIterator) Clone() graph.Iterator {
out := NewAllIterator(string(it.prefix), it.dir, it.ts) out := NewAllIterator(string(it.prefix), it.dir, it.ts)
out.CopyTagsFrom(it) out.tags.CopyFrom(it)
return out return out
} }
func (it *AllIterator) Next() (graph.Value, bool) { func (it *AllIterator) Next() (graph.Value, bool) {
if !it.open { if !it.open {
it.Last = nil it.result = nil
return nil, false return nil, false
} }
var out []byte var out []byte
@ -89,12 +117,29 @@ func (it *AllIterator) Next() (graph.Value, bool) {
it.Close() it.Close()
return nil, false return nil, false
} }
it.Last = out it.result = out
return out, true return out, true
} }
func (it *AllIterator) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())
}
func (it *AllIterator) Result() graph.Value {
return it.result
}
func (it *AllIterator) NextResult() bool {
return false
}
// No subiterators.
func (it *AllIterator) SubIterators() []graph.Iterator {
return nil
}
func (it *AllIterator) Check(v graph.Value) bool { func (it *AllIterator) Check(v graph.Value) bool {
it.Last = v it.result = v
return true return true
} }
@ -116,7 +161,7 @@ func (it *AllIterator) Size() (int64, bool) {
func (it *AllIterator) DebugString(indent int) string { func (it *AllIterator) DebugString(indent int) string {
size, _ := it.Size() size, _ := it.Size()
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.Tags(), size, it.dir, it)
} }
func (it *AllIterator) Type() graph.Type { return graph.All } func (it *AllIterator) Type() graph.Type { return graph.All }

View file

@ -28,7 +28,8 @@ import (
) )
type Iterator struct { type Iterator struct {
iterator.Base uid uint64
tags graph.Tagger
nextPrefix []byte nextPrefix []byte
checkId []byte checkId []byte
dir quad.Direction dir quad.Direction
@ -37,30 +38,46 @@ type Iterator struct {
qs *TripleStore qs *TripleStore
ro *opt.ReadOptions ro *opt.ReadOptions
originalPrefix string originalPrefix string
result graph.Value
} }
func NewIterator(prefix string, d quad.Direction, value graph.Value, qs *TripleStore) *Iterator { func NewIterator(prefix string, d quad.Direction, value graph.Value, qs *TripleStore) *Iterator {
var it Iterator vb := value.([]byte)
iterator.BaseInit(&it.Base) p := make([]byte, 0, 2+qs.hasher.Size())
it.checkId = value.([]byte) p = append(p, []byte(prefix)...)
it.dir = d p = append(p, []byte(vb[1:])...)
it.originalPrefix = prefix
it.nextPrefix = make([]byte, 0, 2+qs.hasher.Size()) opts := &opt.ReadOptions{
it.nextPrefix = append(it.nextPrefix, []byte(prefix)...) DontFillCache: true,
it.nextPrefix = append(it.nextPrefix, []byte(it.checkId[1:])...) }
it.ro = &opt.ReadOptions{}
it.ro.DontFillCache = true it := Iterator{
it.iter = qs.db.NewIterator(nil, it.ro) uid: iterator.NextUID(),
it.open = true nextPrefix: p,
it.qs = qs checkId: vb,
dir: d,
originalPrefix: prefix,
ro: opts,
iter: qs.db.NewIterator(nil, opts),
open: true,
qs: qs,
}
ok := it.iter.Seek(it.nextPrefix) ok := it.iter.Seek(it.nextPrefix)
if !ok { if !ok {
// FIXME(kortschak) What are the semantics here? Is this iterator usable?
// If not, we should return nil *Iterator and an error.
it.open = false it.open = false
it.iter.Release() it.iter.Release()
} }
return &it return &it
} }
func (it *Iterator) UID() uint64 {
return it.uid
}
func (it *Iterator) Reset() { func (it *Iterator) Reset() {
if !it.open { if !it.open {
it.iter = it.qs.db.NewIterator(nil, it.ro) it.iter = it.qs.db.NewIterator(nil, it.ro)
@ -73,9 +90,23 @@ func (it *Iterator) Reset() {
} }
} }
func (it *Iterator) Tagger() *graph.Tagger {
return &it.tags
}
func (it *Iterator) TagResults(dst map[string]graph.Value) {
for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
}
func (it *Iterator) Clone() graph.Iterator { func (it *Iterator) Clone() graph.Iterator {
out := NewIterator(it.originalPrefix, it.dir, it.checkId, it.qs) out := NewIterator(it.originalPrefix, it.dir, it.checkId, it.qs)
out.CopyTagsFrom(it) out.tags.CopyFrom(it)
return out return out
} }
@ -88,22 +119,22 @@ func (it *Iterator) Close() {
func (it *Iterator) Next() (graph.Value, bool) { func (it *Iterator) Next() (graph.Value, bool) {
if it.iter == nil { if it.iter == nil {
it.Last = nil it.result = nil
return nil, false return nil, false
} }
if !it.open { if !it.open {
it.Last = nil it.result = nil
return nil, false return nil, false
} }
if !it.iter.Valid() { if !it.iter.Valid() {
it.Last = nil it.result = nil
it.Close() it.Close()
return nil, false return nil, 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()))
copy(out, it.iter.Key()) copy(out, it.iter.Key())
it.Last = out it.result = out
ok := it.iter.Next() ok := it.iter.Next()
if !ok { if !ok {
it.Close() it.Close()
@ -111,10 +142,27 @@ func (it *Iterator) Next() (graph.Value, bool) {
return out, true return out, true
} }
it.Close() it.Close()
it.Last = nil it.result = nil
return nil, false return nil, false
} }
func (it *Iterator) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())
}
func (it *Iterator) Result() graph.Value {
return it.result
}
func (it *Iterator) NextResult() bool {
return false
}
// No subiterators.
func (it *Iterator) SubIterators() []graph.Iterator {
return nil
}
func PositionOf(prefix []byte, d quad.Direction, qs *TripleStore) int { func PositionOf(prefix []byte, d quad.Direction, qs *TripleStore) int {
if bytes.Equal(prefix, []byte("sp")) { if bytes.Equal(prefix, []byte("sp")) {
switch d { switch d {
@ -193,7 +241,7 @@ func (it *Iterator) Size() (int64, bool) {
func (it *Iterator) DebugString(indent int) string { func (it *Iterator) DebugString(indent int) string {
size, _ := it.Size() size, _ := it.Size()
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.qs.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.Tags(), it.dir, size, it.qs.NameOf(it.checkId))
} }
var levelDBType graph.Type var levelDBType graph.Type

View file

@ -46,7 +46,7 @@ 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 {
val, ok := it.Next() val, ok := graph.Next(it)
if !ok { if !ok {
break break
} }
@ -86,7 +86,7 @@ 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 {
val, ok := it.Next() val, ok := graph.Next(it)
if !ok { if !ok {
break break
} }
@ -266,7 +266,7 @@ func TestIterator(t *testing.T) {
it.Reset() it.Reset()
it = qs.TriplesAllIterator() it = qs.TriplesAllIterator()
edge, _ := it.Next() edge, _ := graph.Next(it)
triple := qs.Quad(edge) triple := qs.Quad(edge)
set := makeTripleSet() set := makeTripleSet()
var ok bool var ok bool
@ -416,7 +416,7 @@ func TestOptimize(t *testing.T) {
// With an linksto-fixed pair // With an linksto-fixed pair
fixed := qs.FixedIterator() fixed := qs.FixedIterator()
fixed.Add(qs.ValueOf("F")) fixed.Add(qs.ValueOf("F"))
fixed.AddTag("internal") fixed.Tagger().Add("internal")
lto := iterator.NewLinksTo(qs, fixed, quad.Object) lto := iterator.NewLinksTo(qs, fixed, quad.Object)
oldIt := lto.Clone() oldIt := lto.Clone()
@ -434,10 +434,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,14 +37,15 @@ 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")
} }
newIt := ts.TripleIterator(it.Direction(), val) newIt := ts.TripleIterator(it.Direction(), val)
newIt.CopyTagsFrom(it) nt := newIt.Tagger()
for _, tag := range primary.Tags() { nt.CopyFrom(it)
newIt.AddFixedTag(tag, val) for _, tag := range primary.Tagger().Tags() {
nt.AddFixed(tag, val)
} }
it.Close() it.Close()
return newIt, true return newIt, true

View file

@ -31,6 +31,11 @@ func NewMemstoreAllIterator(ts *TripleStore) *AllIterator {
return &out return &out
} }
// No subiterators.
func (it *AllIterator) SubIterators() []graph.Iterator {
return nil
}
func (it *AllIterator) Next() (graph.Value, bool) { func (it *AllIterator) Next() (graph.Value, bool) {
next, out := it.Int64.Next() next, out := it.Int64.Next()
if !out { if !out {
@ -41,6 +46,5 @@ func (it *AllIterator) Next() (graph.Value, bool) {
if !ok { if !ok {
return it.Next() return it.Next()
} }
it.Last = next
return next, out return next, out
} }

View file

@ -26,11 +26,13 @@ import (
) )
type Iterator struct { type Iterator struct {
iterator.Base uid uint64
tags graph.Tagger
tree *llrb.LLRB tree *llrb.LLRB
data string data string
isRunning bool isRunning bool
iterLast Int64 iterLast Int64
result graph.Value
} }
type Int64 int64 type Int64 int64
@ -53,34 +55,69 @@ func IterateOne(tree *llrb.LLRB, last Int64) Int64 {
} }
func NewLlrbIterator(tree *llrb.LLRB, data string) *Iterator { func NewLlrbIterator(tree *llrb.LLRB, data string) *Iterator {
var it Iterator return &Iterator{
iterator.BaseInit(&it.Base) uid: iterator.NextUID(),
it.tree = tree tree: tree,
it.iterLast = Int64(-1) iterLast: Int64(-1),
it.data = data data: data,
return &it }
}
func (it *Iterator) UID() uint64 {
return it.uid
} }
func (it *Iterator) Reset() { func (it *Iterator) Reset() {
it.iterLast = Int64(-1) it.iterLast = Int64(-1)
} }
func (it *Iterator) Tagger() *graph.Tagger {
return &it.tags
}
func (it *Iterator) TagResults(dst map[string]graph.Value) {
for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
}
for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
}
func (it *Iterator) Clone() graph.Iterator { func (it *Iterator) Clone() graph.Iterator {
var new_it = NewLlrbIterator(it.tree, it.data) m := NewLlrbIterator(it.tree, it.data)
new_it.CopyTagsFrom(it) m.tags.CopyFrom(it)
return new_it return m
} }
func (it *Iterator) Close() {} func (it *Iterator) Close() {}
func (it *Iterator) Next() (graph.Value, bool) { func (it *Iterator) Next() (graph.Value, bool) {
graph.NextLogIn(it) graph.NextLogIn(it)
if it.tree.Max() == nil || it.Last == 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)
} }
it.iterLast = IterateOne(it.tree, it.iterLast) it.iterLast = IterateOne(it.tree, it.iterLast)
it.Last = int64(it.iterLast) it.result = int64(it.iterLast)
return graph.NextLogOut(it, it.Last, true) return graph.NextLogOut(it, it.result, true)
}
func (it *Iterator) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())
}
func (it *Iterator) Result() graph.Value {
return it.result
}
func (it *Iterator) NextResult() bool {
return false
}
// No subiterators.
func (it *Iterator) SubIterators() []graph.Iterator {
return nil
} }
func (it *Iterator) Size() (int64, bool) { func (it *Iterator) Size() (int64, bool) {
@ -90,7 +127,7 @@ func (it *Iterator) Size() (int64, bool) {
func (it *Iterator) Check(v graph.Value) bool { func (it *Iterator) Check(v graph.Value) bool {
graph.CheckLogIn(it, v) graph.CheckLogIn(it, v)
if it.tree.Has(Int64(v.(int64))) { if it.tree.Has(Int64(v.(int64))) {
it.Last = v it.result = v
return graph.CheckLogOut(it, v, true) return graph.CheckLogOut(it, v, true)
} }
return graph.CheckLogOut(it, v, false) return graph.CheckLogOut(it, v, false)
@ -98,7 +135,7 @@ func (it *Iterator) Check(v graph.Value) bool {
func (it *Iterator) DebugString(indent int) string { func (it *Iterator) DebugString(indent int) string {
size, _ := it.Size() size, _ := it.Size()
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.Tags(), size, it.data)
} }
var memType graph.Type var memType graph.Type

View file

@ -37,14 +37,15 @@ 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")
} }
newIt := ts.TripleIterator(it.Direction(), val) newIt := ts.TripleIterator(it.Direction(), val)
newIt.CopyTagsFrom(it) nt := newIt.Tagger()
for _, tag := range primary.Tags() { nt.CopyFrom(it)
newIt.AddFixedTag(tag, val) for _, tag := range primary.Tagger().Tags() {
nt.AddFixed(tag, val)
} }
return newIt, true return newIt, true
} }

View file

@ -19,6 +19,7 @@ import (
"sort" "sort"
"testing" "testing"
"github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator" "github.com/google/cayley/graph/iterator"
"github.com/google/cayley/quad" "github.com/google/cayley/quad"
) )
@ -150,7 +151,7 @@ func TestLinksToOptimization(t *testing.T) {
fixed.Add(ts.ValueOf("cool")) fixed.Add(ts.ValueOf("cool"))
lto := iterator.NewLinksTo(ts, fixed, quad.Object) lto := iterator.NewLinksTo(ts, fixed, quad.Object)
lto.AddTag("foo") lto.Tagger().Add("foo")
newIt, changed := lto.Optimize() newIt, changed := lto.Optimize()
if !changed { if !changed {
@ -165,7 +166,8 @@ func TestLinksToOptimization(t *testing.T) {
if v_clone.DebugString(0) != v.DebugString(0) { if v_clone.DebugString(0) != v.DebugString(0) {
t.Fatal("Wrong iterator. Got ", v_clone.DebugString(0)) t.Fatal("Wrong iterator. Got ", v_clone.DebugString(0))
} }
if len(v_clone.Tags()) < 1 || v_clone.Tags()[0] != "foo" { vt := v_clone.Tagger()
if len(vt.Tags()) < 1 || vt.Tags()[0] != "foo" {
t.Fatal("Tag on LinksTo did not persist") t.Fatal("Tag on LinksTo did not persist")
} }
} }
@ -188,7 +190,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 := 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

@ -28,7 +28,8 @@ import (
) )
type Iterator struct { type Iterator struct {
iterator.Base uid uint64
tags graph.Tagger
qs *TripleStore qs *TripleStore
dir quad.Direction dir quad.Direction
iter *mgo.Iter iter *mgo.Iter
@ -38,55 +39,68 @@ type Iterator struct {
isAll bool isAll bool
constraint bson.M constraint bson.M
collection string collection string
result graph.Value
} }
func NewIterator(qs *TripleStore, collection string, d quad.Direction, val graph.Value) *Iterator { func NewIterator(qs *TripleStore, collection string, d quad.Direction, val graph.Value) *Iterator {
var m Iterator name := qs.NameOf(val)
iterator.BaseInit(&m.Base)
m.name = qs.NameOf(val) var constraint bson.M
m.collection = collection
switch d { switch d {
case quad.Subject: case quad.Subject:
m.constraint = bson.M{"Subject": m.name} constraint = bson.M{"Subject": name}
case quad.Predicate: case quad.Predicate:
m.constraint = bson.M{"Predicate": m.name} constraint = bson.M{"Predicate": name}
case quad.Object: case quad.Object:
m.constraint = bson.M{"Object": m.name} constraint = bson.M{"Object": name}
case quad.Label: case quad.Label:
m.constraint = bson.M{"Label": m.name} constraint = bson.M{"Label": name}
} }
m.qs = qs size, err := qs.db.C(collection).Find(constraint).Count()
m.dir = d
m.iter = qs.db.C(collection).Find(m.constraint).Iter()
size, err := qs.db.C(collection).Find(m.constraint).Count()
if err != nil { if err != nil {
// FIXME(kortschak) This should be passed back rather than just logging.
glog.Errorln("Trouble getting size for iterator! ", err) glog.Errorln("Trouble getting size for iterator! ", err)
return nil return nil
} }
m.size = int64(size)
m.hash = val.(string) return &Iterator{
m.isAll = false uid: iterator.NextUID(),
return &m name: name,
constraint: constraint,
collection: collection,
qs: qs,
dir: d,
iter: qs.db.C(collection).Find(constraint).Iter(),
size: int64(size),
hash: val.(string),
isAll: false,
}
} }
func NewAllIterator(qs *TripleStore, collection string) *Iterator { func NewAllIterator(qs *TripleStore, collection string) *Iterator {
var m Iterator
m.qs = qs
m.dir = quad.Any
m.constraint = nil
m.collection = collection
m.iter = qs.db.C(collection).Find(nil).Iter()
size, err := qs.db.C(collection).Count() size, err := qs.db.C(collection).Count()
if err != nil { if err != nil {
// FIXME(kortschak) This should be passed back rather than just logging.
glog.Errorln("Trouble getting size for iterator! ", err) glog.Errorln("Trouble getting size for iterator! ", err)
return nil return nil
} }
m.size = int64(size)
m.hash = "" return &Iterator{
m.isAll = true uid: iterator.NextUID(),
return &m qs: qs,
dir: quad.Any,
constraint: nil,
collection: collection,
iter: qs.db.C(collection).Find(nil).Iter(),
size: int64(size),
hash: "",
isAll: true,
}
}
func (it *Iterator) UID() uint64 {
return it.uid
} }
func (it *Iterator) Reset() { func (it *Iterator) Reset() {
@ -99,15 +113,29 @@ func (it *Iterator) Close() {
it.iter.Close() it.iter.Close()
} }
func (it *Iterator) Clone() graph.Iterator { func (it *Iterator) Tagger() *graph.Tagger {
var newM graph.Iterator return &it.tags
if it.isAll { }
newM = NewAllIterator(it.qs, it.collection)
} else { func (it *Iterator) TagResults(dst map[string]graph.Value) {
newM = NewIterator(it.qs, it.collection, it.dir, it.hash) for _, tag := range it.tags.Tags() {
dst[tag] = it.Result()
} }
newM.CopyTagsFrom(it)
return newM for tag, value := range it.tags.Fixed() {
dst[tag] = value
}
}
func (it *Iterator) Clone() graph.Iterator {
var m *Iterator
if it.isAll {
m = NewAllIterator(it.qs, it.collection)
} else {
m = NewIterator(it.qs, it.collection, it.dir, it.hash)
}
m.tags.CopyFrom(it)
return m
} }
func (it *Iterator) Next() (graph.Value, bool) { func (it *Iterator) Next() (graph.Value, bool) {
@ -125,14 +153,31 @@ func (it *Iterator) Next() (graph.Value, bool) {
} }
return nil, false return nil, false
} }
it.Last = result.Id it.result = result.Id
return result.Id, true return result.Id, true
} }
func (it *Iterator) ResultTree() *graph.ResultTree {
return graph.NewResultTree(it.Result())
}
func (it *Iterator) Result() graph.Value {
return it.result
}
func (it *Iterator) NextResult() bool {
return false
}
// No subiterators.
func (it *Iterator) SubIterators() []graph.Iterator {
return nil
}
func (it *Iterator) Check(v graph.Value) bool { func (it *Iterator) Check(v graph.Value) bool {
graph.CheckLogIn(it, v) graph.CheckLogIn(it, v)
if it.isAll { if it.isAll {
it.Last = v it.result = v
return graph.CheckLogOut(it, v, true) return graph.CheckLogOut(it, v, true)
} }
var offset int var offset int
@ -148,7 +193,7 @@ func (it *Iterator) Check(v graph.Value) bool {
} }
val := v.(string)[offset : it.qs.hasher.Size()*2+offset] val := v.(string)[offset : it.qs.hasher.Size()*2+offset]
if val == it.hash { if val == it.hash {
it.Last = v it.result = v
return graph.CheckLogOut(it, v, true) return graph.CheckLogOut(it, v, true)
} }
return graph.CheckLogOut(it, v, false) return graph.CheckLogOut(it, v, false)

View file

@ -37,14 +37,15 @@ 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")
} }
newIt := ts.TripleIterator(it.Direction(), val) newIt := ts.TripleIterator(it.Direction(), val)
newIt.CopyTagsFrom(it) nt := newIt.Tagger()
for _, tag := range primary.Tags() { nt.CopyFrom(it)
newIt.AddFixedTag(tag, val) for _, tag := range primary.Tagger().Tags() {
nt.AddFixed(tag, val)
} }
it.Close() it.Close()
return newIt, true return newIt, true

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

@ -190,7 +190,7 @@ func buildIteratorTree(tree *peg.ExpressionTree, ts graph.TripleStore) graph.Ite
nodeID := getIdentString(tree) nodeID := getIdentString(tree)
if tree.Children[0].Name == "Variable" { if tree.Children[0].Name == "Variable" {
allIt := ts.NodesAllIterator() allIt := ts.NodesAllIterator()
allIt.AddTag(nodeID) allIt.Tagger().Add(nodeID)
out = allIt out = allIt
} else { } else {
n := nodeID n := nodeID

View file

@ -67,7 +67,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)
} }
@ -88,7 +88,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")
} }
@ -105,7 +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 := it.Next() _, ok := graph.Next(it)
if !ok { if !ok {
t.Error("Got no results") t.Error("Got no results")
} }
@ -135,14 +135,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

@ -136,7 +136,7 @@ func buildInOutIterator(obj *otto.Object, ts graph.TripleStore, base graph.Itera
tags = makeListOfStringsFromArrayValue(one.Object()) tags = makeListOfStringsFromArrayValue(one.Object())
} }
for _, tag := range tags { for _, tag := range tags {
predicateNodeIterator.AddTag(tag) predicateNodeIterator.Tagger().Add(tag)
} }
} }
@ -180,7 +180,7 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
case "tag": case "tag":
it = subIt it = subIt
for _, tag := range stringArgs { for _, tag := range stringArgs {
it.AddTag(tag) it.Tagger().Add(tag)
} }
case "save": case "save":
all := ts.NodesAllIterator() all := ts.NodesAllIterator()
@ -188,9 +188,9 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
return iterator.NewNull() return iterator.NewNull()
} }
if len(stringArgs) == 2 { if len(stringArgs) == 2 {
all.AddTag(stringArgs[1]) all.Tagger().Add(stringArgs[1])
} else { } else {
all.AddTag(stringArgs[0]) all.Tagger().Add(stringArgs[0])
} }
predFixed := ts.FixedIterator() predFixed := ts.FixedIterator()
predFixed.Add(ts.ValueOf(stringArgs[0])) predFixed.Add(ts.ValueOf(stringArgs[0]))
@ -208,9 +208,9 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
return iterator.NewNull() return iterator.NewNull()
} }
if len(stringArgs) == 2 { if len(stringArgs) == 2 {
all.AddTag(stringArgs[1]) all.Tagger().Add(stringArgs[1])
} else { } else {
all.AddTag(stringArgs[0]) all.Tagger().Add(stringArgs[0])
} }
predFixed := ts.FixedIterator() predFixed := ts.FixedIterator()
predFixed.Add(ts.ValueOf(stringArgs[0])) predFixed.Add(ts.ValueOf(stringArgs[0]))

View file

@ -38,7 +38,7 @@ func embedFinals(env *otto.Otto, ses *Session, obj *otto.Object) {
func allFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.FunctionCall) otto.Value { func allFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.FunctionCall) otto.Value {
return func(call otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value {
it := buildIteratorTree(obj, ses.ts) it := buildIteratorTree(obj, ses.ts)
it.AddTag(TopResultTag) it.Tagger().Add(TopResultTag)
ses.limit = -1 ses.limit = -1
ses.count = 0 ses.count = 0
runIteratorOnSession(it, ses) runIteratorOnSession(it, ses)
@ -51,7 +51,7 @@ func limitFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.Functio
if len(call.ArgumentList) > 0 { if len(call.ArgumentList) > 0 {
limitVal, _ := call.Argument(0).ToInteger() limitVal, _ := call.Argument(0).ToInteger()
it := buildIteratorTree(obj, ses.ts) it := buildIteratorTree(obj, ses.ts)
it.AddTag(TopResultTag) it.Tagger().Add(TopResultTag)
ses.limit = int(limitVal) ses.limit = int(limitVal)
ses.count = 0 ses.count = 0
runIteratorOnSession(it, ses) runIteratorOnSession(it, ses)
@ -63,7 +63,7 @@ func limitFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.Functio
func toArrayFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value { func toArrayFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value {
return func(call otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value {
it := buildIteratorTree(obj, ses.ts) it := buildIteratorTree(obj, ses.ts)
it.AddTag(TopResultTag) it.Tagger().Add(TopResultTag)
limit := -1 limit := -1
if len(call.ArgumentList) > 0 { if len(call.ArgumentList) > 0 {
limitParsed, _ := call.Argument(0).ToInteger() limitParsed, _ := call.Argument(0).ToInteger()
@ -90,7 +90,7 @@ func toArrayFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool)
func toValueFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value { func toValueFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value {
return func(call otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value {
it := buildIteratorTree(obj, ses.ts) it := buildIteratorTree(obj, ses.ts)
it.AddTag(TopResultTag) it.Tagger().Add(TopResultTag)
limit := 1 limit := 1
var val otto.Value var val otto.Value
var err error var err error
@ -120,7 +120,7 @@ func toValueFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool)
func mapFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.FunctionCall) otto.Value { func mapFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.FunctionCall) otto.Value {
return func(call otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value {
it := buildIteratorTree(obj, ses.ts) it := buildIteratorTree(obj, ses.ts)
it.AddTag(TopResultTag) it.Tagger().Add(TopResultTag)
limit := -1 limit := -1
if len(call.ArgumentList) == 0 { if len(call.ArgumentList) == 0 {
return otto.NullValue() return otto.NullValue()
@ -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

@ -34,7 +34,7 @@ func (q *Query) buildFixed(s string) graph.Iterator {
func (q *Query) buildResultIterator(path Path) graph.Iterator { func (q *Query) buildResultIterator(path Path) graph.Iterator {
all := q.ses.ts.NodesAllIterator() all := q.ses.ts.NodesAllIterator()
all.AddTag(string(path)) all.Tagger().Add(string(path))
return all return all
} }
@ -98,7 +98,7 @@ func (q *Query) buildIteratorTreeInternal(query interface{}, path Path) (it grap
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
it.AddTag(string(path)) it.Tagger().Add(string(path))
return it, optional, nil return it, optional, nil
} }

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
} }