Canonicalise iterator receiver names
This apparently meaningless churn improves godoc readability.
This commit is contained in:
parent
dc65ebce9e
commit
60d5c60817
10 changed files with 432 additions and 435 deletions
|
|
@ -45,70 +45,69 @@ func NewInt64AllIterator(min, max int64) *Int64AllIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start back at the beginning
|
// Start back at the beginning
|
||||||
func (a *Int64AllIterator) Reset() {
|
func (it *Int64AllIterator) Reset() {
|
||||||
a.at = a.min
|
it.at = it.min
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Int64AllIterator) Close() {
|
func (it *Int64AllIterator) Close() {}
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Int64AllIterator) Clone() Iterator {
|
func (it *Int64AllIterator) Clone() Iterator {
|
||||||
out := NewInt64AllIterator(a.min, a.max)
|
out := NewInt64AllIterator(it.min, it.max)
|
||||||
out.CopyTagsFrom(a)
|
out.CopyTagsFrom(it)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints the All iterator as just an "all".
|
// Prints the All iterator as just an "all".
|
||||||
func (a *Int64AllIterator) DebugString(indent int) string {
|
func (it *Int64AllIterator) DebugString(indent int) string {
|
||||||
return fmt.Sprintf("%s(%s)", strings.Repeat(" ", indent), a.Type())
|
return fmt.Sprintf("%s(%s)", strings.Repeat(" ", indent), it.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next() on an Int64 all iterator is a simple incrementing counter.
|
// Next() on an Int64 all iterator is a simple incrementing counter.
|
||||||
// Return the next integer, and mark it as the result.
|
// Return the next integer, and mark it as the result.
|
||||||
func (a *Int64AllIterator) Next() (TSVal, bool) {
|
func (it *Int64AllIterator) Next() (TSVal, bool) {
|
||||||
NextLogIn(a)
|
NextLogIn(it)
|
||||||
if a.at == -1 {
|
if it.at == -1 {
|
||||||
return NextLogOut(a, nil, false)
|
return NextLogOut(it, nil, false)
|
||||||
}
|
}
|
||||||
val := a.at
|
val := it.at
|
||||||
a.at = a.at + 1
|
it.at = it.at + 1
|
||||||
if a.at > a.max {
|
if it.at > it.max {
|
||||||
a.at = -1
|
it.at = -1
|
||||||
}
|
}
|
||||||
a.Last = val
|
it.Last = val
|
||||||
return NextLogOut(a, val, true)
|
return NextLogOut(it, val, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The number of elements in an Int64AllIterator is the size of the range.
|
// The number of elements in an Int64AllIterator is the size of the range.
|
||||||
// The size is exact.
|
// The size is exact.
|
||||||
func (a *Int64AllIterator) Size() (int64, bool) {
|
func (it *Int64AllIterator) Size() (int64, bool) {
|
||||||
Size := ((a.max - a.min) + 1)
|
Size := ((it.max - it.min) + 1)
|
||||||
return Size, true
|
return Size, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check() for an Int64AllIterator is merely seeing if the passed value is
|
// Check() for an Int64AllIterator is merely seeing if the passed value is
|
||||||
// withing the range, assuming the value is an int64.
|
// withing the range, assuming the value is an int64.
|
||||||
func (a *Int64AllIterator) Check(tsv TSVal) bool {
|
func (it *Int64AllIterator) Check(tsv TSVal) bool {
|
||||||
CheckLogIn(a, tsv)
|
CheckLogIn(it, tsv)
|
||||||
v := tsv.(int64)
|
v := tsv.(int64)
|
||||||
if a.min <= v && v <= a.max {
|
if it.min <= v && v <= it.max {
|
||||||
a.Last = v
|
it.Last = v
|
||||||
return CheckLogOut(a, v, true)
|
return CheckLogOut(it, v, true)
|
||||||
}
|
}
|
||||||
return CheckLogOut(a, v, false)
|
return CheckLogOut(it, v, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The type of this iterator is an "all". This is important, as it puts it in
|
// The type of this iterator is an "all". This is important, as it puts it in
|
||||||
// the class of "all iterators.
|
// the class of "all iterators.
|
||||||
func (a *Int64AllIterator) Type() string { return "all" }
|
func (it *Int64AllIterator) Type() string { return "all" }
|
||||||
|
|
||||||
// There's nothing to optimize about this little iterator.
|
// There's nothing to optimize about this little iterator.
|
||||||
func (a *Int64AllIterator) Optimize() (Iterator, bool) { return a, false }
|
func (it *Int64AllIterator) Optimize() (Iterator, bool) { return it, false }
|
||||||
|
|
||||||
// Stats for an Int64AllIterator are simple. Super cheap to do any operation,
|
// Stats for an Int64AllIterator are simple. Super cheap to do any operation,
|
||||||
// and as big as the range.
|
// and as big as the range.
|
||||||
func (a *Int64AllIterator) GetStats() *IteratorStats {
|
func (it *Int64AllIterator) GetStats() *IteratorStats {
|
||||||
s, _ := a.Size()
|
s, _ := it.Size()
|
||||||
return &IteratorStats{
|
return &IteratorStats{
|
||||||
CheckCost: 1,
|
CheckCost: 1,
|
||||||
NextCost: 1,
|
NextCost: 1,
|
||||||
|
|
|
||||||
|
|
@ -41,80 +41,80 @@ func NewAndIterator() *AndIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all internal iterators
|
// Reset all internal iterators
|
||||||
func (and *AndIterator) Reset() {
|
func (it *AndIterator) Reset() {
|
||||||
and.primaryIt.Reset()
|
it.primaryIt.Reset()
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
it.Reset()
|
sub.Reset()
|
||||||
}
|
}
|
||||||
and.checkList = nil
|
it.checkList = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (and *AndIterator) Clone() Iterator {
|
func (it *AndIterator) Clone() Iterator {
|
||||||
newAnd := NewAndIterator()
|
and := NewAndIterator()
|
||||||
newAnd.AddSubIterator(and.primaryIt.Clone())
|
and.AddSubIterator(it.primaryIt.Clone())
|
||||||
newAnd.CopyTagsFrom(and)
|
and.CopyTagsFrom(it)
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
newAnd.AddSubIterator(it.Clone())
|
and.AddSubIterator(sub.Clone())
|
||||||
}
|
}
|
||||||
if and.checkList != nil {
|
if it.checkList != nil {
|
||||||
newAnd.optimizeCheck()
|
and.optimizeCheck()
|
||||||
}
|
}
|
||||||
return newAnd
|
return and
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a list.List of the subiterators, in order (primary iterator first).
|
// Returns a list.List of the subiterators, in order (primary iterator first).
|
||||||
func (and *AndIterator) GetSubIterators() *list.List {
|
func (it *AndIterator) GetSubIterators() *list.List {
|
||||||
l := list.New()
|
l := list.New()
|
||||||
l.PushBack(and.primaryIt)
|
l.PushBack(it.primaryIt)
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
l.PushBack(it)
|
l.PushBack(sub)
|
||||||
}
|
}
|
||||||
return l
|
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
|
||||||
// recurse down it's subiterators.
|
// recurse down it's subiterators.
|
||||||
func (and *AndIterator) TagResults(out *map[string]TSVal) {
|
func (it *AndIterator) TagResults(out *map[string]TSVal) {
|
||||||
and.BaseIterator.TagResults(out)
|
it.BaseIterator.TagResults(out)
|
||||||
if and.primaryIt != nil {
|
if it.primaryIt != nil {
|
||||||
and.primaryIt.TagResults(out)
|
it.primaryIt.TagResults(out)
|
||||||
}
|
}
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
it.TagResults(out)
|
sub.TagResults(out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (and *AndIterator) GetResultTree() *ResultTree {
|
func (it *AndIterator) GetResultTree() *ResultTree {
|
||||||
tree := NewResultTree(and.LastResult())
|
tree := NewResultTree(it.LastResult())
|
||||||
tree.AddSubtree(and.primaryIt.GetResultTree())
|
tree.AddSubtree(it.primaryIt.GetResultTree())
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
tree.AddSubtree(it.GetResultTree())
|
tree.AddSubtree(sub.GetResultTree())
|
||||||
}
|
}
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints information about this iterator.
|
// Prints information about this iterator.
|
||||||
func (and *AndIterator) DebugString(indent int) string {
|
func (it *AndIterator) DebugString(indent int) string {
|
||||||
var total string
|
var total string
|
||||||
for i, it := range and.internalIterators {
|
for i, sub := range it.internalIterators {
|
||||||
total += strings.Repeat(" ", indent+2)
|
total += strings.Repeat(" ", indent+2)
|
||||||
total += fmt.Sprintf("%d:\n%s\n", i, it.DebugString(indent+4))
|
total += fmt.Sprintf("%d:\n%s\n", i, sub.DebugString(indent+4))
|
||||||
}
|
}
|
||||||
var tags string
|
var tags string
|
||||||
for _, k := range and.Tags() {
|
for _, k := range it.Tags() {
|
||||||
tags += fmt.Sprintf("%s;", k)
|
tags += fmt.Sprintf("%s;", k)
|
||||||
}
|
}
|
||||||
spaces := strings.Repeat(" ", indent+2)
|
spaces := strings.Repeat(" ", indent+2)
|
||||||
|
|
||||||
return fmt.Sprintf("%s(%s %d\n%stags:%s\n%sprimary_it:\n%s\n%sother_its:\n%s)",
|
return fmt.Sprintf("%s(%s %d\n%stags:%s\n%sprimary_it:\n%s\n%sother_its:\n%s)",
|
||||||
strings.Repeat(" ", indent),
|
strings.Repeat(" ", indent),
|
||||||
and.Type(),
|
it.Type(),
|
||||||
and.GetUid(),
|
it.GetUid(),
|
||||||
spaces,
|
spaces,
|
||||||
tags,
|
tags,
|
||||||
spaces,
|
spaces,
|
||||||
and.primaryIt.DebugString(indent+4),
|
it.primaryIt.DebugString(indent+4),
|
||||||
spaces,
|
spaces,
|
||||||
total)
|
total)
|
||||||
}
|
}
|
||||||
|
|
@ -125,43 +125,42 @@ func (and *AndIterator) DebugString(indent int) string {
|
||||||
// important. Calling Optimize() is the way to change the order based on
|
// important. Calling Optimize() is the way to change the order based on
|
||||||
// subiterator statistics. Without Optimize(), the order added is the order
|
// subiterator statistics. Without Optimize(), the order added is the order
|
||||||
// used.
|
// used.
|
||||||
func (and *AndIterator) AddSubIterator(sub Iterator) {
|
func (it *AndIterator) AddSubIterator(sub Iterator) {
|
||||||
if and.itCount > 0 {
|
if it.itCount > 0 {
|
||||||
and.internalIterators = append(and.internalIterators, sub)
|
it.internalIterators = append(it.internalIterators, sub)
|
||||||
and.itCount++
|
it.itCount++
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
and.primaryIt = sub
|
it.primaryIt = sub
|
||||||
and.itCount++
|
it.itCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the Next value from the And iterator. Because the And is the
|
// Returns the Next value from the And iterator. Because the And is the
|
||||||
// intersection of its subiterators, it must choose one subiterator to produce a
|
// intersection of its subiterators, it must choose one subiterator to produce a
|
||||||
// candidate, and check this value against the subiterators. A productive choice
|
// candidate, and check this value against the subiterators. A productive choice
|
||||||
// of primary iterator is therefore very important.
|
// of primary iterator is therefore very important.
|
||||||
func (and *AndIterator) Next() (TSVal, bool) {
|
func (it *AndIterator) Next() (TSVal, bool) {
|
||||||
NextLogIn(and)
|
NextLogIn(it)
|
||||||
var curr TSVal
|
var curr TSVal
|
||||||
var exists bool
|
var exists bool
|
||||||
for {
|
for {
|
||||||
|
curr, exists = it.primaryIt.Next()
|
||||||
curr, exists = and.primaryIt.Next()
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return NextLogOut(and, nil, false)
|
return NextLogOut(it, nil, false)
|
||||||
}
|
}
|
||||||
if and.checkSubIts(curr) {
|
if it.checkSubIts(curr) {
|
||||||
and.Last = curr
|
it.Last = curr
|
||||||
return NextLogOut(and, curr, true)
|
return NextLogOut(it, curr, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic("Somehow broke out of Next() loop in AndIterator")
|
panic("Somehow broke out of Next() loop in AndIterator")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks a value against the non-primary iterators, in order.
|
// Checks a value against the non-primary iterators, in order.
|
||||||
func (and *AndIterator) checkSubIts(val TSVal) bool {
|
func (it *AndIterator) checkSubIts(val TSVal) bool {
|
||||||
var subIsGood = true
|
var subIsGood = true
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
subIsGood = it.Check(val)
|
subIsGood = sub.Check(val)
|
||||||
if !subIsGood {
|
if !subIsGood {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -169,43 +168,43 @@ func (and *AndIterator) checkSubIts(val TSVal) bool {
|
||||||
return subIsGood
|
return subIsGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func (and *AndIterator) checkCheckList(val TSVal) bool {
|
func (it *AndIterator) checkCheckList(val TSVal) bool {
|
||||||
var isGood = true
|
var isGood = true
|
||||||
for e := and.checkList.Front(); e != nil; e = e.Next() {
|
for e := it.checkList.Front(); e != nil; e = e.Next() {
|
||||||
isGood = e.Value.(Iterator).Check(val)
|
isGood = e.Value.(Iterator).Check(val)
|
||||||
if !isGood {
|
if !isGood {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CheckLogOut(and, val, isGood)
|
return CheckLogOut(it, val, isGood)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check a value against the entire iterator, in order.
|
// Check a value against the entire iterator, in order.
|
||||||
func (and *AndIterator) Check(val TSVal) bool {
|
func (it *AndIterator) Check(val TSVal) bool {
|
||||||
CheckLogIn(and, val)
|
CheckLogIn(it, val)
|
||||||
if and.checkList != nil {
|
if it.checkList != nil {
|
||||||
return and.checkCheckList(val)
|
return it.checkCheckList(val)
|
||||||
}
|
}
|
||||||
mainGood := and.primaryIt.Check(val)
|
mainGood := it.primaryIt.Check(val)
|
||||||
if !mainGood {
|
if !mainGood {
|
||||||
return CheckLogOut(and, val, false)
|
return CheckLogOut(it, val, false)
|
||||||
}
|
}
|
||||||
othersGood := and.checkSubIts(val)
|
othersGood := it.checkSubIts(val)
|
||||||
if !othersGood {
|
if !othersGood {
|
||||||
return CheckLogOut(and, val, false)
|
return CheckLogOut(it, val, false)
|
||||||
}
|
}
|
||||||
and.Last = val
|
it.Last = val
|
||||||
return CheckLogOut(and, val, true)
|
return CheckLogOut(it, val, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the approximate size of the And iterator. Because we're dealing
|
// Returns the approximate size of the And iterator. Because we're dealing
|
||||||
// with an intersection, we know that the largest we can be is the size of the
|
// with an intersection, we know that the largest we can be is the size of the
|
||||||
// smallest iterator. This is the heuristic we shall follow. Better heuristics
|
// smallest iterator. This is the heuristic we shall follow. Better heuristics
|
||||||
// welcome.
|
// welcome.
|
||||||
func (and *AndIterator) Size() (int64, bool) {
|
func (it *AndIterator) Size() (int64, bool) {
|
||||||
val, b := and.primaryIt.Size()
|
val, b := it.primaryIt.Size()
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
newval, newb := it.Size()
|
newval, newb := sub.Size()
|
||||||
if val > newval {
|
if val > newval {
|
||||||
val = newval
|
val = newval
|
||||||
}
|
}
|
||||||
|
|
@ -217,12 +216,12 @@ func (and *AndIterator) Size() (int64, bool) {
|
||||||
// An And has no NextResult of its own -- that is, there are no other values
|
// An And has no NextResult of its own -- that is, there are no other values
|
||||||
// which satisfy our previous result that are not the result itself. Our
|
// which satisfy our previous result that are not the result itself. Our
|
||||||
// subiterators might, however, so just pass the call recursively.
|
// subiterators might, however, so just pass the call recursively.
|
||||||
func (and *AndIterator) NextResult() bool {
|
func (it *AndIterator) NextResult() bool {
|
||||||
if and.primaryIt.NextResult() {
|
if it.primaryIt.NextResult() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
if it.NextResult() {
|
if sub.NextResult() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,19 +229,18 @@ func (and *AndIterator) NextResult() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform and-specific cleanup, of which there currently is none.
|
// Perform and-specific cleanup, of which there currently is none.
|
||||||
func (and *AndIterator) cleanUp() {
|
func (it *AndIterator) cleanUp() {}
|
||||||
}
|
|
||||||
|
|
||||||
// Close this iterator, and, by extension, close the subiterators.
|
// Close this iterator, and, by extension, close the subiterators.
|
||||||
// Close should be idempotent, and it follows that if it's subiterators
|
// Close should be idempotent, and it follows that if it's subiterators
|
||||||
// follow this contract, the And follows the contract.
|
// follow this contract, the And follows the contract.
|
||||||
func (and *AndIterator) Close() {
|
func (it *AndIterator) Close() {
|
||||||
and.cleanUp()
|
it.cleanUp()
|
||||||
and.primaryIt.Close()
|
it.primaryIt.Close()
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
it.Close()
|
sub.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register this as an "and" iterator.
|
// Register this as an "and" iterator.
|
||||||
func (and *AndIterator) Type() string { return "and" }
|
func (it *AndIterator) Type() string { return "and" }
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,10 @@ import (
|
||||||
|
|
||||||
// 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 (and *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 list of iterators, in order (first one is Next()ed,
|
||||||
// the rest are Check()ed)
|
// the rest are Check()ed)
|
||||||
oldItList := and.GetSubIterators()
|
oldItList := 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
|
||||||
|
|
@ -54,10 +54,10 @@ func (and *AndIterator) Optimize() (Iterator, bool) {
|
||||||
|
|
||||||
// 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 := and.optimizeReplacement(itList)
|
out := it.optimizeReplacement(itList)
|
||||||
if out != nil {
|
if out != nil {
|
||||||
// ...Move the tags to the replacement...
|
// ...Move the tags to the replacement...
|
||||||
moveTagsTo(out, and)
|
moveTagsTo(out, it)
|
||||||
// ...Close everyone except `out`, our replacement...
|
// ...Close everyone except `out`, our replacement...
|
||||||
closeIteratorList(itList, out)
|
closeIteratorList(itList, out)
|
||||||
// ...And return it.
|
// ...And return it.
|
||||||
|
|
@ -80,14 +80,14 @@ func (and *AndIterator) Optimize() (Iterator, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the tags hanging on us (like any good replacement).
|
// Move the tags hanging on us (like any good replacement).
|
||||||
newAnd.CopyTagsFrom(and)
|
newAnd.CopyTagsFrom(it)
|
||||||
|
|
||||||
newAnd.optimizeCheck()
|
newAnd.optimizeCheck()
|
||||||
|
|
||||||
// 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
|
||||||
// start).
|
// start).
|
||||||
and.cleanUp()
|
it.cleanUp()
|
||||||
return newAnd, true
|
return newAnd, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ func closeIteratorList(l *list.List, except Iterator) {
|
||||||
|
|
||||||
// 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 (and *AndIterator) optimizeReplacement(itList *list.List) Iterator {
|
func (_ *AndIterator) optimizeReplacement(itList *list.List) 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 itList.Len() == 0 {
|
||||||
return &NullIterator{}
|
return &NullIterator{}
|
||||||
|
|
@ -190,8 +190,8 @@ func optimizeOrder(l *list.List) *list.List {
|
||||||
|
|
||||||
// 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 (and *AndIterator) optimizeCheck() {
|
func (it *AndIterator) optimizeCheck() {
|
||||||
subIts := and.GetSubIterators()
|
subIts := it.GetSubIterators()
|
||||||
out := list.New()
|
out := list.New()
|
||||||
|
|
||||||
// Find the iterator with the lowest Check() cost, push it to the front, repeat.
|
// Find the iterator with the lowest Check() cost, push it to the front, repeat.
|
||||||
|
|
@ -211,15 +211,15 @@ func (and *AndIterator) optimizeCheck() {
|
||||||
subIts.Remove(best)
|
subIts.Remove(best)
|
||||||
}
|
}
|
||||||
|
|
||||||
and.checkList = out
|
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 (and *AndIterator) getSubTags() map[string]bool {
|
func (it *AndIterator) getSubTags() map[string]bool {
|
||||||
subs := and.GetSubIterators()
|
subs := it.GetSubIterators()
|
||||||
tags := make(map[string]bool)
|
tags := make(map[string]bool)
|
||||||
for e := subs.Front(); e != nil; e = e.Next() {
|
for e := subs.Front(); e != nil; e = e.Next() {
|
||||||
it := e.Value.(Iterator)
|
it := e.Value.(Iterator)
|
||||||
|
|
@ -227,23 +227,23 @@ func (and *AndIterator) getSubTags() map[string]bool {
|
||||||
tags[tag] = true
|
tags[tag] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, tag := range and.Tags() {
|
for _, tag := range it.Tags() {
|
||||||
tags[tag] = true
|
tags[tag] = true
|
||||||
}
|
}
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
// moveTagsTo() gets the tags for all of the And's subiterators and the
|
// moveTagsTo() gets the tags for all of the src's subiterators and the
|
||||||
// And itself, and moves them to `out`.
|
// src itself, and moves them to dst.
|
||||||
func moveTagsTo(out Iterator, and *AndIterator) {
|
func moveTagsTo(dst Iterator, src *AndIterator) {
|
||||||
tagmap := and.getSubTags()
|
tagmap := src.getSubTags()
|
||||||
for _, tag := range out.Tags() {
|
for _, tag := range dst.Tags() {
|
||||||
if tagmap[tag] {
|
if tagmap[tag] {
|
||||||
delete(tagmap, tag)
|
delete(tagmap, tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for k, _ := range tagmap {
|
for k, _ := range tagmap {
|
||||||
out.AddTag(k)
|
dst.AddTag(k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,13 +308,13 @@ func hasOneUsefulIterator(l *list.List) Iterator {
|
||||||
// and.GetStats() lives here in and-iterator-optimize.go because it may
|
// and.GetStats() lives here in and-iterator-optimize.go because it may
|
||||||
// in the future return different statistics based on how it is optimized.
|
// in the future return different statistics based on how it is optimized.
|
||||||
// For now, however, it's pretty static.
|
// For now, however, it's pretty static.
|
||||||
func (and *AndIterator) GetStats() *IteratorStats {
|
func (it *AndIterator) GetStats() *IteratorStats {
|
||||||
primaryStats := and.primaryIt.GetStats()
|
primaryStats := it.primaryIt.GetStats()
|
||||||
CheckCost := primaryStats.CheckCost
|
CheckCost := primaryStats.CheckCost
|
||||||
NextCost := primaryStats.NextCost
|
NextCost := primaryStats.NextCost
|
||||||
Size := primaryStats.Size
|
Size := primaryStats.Size
|
||||||
for _, it := range and.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
stats := it.GetStats()
|
stats := sub.GetStats()
|
||||||
NextCost += stats.CheckCost
|
NextCost += stats.CheckCost
|
||||||
CheckCost += stats.CheckCost
|
CheckCost += stats.CheckCost
|
||||||
if Size > stats.Size {
|
if Size > stats.Size {
|
||||||
|
|
|
||||||
|
|
@ -60,98 +60,97 @@ func NewFixedIteratorWithCompare(compareFn Equality) *FixedIterator {
|
||||||
return &it
|
return &it
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FixedIterator) Reset() {
|
func (it *FixedIterator) Reset() {
|
||||||
f.lastIndex = 0
|
it.lastIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FixedIterator) Close() {
|
func (it *FixedIterator) Close() {}
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FixedIterator) Clone() Iterator {
|
func (it *FixedIterator) Clone() Iterator {
|
||||||
out := NewFixedIteratorWithCompare(f.cmp)
|
out := NewFixedIteratorWithCompare(it.cmp)
|
||||||
for _, val := range f.values {
|
for _, val := range it.values {
|
||||||
out.AddValue(val)
|
out.AddValue(val)
|
||||||
}
|
}
|
||||||
out.CopyTagsFrom(f)
|
out.CopyTagsFrom(it)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a value to the iterator. The array now contains this value.
|
// Add a value to the iterator. The array now contains this value.
|
||||||
// TODO(barakmich): This ought to be a set someday, disallowing repeated values.
|
// TODO(barakmich): This ought to be a set someday, disallowing repeated values.
|
||||||
func (f *FixedIterator) AddValue(v TSVal) {
|
func (it *FixedIterator) AddValue(v TSVal) {
|
||||||
f.values = append(f.values, v)
|
it.values = append(it.values, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print some information about the iterator.
|
// Print some information about the iterator.
|
||||||
func (f *FixedIterator) DebugString(indent int) string {
|
func (it *FixedIterator) DebugString(indent int) string {
|
||||||
value := ""
|
value := ""
|
||||||
if len(f.values) > 0 {
|
if len(it.values) > 0 {
|
||||||
value = fmt.Sprint(f.values[0])
|
value = fmt.Sprint(it.values[0])
|
||||||
}
|
}
|
||||||
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),
|
||||||
f.Type(),
|
it.Type(),
|
||||||
f.FixedTags(),
|
it.FixedTags(),
|
||||||
len(f.values),
|
len(it.values),
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register this iterator as a Fixed iterator.
|
// Register this iterator as a Fixed iterator.
|
||||||
func (f *FixedIterator) Type() string {
|
func (it *FixedIterator) Type() string {
|
||||||
return "fixed"
|
return "fixed"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the passed value is equal to one of the values stored in the iterator.
|
// Check if the passed value is equal to one of the values stored in the iterator.
|
||||||
func (f *FixedIterator) Check(v TSVal) bool {
|
func (it *FixedIterator) Check(v TSVal) bool {
|
||||||
// Could be optimized by keeping it sorted or using a better datastructure.
|
// Could be optimized by keeping it sorted or using a better datastructure.
|
||||||
// However, for fixed iterators, which are by definition kind of tiny, this
|
// However, for fixed iterators, which are by definition kind of tiny, this
|
||||||
// isn't a big issue.
|
// isn't a big issue.
|
||||||
CheckLogIn(f, v)
|
CheckLogIn(it, v)
|
||||||
for _, x := range f.values {
|
for _, x := range it.values {
|
||||||
if f.cmp(x, v) {
|
if it.cmp(x, v) {
|
||||||
f.Last = x
|
it.Last = x
|
||||||
return CheckLogOut(f, v, true)
|
return CheckLogOut(it, v, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CheckLogOut(f, v, false)
|
return CheckLogOut(it, v, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the next stored value from the iterator.
|
// Return the next stored value from the iterator.
|
||||||
func (f *FixedIterator) Next() (TSVal, bool) {
|
func (it *FixedIterator) Next() (TSVal, bool) {
|
||||||
NextLogIn(f)
|
NextLogIn(it)
|
||||||
if f.lastIndex == len(f.values) {
|
if it.lastIndex == len(it.values) {
|
||||||
return NextLogOut(f, nil, false)
|
return NextLogOut(it, nil, false)
|
||||||
}
|
}
|
||||||
out := f.values[f.lastIndex]
|
out := it.values[it.lastIndex]
|
||||||
f.Last = out
|
it.Last = out
|
||||||
f.lastIndex++
|
it.lastIndex++
|
||||||
return NextLogOut(f, out, true)
|
return NextLogOut(it, out, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
func (f *FixedIterator) Optimize() (Iterator, bool) {
|
func (it *FixedIterator) Optimize() (Iterator, bool) {
|
||||||
|
|
||||||
if len(f.values) == 1 && f.values[0] == nil {
|
if len(it.values) == 1 && it.values[0] == nil {
|
||||||
return &NullIterator{}, true
|
return &NullIterator{}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
return f, false
|
return it, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size is the number of values stored.
|
// Size is the number of values stored.
|
||||||
func (f *FixedIterator) Size() (int64, bool) {
|
func (it *FixedIterator) Size() (int64, bool) {
|
||||||
return int64(len(f.values)), true
|
return int64(len(it.values)), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// As we right now have to scan the entire list, Next and Check are linear with the
|
// As we right now have to scan the entire list, Next and Check are linear with the
|
||||||
// size. However, a better data structure could remove these limits.
|
// size. However, a better data structure could remove these limits.
|
||||||
func (a *FixedIterator) GetStats() *IteratorStats {
|
func (it *FixedIterator) GetStats() *IteratorStats {
|
||||||
return &IteratorStats{
|
return &IteratorStats{
|
||||||
CheckCost: int64(len(a.values)),
|
CheckCost: int64(len(it.values)),
|
||||||
NextCost: int64(len(a.values)),
|
NextCost: int64(len(it.values)),
|
||||||
Size: int64(len(a.values)),
|
Size: int64(len(it.values)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,9 @@ package graph
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/barakmich/glog"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/barakmich/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A HasaIterator consists of a reference back to the TripleStore that it references,
|
// A HasaIterator consists of a reference back to the TripleStore that it references,
|
||||||
|
|
@ -63,94 +64,93 @@ func NewHasaIterator(ts TripleStore, subIt Iterator, dir string) *HasaIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return our sole subiterator, in a list.List.
|
// Return our sole subiterator, in a list.List.
|
||||||
func (h *HasaIterator) GetSubIterators() *list.List {
|
func (it *HasaIterator) GetSubIterators() *list.List {
|
||||||
l := list.New()
|
l := list.New()
|
||||||
l.PushBack(h.primaryIt)
|
l.PushBack(it.primaryIt)
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HasaIterator) Reset() {
|
func (it *HasaIterator) Reset() {
|
||||||
h.primaryIt.Reset()
|
it.primaryIt.Reset()
|
||||||
if h.resultIt != nil {
|
if it.resultIt != nil {
|
||||||
h.resultIt.Close()
|
it.resultIt.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HasaIterator) Clone() Iterator {
|
func (it *HasaIterator) Clone() Iterator {
|
||||||
out := NewHasaIterator(h.ts, h.primaryIt.Clone(), h.direction)
|
out := NewHasaIterator(it.ts, it.primaryIt.Clone(), it.direction)
|
||||||
out.CopyTagsFrom(h)
|
out.CopyTagsFrom(it)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direction accessor.
|
// Direction accessor.
|
||||||
func (h *HasaIterator) Direction() string { return h.direction }
|
func (it *HasaIterator) Direction() string { return it.direction }
|
||||||
|
|
||||||
// Pass the Optimize() call along to the subiterator. If it becomes Null,
|
// Pass the Optimize() call along to the subiterator. If it becomes Null,
|
||||||
// then the HasA becomes Null (there are no triples that have any directions).
|
// then the HasA becomes Null (there are no triples that have any directions).
|
||||||
func (h *HasaIterator) Optimize() (Iterator, bool) {
|
func (it *HasaIterator) Optimize() (Iterator, bool) {
|
||||||
|
newPrimary, changed := it.primaryIt.Optimize()
|
||||||
newPrimary, changed := h.primaryIt.Optimize()
|
|
||||||
if changed {
|
if changed {
|
||||||
h.primaryIt = newPrimary
|
it.primaryIt = newPrimary
|
||||||
if h.primaryIt.Type() == "null" {
|
if it.primaryIt.Type() == "null" {
|
||||||
return h.primaryIt, true
|
return it.primaryIt, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return h, false
|
return it, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass the TagResults down the chain.
|
// Pass the TagResults down the chain.
|
||||||
func (h *HasaIterator) TagResults(out *map[string]TSVal) {
|
func (it *HasaIterator) TagResults(out *map[string]TSVal) {
|
||||||
h.BaseIterator.TagResults(out)
|
it.BaseIterator.TagResults(out)
|
||||||
h.primaryIt.TagResults(out)
|
it.primaryIt.TagResults(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED Return results in a ResultTree.
|
// DEPRECATED Return results in a ResultTree.
|
||||||
func (h *HasaIterator) GetResultTree() *ResultTree {
|
func (it *HasaIterator) GetResultTree() *ResultTree {
|
||||||
tree := NewResultTree(h.LastResult())
|
tree := NewResultTree(it.LastResult())
|
||||||
tree.AddSubtree(h.primaryIt.GetResultTree())
|
tree.AddSubtree(it.primaryIt.GetResultTree())
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print some information about this iterator.
|
// Print some information about this iterator.
|
||||||
func (h *HasaIterator) DebugString(indent int) string {
|
func (it *HasaIterator) DebugString(indent int) string {
|
||||||
var tags string
|
var tags string
|
||||||
for _, k := range h.Tags() {
|
for _, k := range it.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), h.Type(), h.GetUid(), tags, h.direction, h.primaryIt.DebugString(indent+4))
|
return fmt.Sprintf("%s(%s %d tags:%s direction:%s\n%s)", strings.Repeat(" ", indent), it.Type(), it.GetUid(), tags, it.direction, it.primaryIt.DebugString(indent+4))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check a value against our internal iterator. In order to do this, we must first open a new
|
// Check a value against our internal iterator. In order to do this, we must first open a new
|
||||||
// iterator of "triples that have `val` in our direction", given to us by the triple store,
|
// iterator of "triples that have `val` in our direction", given to us by the triple store,
|
||||||
// and then Next() values out of that iterator and Check() them against our subiterator.
|
// and then Next() values out of that iterator and Check() them against our subiterator.
|
||||||
func (h *HasaIterator) Check(val TSVal) bool {
|
func (it *HasaIterator) Check(val TSVal) bool {
|
||||||
CheckLogIn(h, val)
|
CheckLogIn(it, val)
|
||||||
if glog.V(4) {
|
if glog.V(4) {
|
||||||
glog.V(4).Infoln("Id is", h.ts.GetNameFor(val))
|
glog.V(4).Infoln("Id is", it.ts.GetNameFor(val))
|
||||||
}
|
}
|
||||||
// TODO(barakmich): Optimize this
|
// TODO(barakmich): Optimize this
|
||||||
if h.resultIt != nil {
|
if it.resultIt != nil {
|
||||||
h.resultIt.Close()
|
it.resultIt.Close()
|
||||||
}
|
}
|
||||||
h.resultIt = h.ts.GetTripleIterator(h.direction, val)
|
it.resultIt = it.ts.GetTripleIterator(it.direction, val)
|
||||||
return CheckLogOut(h, val, h.GetCheckResult())
|
return CheckLogOut(it, val, it.GetCheckResult())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCheckResult() is shared code between Check() and GetNextResult() -- calls next on the
|
// GetCheckResult() is shared code between Check() and GetNextResult() -- calls next on the
|
||||||
// result iterator (a triple iterator based on the last checked value) and returns true if
|
// result iterator (a triple iterator based on the last checked value) and returns true if
|
||||||
// another match is made.
|
// another match is made.
|
||||||
func (h *HasaIterator) GetCheckResult() bool {
|
func (it *HasaIterator) GetCheckResult() bool {
|
||||||
for {
|
for {
|
||||||
linkVal, ok := h.resultIt.Next()
|
linkVal, ok := it.resultIt.Next()
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if glog.V(4) {
|
if glog.V(4) {
|
||||||
glog.V(4).Infoln("Triple is", h.ts.GetTriple(linkVal).ToString())
|
glog.V(4).Infoln("Triple is", it.ts.GetTriple(linkVal).ToString())
|
||||||
}
|
}
|
||||||
if h.primaryIt.Check(linkVal) {
|
if it.primaryIt.Check(linkVal) {
|
||||||
h.Last = h.ts.GetTripleDirection(linkVal, h.direction)
|
it.Last = it.ts.GetTripleDirection(linkVal, it.direction)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -158,37 +158,37 @@ func (h *HasaIterator) GetCheckResult() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the next result that matches this branch.
|
// Get the next result that matches this branch.
|
||||||
func (h *HasaIterator) NextResult() bool {
|
func (it *HasaIterator) NextResult() bool {
|
||||||
// Order here is important. If the subiterator has a NextResult, then we
|
// Order here is important. If the subiterator has a NextResult, then we
|
||||||
// need do nothing -- there is a next result, and we shouldn't move forward.
|
// need do nothing -- there is a next result, and we shouldn't move forward.
|
||||||
// However, we then need to get the next result from our last Check().
|
// However, we then need to get the next result from our last Check().
|
||||||
//
|
//
|
||||||
// The upshot is, the end of NextResult() bubbles up from the bottom of the
|
// The upshot is, the end of NextResult() bubbles up from the bottom of the
|
||||||
// iterator tree up, and we need to respect that.
|
// iterator tree up, and we need to respect that.
|
||||||
if h.primaryIt.NextResult() {
|
if it.primaryIt.NextResult() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return h.GetCheckResult()
|
return it.GetCheckResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the next result from this iterator. This is simpler than Check. We have a
|
// Get the next result from this iterator. This is simpler than Check. We have a
|
||||||
// subiterator we can get a value from, and we can take that resultant triple,
|
// subiterator we can get a value from, and we can take that resultant triple,
|
||||||
// pull our direction out of it, and return that.
|
// pull our direction out of it, and return that.
|
||||||
func (h *HasaIterator) Next() (TSVal, bool) {
|
func (it *HasaIterator) Next() (TSVal, bool) {
|
||||||
NextLogIn(h)
|
NextLogIn(it)
|
||||||
if h.resultIt != nil {
|
if it.resultIt != nil {
|
||||||
h.resultIt.Close()
|
it.resultIt.Close()
|
||||||
}
|
}
|
||||||
h.resultIt = &NullIterator{}
|
it.resultIt = &NullIterator{}
|
||||||
|
|
||||||
tID, ok := h.primaryIt.Next()
|
tID, ok := it.primaryIt.Next()
|
||||||
if !ok {
|
if !ok {
|
||||||
return NextLogOut(h, 0, false)
|
return NextLogOut(it, 0, false)
|
||||||
}
|
}
|
||||||
name := h.ts.GetTriple(tID).Get(h.direction)
|
name := it.ts.GetTriple(tID).Get(it.direction)
|
||||||
val := h.ts.GetIdFor(name)
|
val := it.ts.GetIdFor(name)
|
||||||
h.Last = val
|
it.Last = val
|
||||||
return NextLogOut(h, val, true)
|
return NextLogOut(it, val, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStats() returns the statistics on the HasA iterator. This is curious. Next
|
// GetStats() returns the statistics on the HasA iterator. This is curious. Next
|
||||||
|
|
@ -197,8 +197,8 @@ func (h *HasaIterator) Next() (TSVal, bool) {
|
||||||
// one sticks -- potentially expensive, depending on fanout. Size, however, is
|
// one sticks -- potentially expensive, depending on fanout. Size, however, is
|
||||||
// potentially smaller. we know at worst it's the size of the subiterator, but
|
// potentially smaller. we know at worst it's the size of the subiterator, but
|
||||||
// if there are many repeated values, it could be much smaller in totality.
|
// if there are many repeated values, it could be much smaller in totality.
|
||||||
func (h *HasaIterator) GetStats() *IteratorStats {
|
func (it *HasaIterator) GetStats() *IteratorStats {
|
||||||
subitStats := h.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
|
||||||
// and be optimized.
|
// and be optimized.
|
||||||
faninFactor := int64(1)
|
faninFactor := int64(1)
|
||||||
|
|
@ -213,12 +213,12 @@ func (h *HasaIterator) GetStats() *IteratorStats {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the subiterator, the result iterator (if any) and the HasA.
|
// Close the subiterator, the result iterator (if any) and the HasA.
|
||||||
func (h *HasaIterator) Close() {
|
func (it *HasaIterator) Close() {
|
||||||
if h.resultIt != nil {
|
if it.resultIt != nil {
|
||||||
h.resultIt.Close()
|
it.resultIt.Close()
|
||||||
}
|
}
|
||||||
h.primaryIt.Close()
|
it.primaryIt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register this iterator as a HasA.
|
// Register this iterator as a HasA.
|
||||||
func (h *HasaIterator) Type() string { return "hasa" }
|
func (it *HasaIterator) Type() string { return "hasa" }
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,9 @@ package graph
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/barakmich/glog"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/barakmich/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var iterator_n int = 0
|
var iterator_n int = 0
|
||||||
|
|
@ -120,50 +121,50 @@ type BaseIterator struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by subclases.
|
// Called by subclases.
|
||||||
func BaseIteratorInit(b *BaseIterator) {
|
func BaseIteratorInit(it *BaseIterator) {
|
||||||
// Your basic iterator is nextable
|
// Your basic iterator is nextable
|
||||||
b.nextable = true
|
it.nextable = true
|
||||||
b.uid = iterator_n
|
it.uid = iterator_n
|
||||||
if glog.V(2) {
|
if glog.V(2) {
|
||||||
iterator_n++
|
iterator_n++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseIterator) GetUid() int {
|
func (it *BaseIterator) GetUid() int {
|
||||||
return b.uid
|
return it.uid
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a tag to the iterator. Most iterators don't need to override.
|
// Adds a tag to the iterator. Most iterators don't need to override.
|
||||||
func (b *BaseIterator) AddTag(tag string) {
|
func (it *BaseIterator) AddTag(tag string) {
|
||||||
if b.tags == nil {
|
if it.tags == nil {
|
||||||
b.tags = make([]string, 0)
|
it.tags = make([]string, 0)
|
||||||
}
|
}
|
||||||
b.tags = append(b.tags, tag)
|
it.tags = append(it.tags, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseIterator) AddFixedTag(tag string, value TSVal) {
|
func (it *BaseIterator) AddFixedTag(tag string, value TSVal) {
|
||||||
if b.fixedTags == nil {
|
if it.fixedTags == nil {
|
||||||
b.fixedTags = make(map[string]TSVal)
|
it.fixedTags = make(map[string]TSVal)
|
||||||
}
|
}
|
||||||
b.fixedTags[tag] = value
|
it.fixedTags[tag] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the tags.
|
// Returns the tags.
|
||||||
func (b *BaseIterator) Tags() []string {
|
func (it *BaseIterator) Tags() []string {
|
||||||
return b.tags
|
return it.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseIterator) FixedTags() map[string]TSVal {
|
func (it *BaseIterator) FixedTags() map[string]TSVal {
|
||||||
return b.fixedTags
|
return it.fixedTags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseIterator) CopyTagsFrom(other_it Iterator) {
|
func (it *BaseIterator) CopyTagsFrom(other_it Iterator) {
|
||||||
for _, tag := range other_it.Tags() {
|
for _, tag := range other_it.Tags() {
|
||||||
b.AddTag(tag)
|
it.AddTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range other_it.FixedTags() {
|
for k, v := range other_it.FixedTags() {
|
||||||
b.AddFixedTag(k, v)
|
it.AddFixedTag(k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -185,55 +186,55 @@ func (n *BaseIterator) GetStats() *IteratorStats {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED
|
// DEPRECATED
|
||||||
func (b *BaseIterator) GetResultTree() *ResultTree {
|
func (it *BaseIterator) GetResultTree() *ResultTree {
|
||||||
tree := NewResultTree(b.LastResult())
|
tree := NewResultTree(it.LastResult())
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing in a base iterator.
|
// Nothing in a base iterator.
|
||||||
func (n *BaseIterator) Next() (TSVal, bool) {
|
func (it *BaseIterator) Next() (TSVal, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *BaseIterator) NextResult() bool {
|
func (it *BaseIterator) NextResult() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the last result of an iterator.
|
// Returns the last result of an iterator.
|
||||||
func (n *BaseIterator) LastResult() TSVal {
|
func (it *BaseIterator) LastResult() TSVal {
|
||||||
return n.Last
|
return it.Last
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you're empty and you know it, clap your hands.
|
// If you're empty and you know it, clap your hands.
|
||||||
func (n *BaseIterator) Size() (int64, bool) {
|
func (it *BaseIterator) Size() (int64, bool) {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// No subiterators. Only those with subiterators need to do anything here.
|
// No subiterators. Only those with subiterators need to do anything here.
|
||||||
func (n *BaseIterator) GetSubIterators() *list.List {
|
func (it *BaseIterator) GetSubIterators() *list.List {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessor
|
// Accessor
|
||||||
func (b *BaseIterator) Nextable() bool { return b.nextable }
|
func (it *BaseIterator) Nextable() bool { return it.nextable }
|
||||||
|
|
||||||
// Fill the map based on the tags assigned to this iterator. Default
|
// Fill the map based on the tags assigned to this iterator. Default
|
||||||
// functionality works well for most iterators.
|
// functionality works well for most iterators.
|
||||||
func (a *BaseIterator) TagResults(out_map *map[string]TSVal) {
|
func (it *BaseIterator) TagResults(out_map *map[string]TSVal) {
|
||||||
for _, tag := range a.Tags() {
|
for _, tag := range it.Tags() {
|
||||||
(*out_map)[tag] = a.LastResult()
|
(*out_map)[tag] = it.LastResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
for tag, value := range a.FixedTags() {
|
for tag, value := range it.FixedTags() {
|
||||||
(*out_map)[tag] = value
|
(*out_map)[tag] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing to clean up.
|
// Nothing to clean up.
|
||||||
//func (a *BaseIterator) Close() {}
|
//func (a *BaseIterator) Close() {}
|
||||||
func (a *NullIterator) Close() {}
|
func (it *NullIterator) Close() {}
|
||||||
|
|
||||||
func (a *BaseIterator) Reset() {}
|
func (it *BaseIterator) Reset() {}
|
||||||
|
|
||||||
// Here we define the simplest base iterator -- the Null iterator. It contains nothing.
|
// 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,
|
||||||
|
|
@ -244,26 +245,25 @@ type NullIterator struct {
|
||||||
|
|
||||||
// Fairly useless New function.
|
// Fairly useless New function.
|
||||||
func NewNullIterator() *NullIterator {
|
func NewNullIterator() *NullIterator {
|
||||||
var n NullIterator
|
return &NullIterator{}
|
||||||
return &n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NullIterator) Clone() Iterator { return NewNullIterator() }
|
func (it *NullIterator) Clone() Iterator { return NewNullIterator() }
|
||||||
|
|
||||||
// Name the null iterator.
|
// Name the null iterator.
|
||||||
func (n *NullIterator) Type() string { return "null" }
|
func (it *NullIterator) Type() string { return "null" }
|
||||||
|
|
||||||
// A good iterator will close itself when it returns true.
|
// A good iterator will close itself when it returns true.
|
||||||
// Null has nothing it needs to do.
|
// Null has nothing it needs to do.
|
||||||
func (n *NullIterator) Optimize() (Iterator, bool) { return n, false }
|
func (it *NullIterator) Optimize() (Iterator, bool) { return it, false }
|
||||||
|
|
||||||
// Print the null iterator.
|
// Print the null iterator.
|
||||||
func (n *NullIterator) DebugString(indent int) string {
|
func (it *NullIterator) DebugString(indent int) string {
|
||||||
return strings.Repeat(" ", indent) + "(null)"
|
return strings.Repeat(" ", indent) + "(null)"
|
||||||
}
|
}
|
||||||
|
|
||||||
// A null iterator costs nothing. Use it!
|
// A null iterator costs nothing. Use it!
|
||||||
func (n *NullIterator) GetStats() *IteratorStats {
|
func (it *NullIterator) GetStats() *IteratorStats {
|
||||||
return &IteratorStats{0, 0, 0}
|
return &IteratorStats{0, 0, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,9 @@ package graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/barakmich/glog"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/barakmich/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An optional iterator has the subconstraint iterator we wish to be optional
|
// An optional iterator has the subconstraint iterator we wish to be optional
|
||||||
|
|
@ -49,24 +50,24 @@ func NewOptionalIterator(it Iterator) *OptionalIterator {
|
||||||
return &o
|
return &o
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OptionalIterator) Reset() {
|
func (it *OptionalIterator) Reset() {
|
||||||
o.subIt.Reset()
|
it.subIt.Reset()
|
||||||
o.lastCheck = false
|
it.lastCheck = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OptionalIterator) Close() {
|
func (it *OptionalIterator) Close() {
|
||||||
o.subIt.Close()
|
it.subIt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OptionalIterator) Clone() Iterator {
|
func (it *OptionalIterator) Clone() Iterator {
|
||||||
out := NewOptionalIterator(o.subIt.Clone())
|
out := NewOptionalIterator(it.subIt.Clone())
|
||||||
out.CopyTagsFrom(o)
|
out.CopyTagsFrom(it)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nexting the iterator is unsupported -- error and return an empty set.
|
// Nexting the iterator is unsupported -- error and return an empty set.
|
||||||
// (As above, a reasonable alternative would be to Next() an all iterator)
|
// (As above, a reasonable alternative would be to Next() an all iterator)
|
||||||
func (o *OptionalIterator) Next() (TSVal, bool) {
|
func (it *OptionalIterator) Next() (TSVal, bool) {
|
||||||
glog.Errorln("Nexting an un-nextable iterator")
|
glog.Errorln("Nexting an un-nextable iterator")
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
@ -74,9 +75,9 @@ func (o *OptionalIterator) Next() (TSVal, bool) {
|
||||||
// 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
|
||||||
// we had any results whatsoever, and (b) there was another subresult in our
|
// we had any results whatsoever, and (b) there was another subresult in our
|
||||||
// optional subbranch.
|
// optional subbranch.
|
||||||
func (o *OptionalIterator) NextResult() bool {
|
func (it *OptionalIterator) NextResult() bool {
|
||||||
if o.lastCheck {
|
if it.lastCheck {
|
||||||
return o.subIt.NextResult()
|
return it.subIt.NextResult()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -84,48 +85,48 @@ func (o *OptionalIterator) NextResult() bool {
|
||||||
// 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 (o *OptionalIterator) Check(val TSVal) bool {
|
func (it *OptionalIterator) Check(val TSVal) bool {
|
||||||
checked := o.subIt.Check(val)
|
checked := it.subIt.Check(val)
|
||||||
o.lastCheck = checked
|
it.lastCheck = checked
|
||||||
o.Last = val
|
it.Last = val
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (o *OptionalIterator) TagResults(out *map[string]TSVal) {
|
func (it *OptionalIterator) TagResults(out *map[string]TSVal) {
|
||||||
if o.lastCheck == false {
|
if it.lastCheck == false {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
o.subIt.TagResults(out)
|
it.subIt.TagResults(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registers the optional iterator.
|
// Registers the optional iterator.
|
||||||
func (o *OptionalIterator) Type() string { return "optional" }
|
func (it *OptionalIterator) Type() string { return "optional" }
|
||||||
|
|
||||||
// Prints the optional and it's subiterator.
|
// Prints the optional and it's subiterator.
|
||||||
func (o *OptionalIterator) DebugString(indent int) string {
|
func (it *OptionalIterator) 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),
|
||||||
o.Type(),
|
it.Type(),
|
||||||
o.Tags(),
|
it.Tags(),
|
||||||
o.subIt.DebugString(indent+4))
|
it.subIt.DebugString(indent+4))
|
||||||
}
|
}
|
||||||
|
|
||||||
// There's nothing to optimize for an optional. Optimize the subiterator and
|
// There's nothing to optimize for an optional. Optimize the subiterator and
|
||||||
// potentially replace it.
|
// potentially replace it.
|
||||||
func (o *OptionalIterator) Optimize() (Iterator, bool) {
|
func (it *OptionalIterator) Optimize() (Iterator, bool) {
|
||||||
newSub, changed := o.subIt.Optimize()
|
newSub, changed := it.subIt.Optimize()
|
||||||
if changed {
|
if changed {
|
||||||
o.subIt.Close()
|
it.subIt.Close()
|
||||||
o.subIt = newSub
|
it.subIt = newSub
|
||||||
}
|
}
|
||||||
return o, false
|
return it, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're only as expensive as our subiterator. Except, we can't be nexted.
|
// We're only as expensive as our subiterator. Except, we can't be nexted.
|
||||||
func (o *OptionalIterator) GetStats() *IteratorStats {
|
func (it *OptionalIterator) GetStats() *IteratorStats {
|
||||||
subStats := o.subIt.GetStats()
|
subStats := it.subIt.GetStats()
|
||||||
return &IteratorStats{
|
return &IteratorStats{
|
||||||
CheckCost: subStats.CheckCost,
|
CheckCost: subStats.CheckCost,
|
||||||
NextCost: int64(1 << 62),
|
NextCost: int64(1 << 62),
|
||||||
|
|
|
||||||
|
|
@ -54,68 +54,68 @@ func NewShortCircuitOrIterator() *OrIterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all internal iterators
|
// Reset all internal iterators
|
||||||
func (or *OrIterator) Reset() {
|
func (it *OrIterator) Reset() {
|
||||||
for _, it := range or.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
it.Reset()
|
sub.Reset()
|
||||||
}
|
}
|
||||||
or.currentIterator = -1
|
it.currentIterator = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (or *OrIterator) Clone() Iterator {
|
func (it *OrIterator) Clone() Iterator {
|
||||||
var newOr *OrIterator
|
var or *OrIterator
|
||||||
if or.isShortCircuiting {
|
if it.isShortCircuiting {
|
||||||
newOr = NewShortCircuitOrIterator()
|
or = NewShortCircuitOrIterator()
|
||||||
} else {
|
} else {
|
||||||
newOr = NewOrIterator()
|
or = NewOrIterator()
|
||||||
}
|
}
|
||||||
for _, it := range or.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
newOr.AddSubIterator(it.Clone())
|
or.AddSubIterator(sub.Clone())
|
||||||
}
|
}
|
||||||
or.CopyTagsFrom(or)
|
it.CopyTagsFrom(it)
|
||||||
return newOr
|
return or
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a list.List of the subiterators, in order.
|
// Returns a list.List of the subiterators, in order.
|
||||||
func (or *OrIterator) GetSubIterators() *list.List {
|
func (it *OrIterator) GetSubIterators() *list.List {
|
||||||
l := list.New()
|
l := list.New()
|
||||||
for _, it := range or.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
l.PushBack(it)
|
l.PushBack(sub)
|
||||||
}
|
}
|
||||||
return l
|
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
|
||||||
// recurse down it's subiterators.
|
// recurse down it's subiterators.
|
||||||
func (or *OrIterator) TagResults(out *map[string]TSVal) {
|
func (it *OrIterator) TagResults(out *map[string]TSVal) {
|
||||||
or.BaseIterator.TagResults(out)
|
it.BaseIterator.TagResults(out)
|
||||||
or.internalIterators[or.currentIterator].TagResults(out)
|
it.internalIterators[it.currentIterator].TagResults(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (or *OrIterator) GetResultTree() *ResultTree {
|
func (it *OrIterator) GetResultTree() *ResultTree {
|
||||||
tree := NewResultTree(or.LastResult())
|
tree := NewResultTree(it.LastResult())
|
||||||
for _, it := range or.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
tree.AddSubtree(it.GetResultTree())
|
tree.AddSubtree(sub.GetResultTree())
|
||||||
}
|
}
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints information about this iterator.
|
// Prints information about this iterator.
|
||||||
func (or *OrIterator) DebugString(indent int) string {
|
func (it *OrIterator) DebugString(indent int) string {
|
||||||
var total string
|
var total string
|
||||||
for i, it := range or.internalIterators {
|
for i, sub := range it.internalIterators {
|
||||||
total += strings.Repeat(" ", indent+2)
|
total += strings.Repeat(" ", indent+2)
|
||||||
total += fmt.Sprintf("%d:\n%s\n", i, it.DebugString(indent+4))
|
total += fmt.Sprintf("%d:\n%s\n", i, sub.DebugString(indent+4))
|
||||||
}
|
}
|
||||||
var tags string
|
var tags string
|
||||||
for _, k := range or.Tags() {
|
for _, k := range it.Tags() {
|
||||||
tags += fmt.Sprintf("%s;", k)
|
tags += fmt.Sprintf("%s;", k)
|
||||||
}
|
}
|
||||||
spaces := strings.Repeat(" ", indent+2)
|
spaces := strings.Repeat(" ", indent+2)
|
||||||
|
|
||||||
return fmt.Sprintf("%s(%s\n%stags:%s\n%sits:\n%s)",
|
return fmt.Sprintf("%s(%s\n%stags:%s\n%sits:\n%s)",
|
||||||
strings.Repeat(" ", indent),
|
strings.Repeat(" ", indent),
|
||||||
or.Type(),
|
it.Type(),
|
||||||
spaces,
|
spaces,
|
||||||
tags,
|
tags,
|
||||||
spaces,
|
spaces,
|
||||||
|
|
@ -123,49 +123,49 @@ func (or *OrIterator) DebugString(indent int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a subiterator to this Or iterator. Order matters.
|
// Add a subiterator to this Or iterator. Order matters.
|
||||||
func (or *OrIterator) AddSubIterator(sub Iterator) {
|
func (it *OrIterator) AddSubIterator(sub Iterator) {
|
||||||
or.internalIterators = append(or.internalIterators, sub)
|
it.internalIterators = append(it.internalIterators, sub)
|
||||||
or.itCount++
|
it.itCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the Next value from the Or iterator. Because the Or is the
|
// Returns the Next value from the Or iterator. Because the Or is the
|
||||||
// union of its subiterators, it must produce from all subiterators -- unless
|
// union of its subiterators, it must produce from all subiterators -- unless
|
||||||
// it's shortcircuiting, in which case, it's the first one that returns anything.
|
// it's shortcircuiting, in which case, it's the first one that returns anything.
|
||||||
func (or *OrIterator) Next() (TSVal, bool) {
|
func (it *OrIterator) Next() (TSVal, bool) {
|
||||||
NextLogIn(or)
|
NextLogIn(it)
|
||||||
var curr TSVal
|
var curr TSVal
|
||||||
var exists bool
|
var exists bool
|
||||||
firstTime := false
|
firstTime := false
|
||||||
for {
|
for {
|
||||||
if or.currentIterator == -1 {
|
if it.currentIterator == -1 {
|
||||||
or.currentIterator = 0
|
it.currentIterator = 0
|
||||||
firstTime = true
|
firstTime = true
|
||||||
}
|
}
|
||||||
curIt := or.internalIterators[or.currentIterator]
|
curIt := it.internalIterators[it.currentIterator]
|
||||||
curr, exists = curIt.Next()
|
curr, exists = curIt.Next()
|
||||||
if !exists {
|
if !exists {
|
||||||
if or.isShortCircuiting && !firstTime {
|
if it.isShortCircuiting && !firstTime {
|
||||||
return NextLogOut(or, nil, false)
|
return NextLogOut(it, nil, false)
|
||||||
}
|
}
|
||||||
or.currentIterator++
|
it.currentIterator++
|
||||||
if or.currentIterator == or.itCount {
|
if it.currentIterator == it.itCount {
|
||||||
return NextLogOut(or, nil, false)
|
return NextLogOut(it, nil, false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
or.Last = curr
|
it.Last = curr
|
||||||
return NextLogOut(or, curr, true)
|
return NextLogOut(it, curr, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic("Somehow broke out of Next() loop in OrIterator")
|
panic("Somehow broke out of Next() loop in OrIterator")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks a value against the iterators, in order.
|
// Checks a value against the iterators, in order.
|
||||||
func (or *OrIterator) checkSubIts(val TSVal) bool {
|
func (it *OrIterator) checkSubIts(val TSVal) bool {
|
||||||
var subIsGood = false
|
var subIsGood = false
|
||||||
for i, it := range or.internalIterators {
|
for i, sub := range it.internalIterators {
|
||||||
subIsGood = it.Check(val)
|
subIsGood = sub.Check(val)
|
||||||
if subIsGood {
|
if subIsGood {
|
||||||
or.currentIterator = i
|
it.currentIterator = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,27 +173,27 @@ func (or *OrIterator) checkSubIts(val TSVal) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check a value against the entire iterator, in order.
|
// Check a value against the entire iterator, in order.
|
||||||
func (or *OrIterator) Check(val TSVal) bool {
|
func (it *OrIterator) Check(val TSVal) bool {
|
||||||
CheckLogIn(or, val)
|
CheckLogIn(it, val)
|
||||||
anyGood := or.checkSubIts(val)
|
anyGood := it.checkSubIts(val)
|
||||||
if !anyGood {
|
if !anyGood {
|
||||||
return CheckLogOut(or, val, false)
|
return CheckLogOut(it, val, false)
|
||||||
}
|
}
|
||||||
or.Last = val
|
it.Last = val
|
||||||
return CheckLogOut(or, val, true)
|
return CheckLogOut(it, val, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the approximate size of the Or iterator. Because we're dealing
|
// Returns the approximate size of the Or iterator. Because we're dealing
|
||||||
// with a union, we know that the largest we can be is the sum of all the iterators,
|
// with a union, we know that the largest we can be is the sum of all the iterators,
|
||||||
// or in the case of short-circuiting, the longest.
|
// or in the case of short-circuiting, the longest.
|
||||||
func (or *OrIterator) Size() (int64, bool) {
|
func (it *OrIterator) Size() (int64, bool) {
|
||||||
var val int64
|
var val int64
|
||||||
var b bool
|
var b bool
|
||||||
if or.isShortCircuiting {
|
if it.isShortCircuiting {
|
||||||
val = 0
|
val = 0
|
||||||
b = true
|
b = true
|
||||||
for _, it := range or.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
newval, newb := it.Size()
|
newval, newb := sub.Size()
|
||||||
if val < newval {
|
if val < newval {
|
||||||
val = newval
|
val = newval
|
||||||
}
|
}
|
||||||
|
|
@ -202,8 +202,8 @@ func (or *OrIterator) Size() (int64, bool) {
|
||||||
} else {
|
} else {
|
||||||
val = 0
|
val = 0
|
||||||
b = true
|
b = true
|
||||||
for _, it := range or.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
newval, newb := it.Size()
|
newval, newb := sub.Size()
|
||||||
val += newval
|
val += newval
|
||||||
b = newb && b
|
b = newb && b
|
||||||
}
|
}
|
||||||
|
|
@ -215,34 +215,34 @@ func (or *OrIterator) Size() (int64, bool) {
|
||||||
// which satisfy our previous result that are not the result itself. Our
|
// which satisfy our previous result that are not the result itself. Our
|
||||||
// subiterators might, however, so just pass the call recursively. In the case of
|
// subiterators might, however, so just pass the call recursively. In the case of
|
||||||
// shortcircuiting, only allow new results from the currently checked iterator
|
// shortcircuiting, only allow new results from the currently checked iterator
|
||||||
func (or *OrIterator) NextResult() bool {
|
func (it *OrIterator) NextResult() bool {
|
||||||
if or.currentIterator != -1 {
|
if it.currentIterator != -1 {
|
||||||
return or.internalIterators[or.currentIterator].NextResult()
|
return it.internalIterators[it.currentIterator].NextResult()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform or-specific cleanup, of which there currently is none.
|
// Perform or-specific cleanup, of which there currently is none.
|
||||||
func (or *OrIterator) cleanUp() {}
|
func (it *OrIterator) cleanUp() {}
|
||||||
|
|
||||||
// Close this iterator, and, by extension, close the subiterators.
|
// Close this iterator, and, by extension, close the subiterators.
|
||||||
// Close should be idempotent, and it follows that if it's subiterators
|
// Close should be idempotent, and it follows that if it's subiterators
|
||||||
// follow this contract, the And follows the contract.
|
// follow this contract, the And follows the contract.
|
||||||
func (or *OrIterator) Close() {
|
func (it *OrIterator) Close() {
|
||||||
or.cleanUp()
|
it.cleanUp()
|
||||||
for _, it := range or.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
it.Close()
|
sub.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (or *OrIterator) Optimize() (Iterator, bool) {
|
func (it *OrIterator) Optimize() (Iterator, bool) {
|
||||||
oldItList := or.GetSubIterators()
|
oldItList := it.GetSubIterators()
|
||||||
itList := optimizeSubIterators(oldItList)
|
itList := optimizeSubIterators(oldItList)
|
||||||
// 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(oldItList, nil)
|
||||||
newOr := NewOrIterator()
|
newOr := NewOrIterator()
|
||||||
newOr.isShortCircuiting = or.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 e := itList.Front(); e != nil; e = e.Next() {
|
||||||
|
|
@ -250,24 +250,24 @@ func (or *OrIterator) Optimize() (Iterator, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the tags hanging on us (like any good replacement).
|
// Move the tags hanging on us (like any good replacement).
|
||||||
newOr.CopyTagsFrom(or)
|
newOr.CopyTagsFrom(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
|
||||||
// start).
|
// start).
|
||||||
or.cleanUp()
|
it.cleanUp()
|
||||||
return newOr, true
|
return newOr, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (or *OrIterator) GetStats() *IteratorStats {
|
func (it *OrIterator) GetStats() *IteratorStats {
|
||||||
CheckCost := int64(0)
|
CheckCost := int64(0)
|
||||||
NextCost := int64(0)
|
NextCost := int64(0)
|
||||||
Size := int64(0)
|
Size := int64(0)
|
||||||
for _, it := range or.internalIterators {
|
for _, sub := range it.internalIterators {
|
||||||
stats := it.GetStats()
|
stats := sub.GetStats()
|
||||||
NextCost += stats.NextCost
|
NextCost += stats.NextCost
|
||||||
CheckCost += stats.CheckCost
|
CheckCost += stats.CheckCost
|
||||||
if or.isShortCircuiting {
|
if it.isShortCircuiting {
|
||||||
if Size < stats.Size {
|
if Size < stats.Size {
|
||||||
Size = stats.Size
|
Size = stats.Size
|
||||||
}
|
}
|
||||||
|
|
@ -284,4 +284,4 @@ func (or *OrIterator) GetStats() *IteratorStats {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register this as an "or" iterator.
|
// Register this as an "or" iterator.
|
||||||
func (or *OrIterator) Type() string { return "or" }
|
func (it *OrIterator) Type() string { return "or" }
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,16 @@ type ResultTree struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResultTree(result TSVal) *ResultTree {
|
func NewResultTree(result TSVal) *ResultTree {
|
||||||
var tree ResultTree
|
var t ResultTree
|
||||||
tree.subtrees = list.New()
|
t.subtrees = list.New()
|
||||||
tree.result = result
|
t.result = result
|
||||||
return &tree
|
return &t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tree *ResultTree) ToString() string {
|
func (t *ResultTree) ToString() string {
|
||||||
base := fmt.Sprintf("(%d", tree.result)
|
base := fmt.Sprintf("(%d", t.result)
|
||||||
if tree.subtrees.Len() != 0 {
|
if t.subtrees.Len() != 0 {
|
||||||
for e := tree.subtrees.Front(); e != nil; e = e.Next() {
|
for e := t.subtrees.Front(); e != nil; e = e.Next() {
|
||||||
base += fmt.Sprintf(" %s", (e.Value.(*ResultTree)).ToString())
|
base += fmt.Sprintf(" %s", (e.Value.(*ResultTree)).ToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,8 +42,8 @@ func (tree *ResultTree) ToString() string {
|
||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tree *ResultTree) AddSubtree(sub *ResultTree) {
|
func (t *ResultTree) AddSubtree(sub *ResultTree) {
|
||||||
tree.subtrees.PushBack(sub)
|
t.subtrees.PushBack(sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StringResultTreeEvaluator(it Iterator) string {
|
func StringResultTreeEvaluator(it Iterator) string {
|
||||||
|
|
|
||||||
|
|
@ -68,30 +68,30 @@ func NewValueComparisonIterator(
|
||||||
|
|
||||||
// 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
|
||||||
// and our operator, determine whether or not we meet the requirement.
|
// and our operator, determine whether or not we meet the requirement.
|
||||||
func (vc *ValueComparisonIterator) doComparison(val TSVal) bool {
|
func (it *ValueComparisonIterator) doComparison(val TSVal) bool {
|
||||||
//TODO(barakmich): Implement string comparison.
|
//TODO(barakmich): Implement string comparison.
|
||||||
nodeStr := vc.ts.GetNameFor(val)
|
nodeStr := it.ts.GetNameFor(val)
|
||||||
switch cVal := vc.comparisonValue.(type) {
|
switch cVal := it.comparisonValue.(type) {
|
||||||
case int:
|
case int:
|
||||||
cInt := int64(cVal)
|
cInt := int64(cVal)
|
||||||
intVal, err := strconv.ParseInt(nodeStr, 10, 64)
|
intVal, err := strconv.ParseInt(nodeStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return RunIntOp(intVal, vc.op, cInt)
|
return RunIntOp(intVal, it.op, cInt)
|
||||||
case int64:
|
case int64:
|
||||||
intVal, err := strconv.ParseInt(nodeStr, 10, 64)
|
intVal, err := strconv.ParseInt(nodeStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return RunIntOp(intVal, vc.op, cVal)
|
return RunIntOp(intVal, it.op, cVal)
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *ValueComparisonIterator) Close() {
|
func (it *ValueComparisonIterator) Close() {
|
||||||
vc.subIt.Close()
|
it.subIt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunIntOp(a int64, op ComparisonOperator, b int64) bool {
|
func RunIntOp(a int64, op ComparisonOperator, b int64) bool {
|
||||||
|
|
@ -110,84 +110,84 @@ func RunIntOp(a int64, op ComparisonOperator, b int64) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *ValueComparisonIterator) Reset() {
|
func (it *ValueComparisonIterator) Reset() {
|
||||||
vc.subIt.Reset()
|
it.subIt.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *ValueComparisonIterator) Clone() Iterator {
|
func (it *ValueComparisonIterator) Clone() Iterator {
|
||||||
out := NewValueComparisonIterator(vc.subIt.Clone(), vc.op, vc.comparisonValue, vc.ts)
|
out := NewValueComparisonIterator(it.subIt.Clone(), it.op, it.comparisonValue, it.ts)
|
||||||
out.CopyTagsFrom(vc)
|
out.CopyTagsFrom(it)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *ValueComparisonIterator) Next() (TSVal, bool) {
|
func (it *ValueComparisonIterator) Next() (TSVal, bool) {
|
||||||
var val TSVal
|
var val TSVal
|
||||||
var ok bool
|
var ok bool
|
||||||
for {
|
for {
|
||||||
val, ok = vc.subIt.Next()
|
val, ok = it.subIt.Next()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
if vc.doComparison(val) {
|
if it.doComparison(val) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vc.Last = val
|
it.Last = val
|
||||||
return val, ok
|
return val, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *ValueComparisonIterator) NextResult() bool {
|
func (it *ValueComparisonIterator) NextResult() bool {
|
||||||
for {
|
for {
|
||||||
hasNext := vc.subIt.NextResult()
|
hasNext := it.subIt.NextResult()
|
||||||
if !hasNext {
|
if !hasNext {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if vc.doComparison(vc.subIt.LastResult()) {
|
if it.doComparison(it.subIt.LastResult()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vc.Last = vc.subIt.LastResult()
|
it.Last = it.subIt.LastResult()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *ValueComparisonIterator) Check(val TSVal) bool {
|
func (it *ValueComparisonIterator) Check(val TSVal) bool {
|
||||||
if !vc.doComparison(val) {
|
if !it.doComparison(val) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return vc.subIt.Check(val)
|
return it.subIt.Check(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (vc *ValueComparisonIterator) TagResults(out *map[string]TSVal) {
|
func (it *ValueComparisonIterator) TagResults(out *map[string]TSVal) {
|
||||||
vc.BaseIterator.TagResults(out)
|
it.BaseIterator.TagResults(out)
|
||||||
vc.subIt.TagResults(out)
|
it.subIt.TagResults(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registers the value-comparison iterator.
|
// Registers the value-comparison iterator.
|
||||||
func (vc *ValueComparisonIterator) Type() string { return "value-comparison" }
|
func (it *ValueComparisonIterator) Type() string { return "value-comparison" }
|
||||||
|
|
||||||
// Prints the value-comparison and its subiterator.
|
// Prints the value-comparison and its subiterator.
|
||||||
func (vc *ValueComparisonIterator) DebugString(indent int) string {
|
func (it *ValueComparisonIterator) DebugString(indent int) string {
|
||||||
return fmt.Sprintf("%s(%s\n%s)",
|
return fmt.Sprintf("%s(%s\n%s)",
|
||||||
strings.Repeat(" ", indent),
|
strings.Repeat(" ", indent),
|
||||||
vc.Type(), vc.subIt.DebugString(indent+4))
|
it.Type(), it.subIt.DebugString(indent+4))
|
||||||
}
|
}
|
||||||
|
|
||||||
// There's nothing to optimize, locally, for a value-comparison iterator.
|
// There's nothing to optimize, locally, for a value-comparison iterator.
|
||||||
// Replace the underlying iterator if need be.
|
// Replace the underlying iterator if need be.
|
||||||
// potentially replace it.
|
// potentially replace it.
|
||||||
func (vc *ValueComparisonIterator) Optimize() (Iterator, bool) {
|
func (it *ValueComparisonIterator) Optimize() (Iterator, bool) {
|
||||||
newSub, changed := vc.subIt.Optimize()
|
newSub, changed := it.subIt.Optimize()
|
||||||
if changed {
|
if changed {
|
||||||
vc.subIt.Close()
|
it.subIt.Close()
|
||||||
vc.subIt = newSub
|
it.subIt = newSub
|
||||||
}
|
}
|
||||||
return vc, false
|
return it, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're only as expensive as our subiterator.
|
// We're only as expensive as our subiterator.
|
||||||
// Again, optimized value comparison iterators should do better.
|
// Again, optimized value comparison iterators should do better.
|
||||||
func (vc *ValueComparisonIterator) GetStats() *IteratorStats {
|
func (it *ValueComparisonIterator) GetStats() *IteratorStats {
|
||||||
return vc.subIt.GetStats()
|
return it.subIt.GetStats()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue