diff --git a/graph/and_iterator.go b/graph/and_iterator.go index f3f49e6..f4a17b7 100644 --- a/graph/and_iterator.go +++ b/graph/and_iterator.go @@ -16,7 +16,6 @@ package graph import ( - "container/list" "fmt" "strings" ) @@ -28,7 +27,7 @@ type AndIterator struct { internalIterators []Iterator itCount int primaryIt Iterator - checkList *list.List + checkList []Iterator } // Creates a new And iterator. @@ -62,14 +61,12 @@ func (it *AndIterator) Clone() Iterator { return and } -// Returns a list.List of the subiterators, in order (primary iterator first). -func (it *AndIterator) GetSubIterators() *list.List { - l := list.New() - l.PushBack(it.primaryIt) - for _, sub := range it.internalIterators { - l.PushBack(sub) - } - return l +// Returns a slice of the subiterators, in order (primary iterator first). +func (it *AndIterator) GetSubIterators() []Iterator { + iters := make([]Iterator, len(it.internalIterators)+1) + iters[0] = it.primaryIt + copy(iters[1:], it.internalIterators) + return iters } // 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 { - var isGood = true - for e := it.checkList.Front(); e != nil; e = e.Next() { - isGood = e.Value.(Iterator).Check(val) - if !isGood { + ok := true + for _, c := range it.checkList { + ok = c.Check(val) + if !ok { break } } - return CheckLogOut(it, val, isGood) + return CheckLogOut(it, val, ok) } // Check a value against the entire iterator, in order. diff --git a/graph/and_iterator_optimize.go b/graph/and_iterator_optimize.go index f7e5141..1850865 100644 --- a/graph/and_iterator_optimize.go +++ b/graph/and_iterator_optimize.go @@ -14,6 +14,10 @@ package graph +import ( + "sort" +) + // Perhaps the most tricky file in this entire module. Really a method on the // AndIterator, but important enough to deserve its own file. // @@ -31,42 +35,38 @@ package graph // // In short, tread lightly. -import ( - "container/list" -) - // Optimizes the AndIterator, by picking the most efficient way to Next() and // Check() its subiterators. For SQL fans, this is equivalent to JOIN. 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) - oldItList := it.GetSubIterators() + old := it.GetSubIterators() // 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 // is another list, of only the ones that have returned replacements and // changed. - itList := optimizeSubIterators(oldItList) + its := optimizeSubIterators(old) // Close the replaced iterators (they ought to close themselves, but Close() // 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, // we can replace the And... - out := it.optimizeReplacement(itList) + out := it.optimizeReplacement(its) if out != nil { // ...Move the tags to the replacement... moveTagsTo(out, it) // ...Close everyone except `out`, our replacement... - closeIteratorList(itList, out) + closeIteratorList(its, out) // ...And return it. return out, true } // And now, without changing any of the iterators, we reorder them. it_list is // 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. @@ -75,8 +75,8 @@ func (it *AndIterator) Optimize() (Iterator, bool) { newAnd := NewAndIterator() // Add the subiterators in order. - for e := itList.Front(); e != nil; e = e.Next() { - newAnd.AddSubIterator(e.Value.(Iterator)) + for _, sub := range its { + newAnd.AddSubIterator(sub) } // 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 // of the iterators in the list if `except` is nil. -func closeIteratorList(l *list.List, except Iterator) { - for e := l.Front(); e != nil; e = e.Next() { - it := e.Value.(Iterator) +func closeIteratorList(its []Iterator, except Iterator) { + for _, it := range its { if it != except { - e.Value.(Iterator).Close() + it.Close() } } } // Find if there is a single subiterator which is a valid replacement for this // 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 itList.Len() == 0 { + if len(its) == 0 { return &NullIterator{} } - if itList.Len() == 1 { + if len(its) == 1 { // 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 // there's no point in continuing the branch, we will have no results // and we are null as well. - if hasAnyNullIterators(itList) { + if hasAnyNullIterators(its) { return &NullIterator{} } // If we have one useful iterator, use that. - it := hasOneUsefulIterator(itList) + it := hasOneUsefulIterator(its) if it != nil { 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 // but with a new ordering, however it wishes. -func optimizeOrder(l *list.List) *list.List { - out := list.New() - var bestIt Iterator - bestCost := int64(1 << 62) - // bad contains iterators that can't be (efficiently) nexted, such as - // "optional" or "not". Separate them out and tack them on at the end. - bad := list.New() +func optimizeOrder(its []Iterator) []Iterator { + var ( + // bad contains iterators that can't be (efficiently) nexted, such as + // "optional" or "not". Separate them out and tack them on at the end. + out, bad []Iterator + best Iterator + bestCost = int64(1 << 62) + ) // Find the iterator with the projected "best" total cost. // 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 // else. - for e := l.Front(); e != nil; e = e.Next() { - it := e.Value.(Iterator) + for _, it := range its { if !it.Nextable() { - bad.PushBack(it) + bad = append(bad, it) continue } - rootStats := e.Value.(Iterator).GetStats() - projectedCost := rootStats.NextCost - for f := l.Front(); f != nil; f = f.Next() { - if !f.Value.(Iterator).Nextable() { + rootStats := it.GetStats() + cost := rootStats.NextCost + for _, f := range its { + if !f.Nextable() { continue } - if f == e { + if f == it { continue } - stats := f.Value.(Iterator).GetStats() - projectedCost += stats.CheckCost + stats := f.GetStats() + cost += stats.CheckCost } - projectedCost = projectedCost * rootStats.Size - if projectedCost < bestCost { - bestIt = it - bestCost = projectedCost + cost *= rootStats.Size + if cost < bestCost { + best = it + bestCost = cost } } @@ -172,63 +171,52 @@ func optimizeOrder(l *list.List) *list.List { // useful (fail faster). // Put the best iterator (the one we wish to Next()) at the front... - out.PushBack(bestIt) - // ...And push everyone else after... - for e := l.Front(); e != nil; e = e.Next() { - thisIt := e.Value.(Iterator) - if !thisIt.Nextable() { + out = append(out, best) + + // ... push everyone else after... + for _, it := range its { + if !it.Nextable() { continue } - if thisIt != bestIt { - out.PushBack(thisIt) + if it != best { + out = append(out, it) } } - // ...And finally, the difficult children on the end. - out.PushBackList(bad) - return out + + // ...and finally, the difficult children on the end. + 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 // but with a new ordering, however it wishes. func (it *AndIterator) optimizeCheck() { - subIts := it.GetSubIterators() - out := list.New() - - // Find the iterator with the lowest Check() cost, push it to the front, repeat. - for subIts.Len() != 0 { - var best *list.Element - 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 + // GetSubIterators allocates, so this is currently safe. + // TODO(kortschak) Reuse it.checkList if possible. + // This involves providing GetSubIterators with a slice to fill. + // Generally this is a worthwhile thing to do in other places as well. + it.checkList = it.GetSubIterators() + sort.Sort(byCost(it.checkList)) } // 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 // the same values as this and, are not going to stay. // getSubTags() returns a map of the tags for all the subiterators. -func (it *AndIterator) getSubTags() map[string]bool { - subs := it.GetSubIterators() - tags := make(map[string]bool) - for e := subs.Front(); e != nil; e = e.Next() { - it := e.Value.(Iterator) - for _, tag := range it.Tags() { - tags[tag] = true +func (it *AndIterator) getSubTags() map[string]struct{} { + tags := make(map[string]struct{}) + for _, sub := range it.GetSubIterators() { + for _, tag := range sub.Tags() { + tags[tag] = struct{}{} } } for _, tag := range it.Tags() { - tags[tag] = true + tags[tag] = struct{}{} } 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 // src itself, and moves them to dst. func moveTagsTo(dst Iterator, src *AndIterator) { - tagmap := src.getSubTags() + tags := src.getSubTags() for _, tag := range dst.Tags() { - if tagmap[tag] { - delete(tagmap, tag) + if _, ok := tags[tag]; ok { + delete(tags, tag) } } - for k, _ := range tagmap { + for k := range tags { 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 // any replacements are made by Optimize() and the second contains the originals // which were replaced. -func optimizeSubIterators(l *list.List) *list.List { - itList := list.New() - for e := l.Front(); e != nil; e = e.Next() { - it := e.Value.(Iterator) - newIt, change := it.Optimize() - if change { - itList.PushBack(newIt) +func optimizeSubIterators(its []Iterator) []Iterator { + var optIts []Iterator + for _, it := range its { + o, changed := it.Optimize() + if changed { + optIts = append(optIts, o) } else { - itList.PushBack(it.Clone()) + optIts = append(optIts, it.Clone()) } } - return itList + return optIts } // Check a list of iterators for any Null iterators. -func hasAnyNullIterators(l *list.List) bool { - for e := l.Front(); e != nil; e = e.Next() { - it := e.Value.(Iterator) +func hasAnyNullIterators(its []Iterator) bool { + for _, it := range its { if it.Type() == "null" { return true } @@ -280,11 +266,10 @@ func hasAnyNullIterators(l *list.List) bool { // nothing, and "all" which returns everything. Particularly, we want // to see if we're intersecting with a bunch of "all" iterators, and, // if we are, then we have only one useful iterator. -func hasOneUsefulIterator(l *list.List) Iterator { +func hasOneUsefulIterator(its []Iterator) Iterator { usefulCount := 0 var usefulIt Iterator - for e := l.Front(); e != nil; e = e.Next() { - it := e.Value.(Iterator) + for _, it := range its { switch it.Type() { case "null", "all": continue diff --git a/graph/and_iterator_optimize_test.go b/graph/and_iterator_optimize_test.go index 4ecee81..8f8955b 100644 --- a/graph/and_iterator_optimize_test.go +++ b/graph/and_iterator_optimize_test.go @@ -79,9 +79,8 @@ func TestReorderWithTag(t *testing.T) { } expectedTags := []string{"good", "slow"} tagsOut := make([]string, 0) - l := newIt.GetSubIterators() - for e := l.Front(); e != nil; e = e.Next() { - for _, x := range e.Value.(Iterator).Tags() { + for _, sub := range newIt.GetSubIterators() { + for _, x := range sub.Tags() { tagsOut = append(tagsOut, x) } } diff --git a/graph/hasa_iterator.go b/graph/hasa_iterator.go index b29f587..c177538 100644 --- a/graph/hasa_iterator.go +++ b/graph/hasa_iterator.go @@ -34,7 +34,6 @@ package graph // Alternatively, can be seen as the dual of the LinksTo iterator. import ( - "container/list" "fmt" "strings" @@ -63,11 +62,9 @@ func NewHasaIterator(ts TripleStore, subIt Iterator, dir string) *HasaIterator { return &hasa } -// Return our sole subiterator, in a list.List. -func (it *HasaIterator) GetSubIterators() *list.List { - l := list.New() - l.PushBack(it.primaryIt) - return l +// Return our sole subiterator. +func (it *HasaIterator) GetSubIterators() []Iterator { + return []Iterator{it.primaryIt} } func (it *HasaIterator) Reset() { diff --git a/graph/iterator.go b/graph/iterator.go index 26a02c0..0bffb01 100644 --- a/graph/iterator.go +++ b/graph/iterator.go @@ -18,7 +18,6 @@ package graph // iterators can "inherit" from to get default iterator functionality. import ( - "container/list" "fmt" "strings" @@ -90,8 +89,9 @@ type Iterator interface { // around internally. if it chooses to replace it with a better iterator, // returns (the new iterator, true), if not, it returns (self, false). 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. DebugString(int) string @@ -170,18 +170,18 @@ func (it *BaseIterator) CopyTagsFrom(other_it Iterator) { } // 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)) } // Nothing in a base iterator. -func (n *BaseIterator) Check(v TSVal) bool { +func (it *BaseIterator) Check(v TSVal) bool { return false } // Base iterators should never appear in a tree if they are, select against // them. -func (n *BaseIterator) GetStats() *IteratorStats { +func (it *BaseIterator) GetStats() *IteratorStats { 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. -func (it *BaseIterator) GetSubIterators() *list.List { +func (it *BaseIterator) GetSubIterators() []Iterator { return nil } @@ -231,7 +231,8 @@ func (it *BaseIterator) TagResults(out_map *map[string]TSVal) { } // Nothing to clean up. -//func (a *BaseIterator) Close() {} +// func (it *BaseIterator) Close() {} + func (it *NullIterator) Close() {} func (it *BaseIterator) Reset() {} diff --git a/graph/leveldb/triplestore_iterator_optimize.go b/graph/leveldb/triplestore_iterator_optimize.go index 259e6af..cf31f71 100644 --- a/graph/leveldb/triplestore_iterator_optimize.go +++ b/graph/leveldb/triplestore_iterator_optimize.go @@ -28,21 +28,21 @@ func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool } func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { - l := it.GetSubIterators() - if l.Len() != 1 { + subs := it.GetSubIterators() + if len(subs) != 1 { return it, false } - primaryIt := l.Front().Value.(graph.Iterator) - if primaryIt.Type() == "fixed" { - size, _ := primaryIt.Size() + primary := subs[0] + if primary.Type() == "fixed" { + size, _ := primary.Size() if size == 1 { - val, ok := primaryIt.Next() + val, ok := primary.Next() if !ok { panic("Sizes lie") } newIt := ts.GetTripleIterator(it.Direction(), val) newIt.CopyTagsFrom(it) - for _, tag := range primaryIt.Tags() { + for _, tag := range primary.Tags() { newIt.AddFixedTag(tag, val) } it.Close() diff --git a/graph/linksto_iterator.go b/graph/linksto_iterator.go index 45c5e2f..61a42cb 100644 --- a/graph/linksto_iterator.go +++ b/graph/linksto_iterator.go @@ -30,7 +30,6 @@ package graph // Can be seen as the dual of the HasA iterator. import ( - "container/list" "fmt" "strings" ) @@ -58,120 +57,118 @@ func NewLinksToIterator(ts TripleStore, it Iterator, dir string) *LinksToIterato return <o } -func (l *LinksToIterator) Reset() { - l.primaryIt.Reset() - if l.nextIt != nil { - l.nextIt.Close() +func (it *LinksToIterator) Reset() { + it.primaryIt.Reset() + if it.nextIt != nil { + it.nextIt.Close() } - l.nextIt = &NullIterator{} + it.nextIt = &NullIterator{} } -func (l *LinksToIterator) Clone() Iterator { - out := NewLinksToIterator(l.ts, l.primaryIt.Clone(), l.direction) - out.CopyTagsFrom(l) +func (it *LinksToIterator) Clone() Iterator { + out := NewLinksToIterator(it.ts, it.primaryIt.Clone(), it.direction) + out.CopyTagsFrom(it) return out } // 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. -func (l *LinksToIterator) TagResults(out *map[string]TSVal) { - l.BaseIterator.TagResults(out) - l.primaryIt.TagResults(out) +func (it *LinksToIterator) TagResults(out *map[string]TSVal) { + it.BaseIterator.TagResults(out) + it.primaryIt.TagResults(out) } // DEPRECATED -func (l *LinksToIterator) GetResultTree() *ResultTree { - tree := NewResultTree(l.LastResult()) - tree.AddSubtree(l.primaryIt.GetResultTree()) +func (it *LinksToIterator) GetResultTree() *ResultTree { + tree := NewResultTree(it.LastResult()) + tree.AddSubtree(it.primaryIt.GetResultTree()) return tree } // 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)", 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 // for the LinksTo. -func (l *LinksToIterator) Check(val TSVal) bool { - CheckLogIn(l, val) - node := l.ts.GetTripleDirection(val, l.direction) - if l.primaryIt.Check(node) { - l.Last = val - return CheckLogOut(l, val, true) +func (it *LinksToIterator) Check(val TSVal) bool { + CheckLogIn(it, val) + node := it.ts.GetTripleDirection(val, it.direction) + if it.primaryIt.Check(node) { + it.Last = val + return CheckLogOut(it, val, true) } - return CheckLogOut(l, val, false) + return CheckLogOut(it, val, false) } // Return a list containing only our subiterator. -func (lto *LinksToIterator) GetSubIterators() *list.List { - l := list.New() - l.PushBack(lto.primaryIt) - return l +func (it *LinksToIterator) GetSubIterators() []Iterator { + return []Iterator{it.primaryIt} } // Optimize the LinksTo, by replacing it if it can be. -func (lto *LinksToIterator) Optimize() (Iterator, bool) { - newPrimary, changed := lto.primaryIt.Optimize() +func (it *LinksToIterator) Optimize() (Iterator, bool) { + newPrimary, changed := it.primaryIt.Optimize() if changed { - lto.primaryIt = newPrimary - if lto.primaryIt.Type() == "null" { - lto.nextIt.Close() - return lto.primaryIt, true + it.primaryIt = newPrimary + if it.primaryIt.Type() == "null" { + it.nextIt.Close() + return it.primaryIt, true } } // Ask the TripleStore if we can be replaced. Often times, this is a great // optimization opportunity (there's a fixed iterator underneath us, for // example). - newReplacement, hasOne := lto.ts.OptimizeIterator(lto) + newReplacement, hasOne := it.ts.OptimizeIterator(it) if hasOne { - lto.Close() + it.Close() return newReplacement, true } - return lto, false + return it, false } // Next()ing a LinksTo operates as described above. -func (l *LinksToIterator) Next() (TSVal, bool) { - NextLogIn(l) - val, ok := l.nextIt.Next() +func (it *LinksToIterator) Next() (TSVal, bool) { + NextLogIn(it) + val, ok := it.nextIt.Next() if !ok { // Subiterator is empty, get another one - candidate, ok := l.primaryIt.Next() + candidate, ok := it.primaryIt.Next() if !ok { // 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() - l.nextIt = l.ts.GetTripleIterator(l.direction, candidate) + it.nextIt.Close() + it.nextIt = it.ts.GetTripleIterator(it.direction, candidate) // Recurse -- return the first in the next set. - return l.Next() + return it.Next() } - l.Last = val - return NextLogOut(l, val, ok) + it.Last = val + return NextLogOut(it, val, ok) } // Close our subiterators. -func (l *LinksToIterator) Close() { - l.nextIt.Close() - l.primaryIt.Close() +func (it *LinksToIterator) Close() { + it.nextIt.Close() + it.primaryIt.Close() } // We won't ever have a new result, but our subiterators might. -func (l *LinksToIterator) NextResult() bool { - return l.primaryIt.NextResult() +func (it *LinksToIterator) NextResult() bool { + return it.primaryIt.NextResult() } // 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. -func (l *LinksToIterator) GetStats() *IteratorStats { - subitStats := l.primaryIt.GetStats() +func (it *LinksToIterator) GetStats() *IteratorStats { + subitStats := it.primaryIt.GetStats() // TODO(barakmich): These should really come from the triplestore itself fanoutFactor := int64(20) checkConstant := int64(1) diff --git a/graph/memstore/triplestore_iterator_optimize.go b/graph/memstore/triplestore_iterator_optimize.go index b51ef59..165952d 100644 --- a/graph/memstore/triplestore_iterator_optimize.go +++ b/graph/memstore/triplestore_iterator_optimize.go @@ -28,21 +28,21 @@ func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool } func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { - l := it.GetSubIterators() - if l.Len() != 1 { + subs := it.GetSubIterators() + if len(subs) != 1 { return it, false } - primaryIt := l.Front().Value.(graph.Iterator) - if primaryIt.Type() == "fixed" { - size, _ := primaryIt.Size() + primary := subs[0] + if primary.Type() == "fixed" { + size, _ := primary.Size() if size == 1 { - val, ok := primaryIt.Next() + val, ok := primary.Next() if !ok { panic("Sizes lie") } newIt := ts.GetTripleIterator(it.Direction(), val) newIt.CopyTagsFrom(it) - for _, tag := range primaryIt.Tags() { + for _, tag := range primary.Tags() { newIt.AddFixedTag(tag, val) } return newIt, true diff --git a/graph/mongo/triplestore_iterator_optimize.go b/graph/mongo/triplestore_iterator_optimize.go index 2d6ebde..b1e50db 100644 --- a/graph/mongo/triplestore_iterator_optimize.go +++ b/graph/mongo/triplestore_iterator_optimize.go @@ -28,21 +28,21 @@ func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool } func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { - l := it.GetSubIterators() - if l.Len() != 1 { + subs := it.GetSubIterators() + if len(subs) != 1 { return it, false } - primaryIt := l.Front().Value.(graph.Iterator) - if primaryIt.Type() == "fixed" { - size, _ := primaryIt.Size() + primary := subs[0] + if primary.Type() == "fixed" { + size, _ := primary.Size() if size == 1 { - val, ok := primaryIt.Next() + val, ok := primary.Next() if !ok { panic("Sizes lie") } newIt := ts.GetTripleIterator(it.Direction(), val) newIt.CopyTagsFrom(it) - for _, tag := range primaryIt.Tags() { + for _, tag := range primary.Tags() { newIt.AddFixedTag(tag, val) } it.Close() diff --git a/graph/or_iterator.go b/graph/or_iterator.go index 5844898..6c1c8f2 100644 --- a/graph/or_iterator.go +++ b/graph/or_iterator.go @@ -22,7 +22,6 @@ package graph // May return the same value twice -- once for each branch. import ( - "container/list" "fmt" "strings" ) @@ -75,13 +74,9 @@ func (it *OrIterator) Clone() Iterator { return or } -// Returns a list.List of the subiterators, in order. -func (it *OrIterator) GetSubIterators() *list.List { - l := list.New() - for _, sub := range it.internalIterators { - l.PushBack(sub) - } - return l +// Returns a list.List of the subiterators, in order. The returned slice must not be modified. +func (it *OrIterator) GetSubIterators() []Iterator { + return it.internalIterators } // 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) { - oldItList := it.GetSubIterators() - itList := optimizeSubIterators(oldItList) + old := it.GetSubIterators() + optIts := optimizeSubIterators(old) // Close the replaced iterators (they ought to close themselves, but Close() // is idempotent, so this just protects against any machinations). - closeIteratorList(oldItList, nil) + closeIteratorList(old, nil) newOr := NewOrIterator() newOr.isShortCircuiting = it.isShortCircuiting // Add the subiterators in order. - for e := itList.Front(); e != nil; e = e.Next() { - newOr.AddSubIterator(e.Value.(Iterator)) + for _, o := range optIts { + newOr.AddSubIterator(o) } // Move the tags hanging on us (like any good replacement). diff --git a/graph/query_shape.go b/graph/query_shape.go index dece079..1c20ca1 100644 --- a/graph/query_shape.go +++ b/graph/query_shape.go @@ -105,12 +105,7 @@ func (qs *queryShape) StealNode(left *Node, right *Node) { } func (qs *queryShape) MakeNode(it Iterator) *Node { - var n Node - n.IsLinkNode = false - n.IsFixed = false - n.Id = qs.nodeId - n.Tags = make([]string, 0) - n.Values = make([]string, 0) + n := Node{Id: qs.nodeId} for _, tag := range it.Tags() { n.Tags = append(n.Tags, tag) } @@ -120,12 +115,10 @@ func (qs *queryShape) MakeNode(it Iterator) *Node { switch it.Type() { case "and": - list := it.GetSubIterators() - for e := list.Front(); e != nil; e = e.Next() { - subit := e.Value.(Iterator) + for _, sub := range it.GetSubIterators() { qs.nodeId++ - newNode := qs.MakeNode(subit) - if subit.Type() != "or" { + newNode := qs.MakeNode(sub) + if sub.Type() != "or" { qs.StealNode(&n, newNode) } else { qs.AddNode(newNode) @@ -149,12 +142,10 @@ func (qs *queryShape) MakeNode(it Iterator) *Node { qs.AddNode(newNode) qs.RemoveHasa() case "or": - list := it.GetSubIterators() - for e := list.Front(); e != nil; e = e.Next() { - subit := e.Value.(Iterator) + for _, sub := range it.GetSubIterators() { qs.nodeId++ - newNode := qs.MakeNode(subit) - if subit.Type() == "or" { + newNode := qs.MakeNode(sub) + if sub.Type() == "or" { qs.StealNode(&n, newNode) } else { qs.AddNode(newNode)