Remove uses of container/list

This commit is contained in:
kortschak 2014-06-29 14:38:28 +09:30
parent bc77744449
commit bed8d3813a
11 changed files with 202 additions and 240 deletions

View file

@ -16,7 +16,6 @@
package graph package graph
import ( import (
"container/list"
"fmt" "fmt"
"strings" "strings"
) )
@ -28,7 +27,7 @@ type AndIterator struct {
internalIterators []Iterator internalIterators []Iterator
itCount int itCount int
primaryIt Iterator primaryIt Iterator
checkList *list.List checkList []Iterator
} }
// Creates a new And iterator. // Creates a new And iterator.
@ -62,14 +61,12 @@ func (it *AndIterator) Clone() Iterator {
return and return and
} }
// Returns a list.List of the subiterators, in order (primary iterator first). // Returns a slice of the subiterators, in order (primary iterator first).
func (it *AndIterator) GetSubIterators() *list.List { func (it *AndIterator) GetSubIterators() []Iterator {
l := list.New() iters := make([]Iterator, len(it.internalIterators)+1)
l.PushBack(it.primaryIt) iters[0] = it.primaryIt
for _, sub := range it.internalIterators { copy(iters[1:], it.internalIterators)
l.PushBack(sub) return iters
}
return l
} }
// 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
@ -169,14 +166,14 @@ func (it *AndIterator) checkSubIts(val TSVal) bool {
} }
func (it *AndIterator) checkCheckList(val TSVal) bool { func (it *AndIterator) checkCheckList(val TSVal) bool {
var isGood = true ok := true
for e := it.checkList.Front(); e != nil; e = e.Next() { for _, c := range it.checkList {
isGood = e.Value.(Iterator).Check(val) ok = c.Check(val)
if !isGood { if !ok {
break break
} }
} }
return CheckLogOut(it, val, isGood) return CheckLogOut(it, val, ok)
} }
// Check a value against the entire iterator, in order. // Check a value against the entire iterator, in order.

View file

@ -14,6 +14,10 @@
package graph package graph
import (
"sort"
)
// Perhaps the most tricky file in this entire module. Really a method on the // Perhaps the most tricky file in this entire module. Really a method on the
// AndIterator, but important enough to deserve its own file. // AndIterator, but important enough to deserve its own file.
// //
@ -31,42 +35,38 @@ package graph
// //
// In short, tread lightly. // In short, tread lightly.
import (
"container/list"
)
// Optimizes the AndIterator, by picking the most efficient way to Next() and // Optimizes the AndIterator, by picking the most efficient way to Next() and
// Check() its subiterators. For SQL fans, this is equivalent to JOIN. // Check() its subiterators. For SQL fans, this is equivalent to JOIN.
func (it *AndIterator) Optimize() (Iterator, bool) { func (it *AndIterator) Optimize() (Iterator, bool) {
// First, let's get the list of iterators, in order (first one is Next()ed, // First, let's get the slice of iterators, in order (first one is Next()ed,
// the rest are Check()ed) // the rest are Check()ed)
oldItList := it.GetSubIterators() old := it.GetSubIterators()
// And call Optimize() on our subtree, replacing each one in the order we // And call Optimize() on our subtree, replacing each one in the order we
// found them. it_list is the newly optimized versions of these, and changed // found them. it_list is the newly optimized versions of these, and changed
// is another list, of only the ones that have returned replacements and // is another list, of only the ones that have returned replacements and
// changed. // changed.
itList := optimizeSubIterators(oldItList) its := optimizeSubIterators(old)
// Close the replaced iterators (they ought to close themselves, but Close() // Close the replaced iterators (they ought to close themselves, but Close()
// is idempotent, so this just protects against any machinations). // is idempotent, so this just protects against any machinations).
closeIteratorList(oldItList, nil) closeIteratorList(old, nil)
// If we can find only one subiterator which is equivalent to this whole and, // If we can find only one subiterator which is equivalent to this whole and,
// we can replace the And... // we can replace the And...
out := it.optimizeReplacement(itList) out := it.optimizeReplacement(its)
if out != nil { if out != nil {
// ...Move the tags to the replacement... // ...Move the tags to the replacement...
moveTagsTo(out, it) moveTagsTo(out, it)
// ...Close everyone except `out`, our replacement... // ...Close everyone except `out`, our replacement...
closeIteratorList(itList, out) closeIteratorList(its, out)
// ...And return it. // ...And return it.
return out, true return out, true
} }
// And now, without changing any of the iterators, we reorder them. it_list is // And now, without changing any of the iterators, we reorder them. it_list is
// now a permutation of itself, but the contents are unchanged. // now a permutation of itself, but the contents are unchanged.
itList = optimizeOrder(itList) its = optimizeOrder(its)
// Okay! At this point we have an optimized order. // Okay! At this point we have an optimized order.
@ -75,8 +75,8 @@ func (it *AndIterator) Optimize() (Iterator, bool) {
newAnd := NewAndIterator() newAnd := NewAndIterator()
// Add the subiterators in order. // Add the subiterators in order.
for e := itList.Front(); e != nil; e = e.Next() { for _, sub := range its {
newAnd.AddSubIterator(e.Value.(Iterator)) newAnd.AddSubIterator(sub)
} }
// Move the tags hanging on us (like any good replacement). // Move the tags hanging on us (like any good replacement).
@ -93,35 +93,34 @@ func (it *AndIterator) Optimize() (Iterator, bool) {
// Closes a list of iterators, except the one passed in `except`. Closes all // Closes a list of iterators, except the one passed in `except`. Closes all
// of the iterators in the list if `except` is nil. // of the iterators in the list if `except` is nil.
func closeIteratorList(l *list.List, except Iterator) { func closeIteratorList(its []Iterator, except Iterator) {
for e := l.Front(); e != nil; e = e.Next() { for _, it := range its {
it := e.Value.(Iterator)
if it != except { if it != except {
e.Value.(Iterator).Close() it.Close()
} }
} }
} }
// Find if there is a single subiterator which is a valid replacement for this // Find if there is a single subiterator which is a valid replacement for this
// AndIterator. // AndIterator.
func (_ *AndIterator) optimizeReplacement(itList *list.List) Iterator { func (_ *AndIterator) optimizeReplacement(its []Iterator) Iterator {
// If we were created with no SubIterators, we're as good as Null. // If we were created with no SubIterators, we're as good as Null.
if itList.Len() == 0 { if len(its) == 0 {
return &NullIterator{} return &NullIterator{}
} }
if itList.Len() == 1 { if len(its) == 1 {
// When there's only one iterator, there's only one choice. // When there's only one iterator, there's only one choice.
return itList.Front().Value.(Iterator) return its[0]
} }
// If any of our subiterators, post-optimization, are also Null, then // If any of our subiterators, post-optimization, are also Null, then
// there's no point in continuing the branch, we will have no results // there's no point in continuing the branch, we will have no results
// and we are null as well. // and we are null as well.
if hasAnyNullIterators(itList) { if hasAnyNullIterators(its) {
return &NullIterator{} return &NullIterator{}
} }
// If we have one useful iterator, use that. // If we have one useful iterator, use that.
it := hasOneUsefulIterator(itList) it := hasOneUsefulIterator(its)
if it != nil { if it != nil {
return it return it
} }
@ -130,40 +129,40 @@ func (_ *AndIterator) optimizeReplacement(itList *list.List) Iterator {
// optimizeOrder(l) takes a list and returns a list, containing the same contents // optimizeOrder(l) takes a list and returns a list, containing the same contents
// but with a new ordering, however it wishes. // but with a new ordering, however it wishes.
func optimizeOrder(l *list.List) *list.List { func optimizeOrder(its []Iterator) []Iterator {
out := list.New() var (
var bestIt Iterator
bestCost := int64(1 << 62)
// bad contains iterators that can't be (efficiently) nexted, such as // bad contains iterators that can't be (efficiently) nexted, such as
// "optional" or "not". Separate them out and tack them on at the end. // "optional" or "not". Separate them out and tack them on at the end.
bad := list.New() out, bad []Iterator
best Iterator
bestCost = int64(1 << 62)
)
// Find the iterator with the projected "best" total cost. // Find the iterator with the projected "best" total cost.
// Total cost is defined as The Next()ed iterator's cost to Next() out // Total cost is defined as The Next()ed iterator's cost to Next() out
// 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 e := l.Front(); e != nil; e = e.Next() { for _, it := range its {
it := e.Value.(Iterator)
if !it.Nextable() { if !it.Nextable() {
bad.PushBack(it) bad = append(bad, it)
continue continue
} }
rootStats := e.Value.(Iterator).GetStats() rootStats := it.GetStats()
projectedCost := rootStats.NextCost cost := rootStats.NextCost
for f := l.Front(); f != nil; f = f.Next() { for _, f := range its {
if !f.Value.(Iterator).Nextable() { if !f.Nextable() {
continue continue
} }
if f == e { if f == it {
continue continue
} }
stats := f.Value.(Iterator).GetStats() stats := f.GetStats()
projectedCost += stats.CheckCost cost += stats.CheckCost
} }
projectedCost = projectedCost * rootStats.Size cost *= rootStats.Size
if projectedCost < bestCost { if cost < bestCost {
bestIt = it best = it
bestCost = projectedCost bestCost = cost
} }
} }
@ -172,63 +171,52 @@ func optimizeOrder(l *list.List) *list.List {
// useful (fail faster). // useful (fail faster).
// Put the best iterator (the one we wish to Next()) at the front... // Put the best iterator (the one we wish to Next()) at the front...
out.PushBack(bestIt) out = append(out, best)
// ...And push everyone else after...
for e := l.Front(); e != nil; e = e.Next() { // ... push everyone else after...
thisIt := e.Value.(Iterator) for _, it := range its {
if !thisIt.Nextable() { if !it.Nextable() {
continue continue
} }
if thisIt != bestIt { if it != best {
out.PushBack(thisIt) out = append(out, it)
} }
} }
// ...And finally, the difficult children on the end.
out.PushBackList(bad) // ...and finally, the difficult children on the end.
return out return append(out, bad...)
} }
type byCost []Iterator
func (c byCost) Len() int { return len(c) }
func (c byCost) Less(i, j int) bool { return c[i].GetStats().CheckCost < c[j].GetStats().CheckCost }
func (c byCost) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
// optimizeCheck(l) creates an alternate check list, containing the same contents // optimizeCheck(l) creates an alternate check list, containing the same contents
// but with a new ordering, however it wishes. // but with a new ordering, however it wishes.
func (it *AndIterator) optimizeCheck() { func (it *AndIterator) optimizeCheck() {
subIts := it.GetSubIterators() // GetSubIterators allocates, so this is currently safe.
out := list.New() // TODO(kortschak) Reuse it.checkList if possible.
// This involves providing GetSubIterators with a slice to fill.
// Find the iterator with the lowest Check() cost, push it to the front, repeat. // Generally this is a worthwhile thing to do in other places as well.
for subIts.Len() != 0 { it.checkList = it.GetSubIterators()
var best *list.Element sort.Sort(byCost(it.checkList))
bestCost := int64(1 << 62)
for e := subIts.Front(); e != nil; e = e.Next() {
it := e.Value.(Iterator)
rootStats := it.GetStats()
projectedCost := rootStats.CheckCost
if projectedCost < bestCost {
best = e
bestCost = projectedCost
}
}
out.PushBack(best.Value)
subIts.Remove(best)
}
it.checkList = out
} }
// If we're replacing ourselves by a single iterator, we need to grab the // If we're replacing ourselves by a single iterator, we need to grab the
// result tags from the iterators that, while still valid and would hold // result tags from the iterators that, while still valid and would hold
// the same values as this and, are not going to stay. // the same values as this and, are not going to stay.
// getSubTags() returns a map of the tags for all the subiterators. // getSubTags() returns a map of the tags for all the subiterators.
func (it *AndIterator) getSubTags() map[string]bool { func (it *AndIterator) getSubTags() map[string]struct{} {
subs := it.GetSubIterators() tags := make(map[string]struct{})
tags := make(map[string]bool) for _, sub := range it.GetSubIterators() {
for e := subs.Front(); e != nil; e = e.Next() { for _, tag := range sub.Tags() {
it := e.Value.(Iterator) tags[tag] = struct{}{}
for _, tag := range it.Tags() {
tags[tag] = true
} }
} }
for _, tag := range it.Tags() { for _, tag := range it.Tags() {
tags[tag] = true tags[tag] = struct{}{}
} }
return tags return tags
} }
@ -236,13 +224,13 @@ func (it *AndIterator) getSubTags() map[string]bool {
// moveTagsTo() gets the tags for all of the src's subiterators and the // moveTagsTo() gets the tags for all of the src's subiterators and the
// src itself, and moves them to dst. // src itself, and moves them to dst.
func moveTagsTo(dst Iterator, src *AndIterator) { func moveTagsTo(dst Iterator, src *AndIterator) {
tagmap := src.getSubTags() tags := src.getSubTags()
for _, tag := range dst.Tags() { for _, tag := range dst.Tags() {
if tagmap[tag] { if _, ok := tags[tag]; ok {
delete(tagmap, tag) delete(tags, tag)
} }
} }
for k, _ := range tagmap { for k := range tags {
dst.AddTag(k) dst.AddTag(k)
} }
} }
@ -251,24 +239,22 @@ func moveTagsTo(dst Iterator, src *AndIterator) {
// of them. It returns two lists -- the first contains the same list as l, where // of them. It returns two lists -- the first contains the same list as l, where
// any replacements are made by Optimize() and the second contains the originals // any replacements are made by Optimize() and the second contains the originals
// which were replaced. // which were replaced.
func optimizeSubIterators(l *list.List) *list.List { func optimizeSubIterators(its []Iterator) []Iterator {
itList := list.New() var optIts []Iterator
for e := l.Front(); e != nil; e = e.Next() { for _, it := range its {
it := e.Value.(Iterator) o, changed := it.Optimize()
newIt, change := it.Optimize() if changed {
if change { optIts = append(optIts, o)
itList.PushBack(newIt)
} else { } else {
itList.PushBack(it.Clone()) optIts = append(optIts, it.Clone())
} }
} }
return itList return optIts
} }
// Check a list of iterators for any Null iterators. // Check a list of iterators for any Null iterators.
func hasAnyNullIterators(l *list.List) bool { func hasAnyNullIterators(its []Iterator) bool {
for e := l.Front(); e != nil; e = e.Next() { for _, it := range its {
it := e.Value.(Iterator)
if it.Type() == "null" { if it.Type() == "null" {
return true return true
} }
@ -280,11 +266,10 @@ func hasAnyNullIterators(l *list.List) bool {
// nothing, and "all" which returns everything. Particularly, we want // nothing, and "all" which returns everything. Particularly, we want
// to see if we're intersecting with a bunch of "all" iterators, and, // to see if we're intersecting with a bunch of "all" iterators, and,
// if we are, then we have only one useful iterator. // if we are, then we have only one useful iterator.
func hasOneUsefulIterator(l *list.List) Iterator { func hasOneUsefulIterator(its []Iterator) Iterator {
usefulCount := 0 usefulCount := 0
var usefulIt Iterator var usefulIt Iterator
for e := l.Front(); e != nil; e = e.Next() { for _, it := range its {
it := e.Value.(Iterator)
switch it.Type() { switch it.Type() {
case "null", "all": case "null", "all":
continue continue

View file

@ -79,9 +79,8 @@ func TestReorderWithTag(t *testing.T) {
} }
expectedTags := []string{"good", "slow"} expectedTags := []string{"good", "slow"}
tagsOut := make([]string, 0) tagsOut := make([]string, 0)
l := newIt.GetSubIterators() for _, sub := range newIt.GetSubIterators() {
for e := l.Front(); e != nil; e = e.Next() { for _, x := range sub.Tags() {
for _, x := range e.Value.(Iterator).Tags() {
tagsOut = append(tagsOut, x) tagsOut = append(tagsOut, x)
} }
} }

View file

@ -34,7 +34,6 @@ package graph
// Alternatively, can be seen as the dual of the LinksTo iterator. // Alternatively, can be seen as the dual of the LinksTo iterator.
import ( import (
"container/list"
"fmt" "fmt"
"strings" "strings"
@ -63,11 +62,9 @@ func NewHasaIterator(ts TripleStore, subIt Iterator, dir string) *HasaIterator {
return &hasa return &hasa
} }
// Return our sole subiterator, in a list.List. // Return our sole subiterator.
func (it *HasaIterator) GetSubIterators() *list.List { func (it *HasaIterator) GetSubIterators() []Iterator {
l := list.New() return []Iterator{it.primaryIt}
l.PushBack(it.primaryIt)
return l
} }
func (it *HasaIterator) Reset() { func (it *HasaIterator) Reset() {

View file

@ -18,7 +18,6 @@ package graph
// iterators can "inherit" from to get default iterator functionality. // iterators can "inherit" from to get default iterator functionality.
import ( import (
"container/list"
"fmt" "fmt"
"strings" "strings"
@ -90,8 +89,9 @@ type Iterator interface {
// around internally. if it chooses to replace it with a better iterator, // around internally. if it chooses to replace it with a better iterator,
// returns (the new iterator, true), if not, it returns (self, false). // returns (the new iterator, true), if not, it returns (self, false).
Optimize() (Iterator, bool) Optimize() (Iterator, bool)
// Return a list of the subiterators for this iterator.
GetSubIterators() *list.List // Return a slice of the subiterators for this iterator.
GetSubIterators() []Iterator
// Return a string representation of the iterator, indented by the given amount. // Return a string representation of the iterator, indented by the given amount.
DebugString(int) string DebugString(int) string
@ -170,18 +170,18 @@ func (it *BaseIterator) CopyTagsFrom(other_it Iterator) {
} }
// Prints a silly debug string. Most classes override. // Prints a silly debug string. Most classes override.
func (n *BaseIterator) DebugString(indent int) string { func (it *BaseIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(base)", strings.Repeat(" ", indent)) return fmt.Sprintf("%s(base)", strings.Repeat(" ", indent))
} }
// Nothing in a base iterator. // Nothing in a base iterator.
func (n *BaseIterator) Check(v TSVal) bool { func (it *BaseIterator) Check(v TSVal) bool {
return false return false
} }
// Base iterators should never appear in a tree if they are, select against // Base iterators should never appear in a tree if they are, select against
// them. // them.
func (n *BaseIterator) GetStats() *IteratorStats { func (it *BaseIterator) GetStats() *IteratorStats {
return &IteratorStats{100000, 100000, 100000} return &IteratorStats{100000, 100000, 100000}
} }
@ -211,7 +211,7 @@ func (it *BaseIterator) Size() (int64, bool) {
} }
// No subiterators. Only those with subiterators need to do anything here. // No subiterators. Only those with subiterators need to do anything here.
func (it *BaseIterator) GetSubIterators() *list.List { func (it *BaseIterator) GetSubIterators() []Iterator {
return nil return nil
} }
@ -231,7 +231,8 @@ func (it *BaseIterator) TagResults(out_map *map[string]TSVal) {
} }
// Nothing to clean up. // Nothing to clean up.
//func (a *BaseIterator) Close() {} // func (it *BaseIterator) Close() {}
func (it *NullIterator) Close() {} func (it *NullIterator) Close() {}
func (it *BaseIterator) Reset() {} func (it *BaseIterator) Reset() {}

View file

@ -28,21 +28,21 @@ func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool
} }
func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) {
l := it.GetSubIterators() subs := it.GetSubIterators()
if l.Len() != 1 { if len(subs) != 1 {
return it, false return it, false
} }
primaryIt := l.Front().Value.(graph.Iterator) primary := subs[0]
if primaryIt.Type() == "fixed" { if primary.Type() == "fixed" {
size, _ := primaryIt.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := primaryIt.Next() val, ok := primary.Next()
if !ok { if !ok {
panic("Sizes lie") panic("Sizes lie")
} }
newIt := ts.GetTripleIterator(it.Direction(), val) newIt := ts.GetTripleIterator(it.Direction(), val)
newIt.CopyTagsFrom(it) newIt.CopyTagsFrom(it)
for _, tag := range primaryIt.Tags() { for _, tag := range primary.Tags() {
newIt.AddFixedTag(tag, val) newIt.AddFixedTag(tag, val)
} }
it.Close() it.Close()

View file

@ -30,7 +30,6 @@ package graph
// Can be seen as the dual of the HasA iterator. // Can be seen as the dual of the HasA iterator.
import ( import (
"container/list"
"fmt" "fmt"
"strings" "strings"
) )
@ -58,120 +57,118 @@ func NewLinksToIterator(ts TripleStore, it Iterator, dir string) *LinksToIterato
return &lto return &lto
} }
func (l *LinksToIterator) Reset() { func (it *LinksToIterator) Reset() {
l.primaryIt.Reset() it.primaryIt.Reset()
if l.nextIt != nil { if it.nextIt != nil {
l.nextIt.Close() it.nextIt.Close()
} }
l.nextIt = &NullIterator{} it.nextIt = &NullIterator{}
} }
func (l *LinksToIterator) Clone() Iterator { func (it *LinksToIterator) Clone() Iterator {
out := NewLinksToIterator(l.ts, l.primaryIt.Clone(), l.direction) out := NewLinksToIterator(it.ts, it.primaryIt.Clone(), it.direction)
out.CopyTagsFrom(l) out.CopyTagsFrom(it)
return out return out
} }
// Return the direction under consideration. // Return the direction under consideration.
func (l *LinksToIterator) Direction() string { return l.direction } func (it *LinksToIterator) Direction() string { return it.direction }
// Tag these results, and our subiterator's results. // Tag these results, and our subiterator's results.
func (l *LinksToIterator) TagResults(out *map[string]TSVal) { func (it *LinksToIterator) TagResults(out *map[string]TSVal) {
l.BaseIterator.TagResults(out) it.BaseIterator.TagResults(out)
l.primaryIt.TagResults(out) it.primaryIt.TagResults(out)
} }
// DEPRECATED // DEPRECATED
func (l *LinksToIterator) GetResultTree() *ResultTree { func (it *LinksToIterator) GetResultTree() *ResultTree {
tree := NewResultTree(l.LastResult()) tree := NewResultTree(it.LastResult())
tree.AddSubtree(l.primaryIt.GetResultTree()) tree.AddSubtree(it.primaryIt.GetResultTree())
return tree return tree
} }
// Print the iterator. // Print the iterator.
func (l *LinksToIterator) DebugString(indent int) string { func (it *LinksToIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s %d direction:%s\n%s)", return fmt.Sprintf("%s(%s %d direction:%s\n%s)",
strings.Repeat(" ", indent), strings.Repeat(" ", indent),
l.Type(), l.GetUid(), l.direction, l.primaryIt.DebugString(indent+4)) it.Type(), it.GetUid(), it.direction, it.primaryIt.DebugString(indent+4))
} }
// If it checks in the right direction for the subiterator, it is a valid link // If it checks in the right direction for the subiterator, it is a valid link
// for the LinksTo. // for the LinksTo.
func (l *LinksToIterator) Check(val TSVal) bool { func (it *LinksToIterator) Check(val TSVal) bool {
CheckLogIn(l, val) CheckLogIn(it, val)
node := l.ts.GetTripleDirection(val, l.direction) node := it.ts.GetTripleDirection(val, it.direction)
if l.primaryIt.Check(node) { if it.primaryIt.Check(node) {
l.Last = val it.Last = val
return CheckLogOut(l, val, true) return CheckLogOut(it, val, true)
} }
return CheckLogOut(l, val, false) return CheckLogOut(it, val, false)
} }
// Return a list containing only our subiterator. // Return a list containing only our subiterator.
func (lto *LinksToIterator) GetSubIterators() *list.List { func (it *LinksToIterator) GetSubIterators() []Iterator {
l := list.New() return []Iterator{it.primaryIt}
l.PushBack(lto.primaryIt)
return l
} }
// Optimize the LinksTo, by replacing it if it can be. // Optimize the LinksTo, by replacing it if it can be.
func (lto *LinksToIterator) Optimize() (Iterator, bool) { func (it *LinksToIterator) Optimize() (Iterator, bool) {
newPrimary, changed := lto.primaryIt.Optimize() newPrimary, changed := it.primaryIt.Optimize()
if changed { if changed {
lto.primaryIt = newPrimary it.primaryIt = newPrimary
if lto.primaryIt.Type() == "null" { if it.primaryIt.Type() == "null" {
lto.nextIt.Close() it.nextIt.Close()
return lto.primaryIt, true return it.primaryIt, true
} }
} }
// Ask the TripleStore if we can be replaced. Often times, this is a great // Ask the TripleStore if we can be replaced. Often times, this is a great
// optimization opportunity (there's a fixed iterator underneath us, for // optimization opportunity (there's a fixed iterator underneath us, for
// example). // example).
newReplacement, hasOne := lto.ts.OptimizeIterator(lto) newReplacement, hasOne := it.ts.OptimizeIterator(it)
if hasOne { if hasOne {
lto.Close() it.Close()
return newReplacement, true return newReplacement, true
} }
return lto, false return it, false
} }
// Next()ing a LinksTo operates as described above. // Next()ing a LinksTo operates as described above.
func (l *LinksToIterator) Next() (TSVal, bool) { func (it *LinksToIterator) Next() (TSVal, bool) {
NextLogIn(l) NextLogIn(it)
val, ok := l.nextIt.Next() val, ok := it.nextIt.Next()
if !ok { if !ok {
// Subiterator is empty, get another one // Subiterator is empty, get another one
candidate, ok := l.primaryIt.Next() candidate, ok := it.primaryIt.Next()
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 NextLogOut(l, 0, false) return NextLogOut(it, 0, false)
} }
l.nextIt.Close() it.nextIt.Close()
l.nextIt = l.ts.GetTripleIterator(l.direction, candidate) it.nextIt = it.ts.GetTripleIterator(it.direction, candidate)
// Recurse -- return the first in the next set. // Recurse -- return the first in the next set.
return l.Next() return it.Next()
} }
l.Last = val it.Last = val
return NextLogOut(l, val, ok) return NextLogOut(it, val, ok)
} }
// Close our subiterators. // Close our subiterators.
func (l *LinksToIterator) Close() { func (it *LinksToIterator) Close() {
l.nextIt.Close() it.nextIt.Close()
l.primaryIt.Close() it.primaryIt.Close()
} }
// We won't ever have a new result, but our subiterators might. // We won't ever have a new result, but our subiterators might.
func (l *LinksToIterator) NextResult() bool { func (it *LinksToIterator) NextResult() bool {
return l.primaryIt.NextResult() return it.primaryIt.NextResult()
} }
// Register the LinksTo. // Register the LinksTo.
func (l *LinksToIterator) Type() string { return "linksto" } func (it *LinksToIterator) Type() string { return "linksto" }
// Return a guess as to how big or costly it is to next the iterator. // Return a guess as to how big or costly it is to next the iterator.
func (l *LinksToIterator) GetStats() *IteratorStats { func (it *LinksToIterator) GetStats() *IteratorStats {
subitStats := l.primaryIt.GetStats() subitStats := it.primaryIt.GetStats()
// TODO(barakmich): These should really come from the triplestore itself // TODO(barakmich): These should really come from the triplestore itself
fanoutFactor := int64(20) fanoutFactor := int64(20)
checkConstant := int64(1) checkConstant := int64(1)

View file

@ -28,21 +28,21 @@ func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool
} }
func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) {
l := it.GetSubIterators() subs := it.GetSubIterators()
if l.Len() != 1 { if len(subs) != 1 {
return it, false return it, false
} }
primaryIt := l.Front().Value.(graph.Iterator) primary := subs[0]
if primaryIt.Type() == "fixed" { if primary.Type() == "fixed" {
size, _ := primaryIt.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := primaryIt.Next() val, ok := primary.Next()
if !ok { if !ok {
panic("Sizes lie") panic("Sizes lie")
} }
newIt := ts.GetTripleIterator(it.Direction(), val) newIt := ts.GetTripleIterator(it.Direction(), val)
newIt.CopyTagsFrom(it) newIt.CopyTagsFrom(it)
for _, tag := range primaryIt.Tags() { for _, tag := range primary.Tags() {
newIt.AddFixedTag(tag, val) newIt.AddFixedTag(tag, val)
} }
return newIt, true return newIt, true

View file

@ -28,21 +28,21 @@ func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool
} }
func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) {
l := it.GetSubIterators() subs := it.GetSubIterators()
if l.Len() != 1 { if len(subs) != 1 {
return it, false return it, false
} }
primaryIt := l.Front().Value.(graph.Iterator) primary := subs[0]
if primaryIt.Type() == "fixed" { if primary.Type() == "fixed" {
size, _ := primaryIt.Size() size, _ := primary.Size()
if size == 1 { if size == 1 {
val, ok := primaryIt.Next() val, ok := primary.Next()
if !ok { if !ok {
panic("Sizes lie") panic("Sizes lie")
} }
newIt := ts.GetTripleIterator(it.Direction(), val) newIt := ts.GetTripleIterator(it.Direction(), val)
newIt.CopyTagsFrom(it) newIt.CopyTagsFrom(it)
for _, tag := range primaryIt.Tags() { for _, tag := range primary.Tags() {
newIt.AddFixedTag(tag, val) newIt.AddFixedTag(tag, val)
} }
it.Close() it.Close()

View file

@ -22,7 +22,6 @@ package graph
// May return the same value twice -- once for each branch. // May return the same value twice -- once for each branch.
import ( import (
"container/list"
"fmt" "fmt"
"strings" "strings"
) )
@ -75,13 +74,9 @@ func (it *OrIterator) Clone() Iterator {
return or return or
} }
// Returns a list.List of the subiterators, in order. // Returns a list.List of the subiterators, in order. The returned slice must not be modified.
func (it *OrIterator) GetSubIterators() *list.List { func (it *OrIterator) GetSubIterators() []Iterator {
l := list.New() return it.internalIterators
for _, sub := range it.internalIterators {
l.PushBack(sub)
}
return l
} }
// 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
@ -236,17 +231,17 @@ func (it *OrIterator) Close() {
} }
func (it *OrIterator) Optimize() (Iterator, bool) { func (it *OrIterator) Optimize() (Iterator, bool) {
oldItList := it.GetSubIterators() old := it.GetSubIterators()
itList := optimizeSubIterators(oldItList) optIts := optimizeSubIterators(old)
// Close the replaced iterators (they ought to close themselves, but Close() // Close the replaced iterators (they ought to close themselves, but Close()
// is idempotent, so this just protects against any machinations). // is idempotent, so this just protects against any machinations).
closeIteratorList(oldItList, nil) closeIteratorList(old, nil)
newOr := NewOrIterator() newOr := NewOrIterator()
newOr.isShortCircuiting = it.isShortCircuiting newOr.isShortCircuiting = it.isShortCircuiting
// Add the subiterators in order. // Add the subiterators in order.
for e := itList.Front(); e != nil; e = e.Next() { for _, o := range optIts {
newOr.AddSubIterator(e.Value.(Iterator)) newOr.AddSubIterator(o)
} }
// Move the tags hanging on us (like any good replacement). // Move the tags hanging on us (like any good replacement).

View file

@ -105,12 +105,7 @@ func (qs *queryShape) StealNode(left *Node, right *Node) {
} }
func (qs *queryShape) MakeNode(it Iterator) *Node { func (qs *queryShape) MakeNode(it Iterator) *Node {
var n Node n := Node{Id: qs.nodeId}
n.IsLinkNode = false
n.IsFixed = false
n.Id = qs.nodeId
n.Tags = make([]string, 0)
n.Values = make([]string, 0)
for _, tag := range it.Tags() { for _, tag := range it.Tags() {
n.Tags = append(n.Tags, tag) n.Tags = append(n.Tags, tag)
} }
@ -120,12 +115,10 @@ func (qs *queryShape) MakeNode(it Iterator) *Node {
switch it.Type() { switch it.Type() {
case "and": case "and":
list := it.GetSubIterators() for _, sub := range it.GetSubIterators() {
for e := list.Front(); e != nil; e = e.Next() {
subit := e.Value.(Iterator)
qs.nodeId++ qs.nodeId++
newNode := qs.MakeNode(subit) newNode := qs.MakeNode(sub)
if subit.Type() != "or" { if sub.Type() != "or" {
qs.StealNode(&n, newNode) qs.StealNode(&n, newNode)
} else { } else {
qs.AddNode(newNode) qs.AddNode(newNode)
@ -149,12 +142,10 @@ func (qs *queryShape) MakeNode(it Iterator) *Node {
qs.AddNode(newNode) qs.AddNode(newNode)
qs.RemoveHasa() qs.RemoveHasa()
case "or": case "or":
list := it.GetSubIterators() for _, sub := range it.GetSubIterators() {
for e := list.Front(); e != nil; e = e.Next() {
subit := e.Value.(Iterator)
qs.nodeId++ qs.nodeId++
newNode := qs.MakeNode(subit) newNode := qs.MakeNode(sub)
if subit.Type() == "or" { if sub.Type() == "or" {
qs.StealNode(&n, newNode) qs.StealNode(&n, newNode)
} else { } else {
qs.AddNode(newNode) qs.AddNode(newNode)