Merge pull request #31 from kortschak/names

Rewrite receiver names
This commit is contained in:
Barak Michener 2014-06-28 12:45:29 -07:00
commit 3113e11344
22 changed files with 749 additions and 752 deletions

View file

@ -45,70 +45,69 @@ func NewInt64AllIterator(min, max int64) *Int64AllIterator {
}
// Start back at the beginning
func (a *Int64AllIterator) Reset() {
a.at = a.min
func (it *Int64AllIterator) Reset() {
it.at = it.min
}
func (a *Int64AllIterator) Close() {
}
func (it *Int64AllIterator) Close() {}
func (a *Int64AllIterator) Clone() Iterator {
out := NewInt64AllIterator(a.min, a.max)
out.CopyTagsFrom(a)
func (it *Int64AllIterator) Clone() Iterator {
out := NewInt64AllIterator(it.min, it.max)
out.CopyTagsFrom(it)
return out
}
// Prints the All iterator as just an "all".
func (a *Int64AllIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s)", strings.Repeat(" ", indent), a.Type())
func (it *Int64AllIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s)", strings.Repeat(" ", indent), it.Type())
}
// Next() on an Int64 all iterator is a simple incrementing counter.
// Return the next integer, and mark it as the result.
func (a *Int64AllIterator) Next() (TSVal, bool) {
NextLogIn(a)
if a.at == -1 {
return NextLogOut(a, nil, false)
func (it *Int64AllIterator) Next() (TSVal, bool) {
NextLogIn(it)
if it.at == -1 {
return NextLogOut(it, nil, false)
}
val := a.at
a.at = a.at + 1
if a.at > a.max {
a.at = -1
val := it.at
it.at = it.at + 1
if it.at > it.max {
it.at = -1
}
a.Last = val
return NextLogOut(a, val, true)
it.Last = val
return NextLogOut(it, val, true)
}
// The number of elements in an Int64AllIterator is the size of the range.
// The size is exact.
func (a *Int64AllIterator) Size() (int64, bool) {
Size := ((a.max - a.min) + 1)
func (it *Int64AllIterator) Size() (int64, bool) {
Size := ((it.max - it.min) + 1)
return Size, true
}
// Check() for an Int64AllIterator is merely seeing if the passed value is
// withing the range, assuming the value is an int64.
func (a *Int64AllIterator) Check(tsv TSVal) bool {
CheckLogIn(a, tsv)
func (it *Int64AllIterator) Check(tsv TSVal) bool {
CheckLogIn(it, tsv)
v := tsv.(int64)
if a.min <= v && v <= a.max {
a.Last = v
return CheckLogOut(a, v, true)
if it.min <= v && v <= it.max {
it.Last = v
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 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.
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,
// and as big as the range.
func (a *Int64AllIterator) GetStats() *IteratorStats {
s, _ := a.Size()
func (it *Int64AllIterator) GetStats() *IteratorStats {
s, _ := it.Size()
return &IteratorStats{
CheckCost: 1,
NextCost: 1,

View file

@ -41,80 +41,80 @@ func NewAndIterator() *AndIterator {
}
// Reset all internal iterators
func (and *AndIterator) Reset() {
and.primaryIt.Reset()
for _, it := range and.internalIterators {
it.Reset()
func (it *AndIterator) Reset() {
it.primaryIt.Reset()
for _, sub := range it.internalIterators {
sub.Reset()
}
and.checkList = nil
it.checkList = nil
}
func (and *AndIterator) Clone() Iterator {
newAnd := NewAndIterator()
newAnd.AddSubIterator(and.primaryIt.Clone())
newAnd.CopyTagsFrom(and)
for _, it := range and.internalIterators {
newAnd.AddSubIterator(it.Clone())
func (it *AndIterator) Clone() Iterator {
and := NewAndIterator()
and.AddSubIterator(it.primaryIt.Clone())
and.CopyTagsFrom(it)
for _, sub := range it.internalIterators {
and.AddSubIterator(sub.Clone())
}
if and.checkList != nil {
newAnd.optimizeCheck()
if it.checkList != nil {
and.optimizeCheck()
}
return newAnd
return and
}
// 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.PushBack(and.primaryIt)
for _, it := range and.internalIterators {
l.PushBack(it)
l.PushBack(it.primaryIt)
for _, sub := range it.internalIterators {
l.PushBack(sub)
}
return l
}
// Overrides BaseIterator TagResults, as it needs to add it's own results and
// recurse down it's subiterators.
func (and *AndIterator) TagResults(out *map[string]TSVal) {
and.BaseIterator.TagResults(out)
if and.primaryIt != nil {
and.primaryIt.TagResults(out)
func (it *AndIterator) TagResults(out *map[string]TSVal) {
it.BaseIterator.TagResults(out)
if it.primaryIt != nil {
it.primaryIt.TagResults(out)
}
for _, it := range and.internalIterators {
it.TagResults(out)
for _, sub := range it.internalIterators {
sub.TagResults(out)
}
}
// DEPRECATED Returns the ResultTree for this iterator, recurses to it's subiterators.
func (and *AndIterator) GetResultTree() *ResultTree {
tree := NewResultTree(and.LastResult())
tree.AddSubtree(and.primaryIt.GetResultTree())
for _, it := range and.internalIterators {
tree.AddSubtree(it.GetResultTree())
func (it *AndIterator) GetResultTree() *ResultTree {
tree := NewResultTree(it.LastResult())
tree.AddSubtree(it.primaryIt.GetResultTree())
for _, sub := range it.internalIterators {
tree.AddSubtree(sub.GetResultTree())
}
return tree
}
// Prints information about this iterator.
func (and *AndIterator) DebugString(indent int) string {
func (it *AndIterator) DebugString(indent int) string {
var total string
for i, it := range and.internalIterators {
for i, sub := range it.internalIterators {
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
for _, k := range and.Tags() {
for _, k := range it.Tags() {
tags += fmt.Sprintf("%s;", k)
}
spaces := strings.Repeat(" ", indent+2)
return fmt.Sprintf("%s(%s %d\n%stags:%s\n%sprimary_it:\n%s\n%sother_its:\n%s)",
strings.Repeat(" ", indent),
and.Type(),
and.GetUid(),
it.Type(),
it.GetUid(),
spaces,
tags,
spaces,
and.primaryIt.DebugString(indent+4),
it.primaryIt.DebugString(indent+4),
spaces,
total)
}
@ -125,43 +125,42 @@ func (and *AndIterator) DebugString(indent int) string {
// important. Calling Optimize() is the way to change the order based on
// subiterator statistics. Without Optimize(), the order added is the order
// used.
func (and *AndIterator) AddSubIterator(sub Iterator) {
if and.itCount > 0 {
and.internalIterators = append(and.internalIterators, sub)
and.itCount++
func (it *AndIterator) AddSubIterator(sub Iterator) {
if it.itCount > 0 {
it.internalIterators = append(it.internalIterators, sub)
it.itCount++
return
}
and.primaryIt = sub
and.itCount++
it.primaryIt = sub
it.itCount++
}
// 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
// candidate, and check this value against the subiterators. A productive choice
// of primary iterator is therefore very important.
func (and *AndIterator) Next() (TSVal, bool) {
NextLogIn(and)
func (it *AndIterator) Next() (TSVal, bool) {
NextLogIn(it)
var curr TSVal
var exists bool
for {
curr, exists = and.primaryIt.Next()
curr, exists = it.primaryIt.Next()
if !exists {
return NextLogOut(and, nil, false)
return NextLogOut(it, nil, false)
}
if and.checkSubIts(curr) {
and.Last = curr
return NextLogOut(and, curr, true)
if it.checkSubIts(curr) {
it.Last = curr
return NextLogOut(it, curr, true)
}
}
panic("Somehow broke out of Next() loop in AndIterator")
}
// 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
for _, it := range and.internalIterators {
subIsGood = it.Check(val)
for _, sub := range it.internalIterators {
subIsGood = sub.Check(val)
if !subIsGood {
break
}
@ -169,43 +168,43 @@ func (and *AndIterator) checkSubIts(val TSVal) bool {
return subIsGood
}
func (and *AndIterator) checkCheckList(val TSVal) bool {
func (it *AndIterator) checkCheckList(val TSVal) bool {
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)
if !isGood {
break
}
}
return CheckLogOut(and, val, isGood)
return CheckLogOut(it, val, isGood)
}
// Check a value against the entire iterator, in order.
func (and *AndIterator) Check(val TSVal) bool {
CheckLogIn(and, val)
if and.checkList != nil {
return and.checkCheckList(val)
func (it *AndIterator) Check(val TSVal) bool {
CheckLogIn(it, val)
if it.checkList != nil {
return it.checkCheckList(val)
}
mainGood := and.primaryIt.Check(val)
mainGood := it.primaryIt.Check(val)
if !mainGood {
return CheckLogOut(and, val, false)
return CheckLogOut(it, val, false)
}
othersGood := and.checkSubIts(val)
othersGood := it.checkSubIts(val)
if !othersGood {
return CheckLogOut(and, val, false)
return CheckLogOut(it, val, false)
}
and.Last = val
return CheckLogOut(and, val, true)
it.Last = val
return CheckLogOut(it, val, true)
}
// 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
// smallest iterator. This is the heuristic we shall follow. Better heuristics
// welcome.
func (and *AndIterator) Size() (int64, bool) {
val, b := and.primaryIt.Size()
for _, it := range and.internalIterators {
newval, newb := it.Size()
func (it *AndIterator) Size() (int64, bool) {
val, b := it.primaryIt.Size()
for _, sub := range it.internalIterators {
newval, newb := sub.Size()
if 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
// which satisfy our previous result that are not the result itself. Our
// subiterators might, however, so just pass the call recursively.
func (and *AndIterator) NextResult() bool {
if and.primaryIt.NextResult() {
func (it *AndIterator) NextResult() bool {
if it.primaryIt.NextResult() {
return true
}
for _, it := range and.internalIterators {
if it.NextResult() {
for _, sub := range it.internalIterators {
if sub.NextResult() {
return true
}
}
@ -230,19 +229,18 @@ func (and *AndIterator) NextResult() bool {
}
// 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 should be idempotent, and it follows that if it's subiterators
// follow this contract, the And follows the contract.
func (and *AndIterator) Close() {
and.cleanUp()
and.primaryIt.Close()
for _, it := range and.internalIterators {
it.Close()
func (it *AndIterator) Close() {
it.cleanUp()
it.primaryIt.Close()
for _, sub := range it.internalIterators {
sub.Close()
}
}
// Register this as an "and" iterator.
func (and *AndIterator) Type() string { return "and" }
func (it *AndIterator) Type() string { return "and" }

View file

@ -37,10 +37,10 @@ import (
// Optimizes the AndIterator, by picking the most efficient way to Next() and
// 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,
// the rest are Check()ed)
oldItList := and.GetSubIterators()
oldItList := 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
@ -54,10 +54,10 @@ func (and *AndIterator) Optimize() (Iterator, bool) {
// If we can find only one subiterator which is equivalent to this whole and,
// we can replace the And...
out := and.optimizeReplacement(itList)
out := it.optimizeReplacement(itList)
if out != nil {
// ...Move the tags to the replacement...
moveTagsTo(out, and)
moveTagsTo(out, it)
// ...Close everyone except `out`, our replacement...
closeIteratorList(itList, out)
// ...And return it.
@ -80,14 +80,14 @@ func (and *AndIterator) Optimize() (Iterator, bool) {
}
// Move the tags hanging on us (like any good replacement).
newAnd.CopyTagsFrom(and)
newAnd.CopyTagsFrom(it)
newAnd.optimizeCheck()
// 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
// start).
and.cleanUp()
it.cleanUp()
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
// 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 itList.Len() == 0 {
return &NullIterator{}
@ -190,8 +190,8 @@ func optimizeOrder(l *list.List) *list.List {
// optimizeCheck(l) creates an alternate check list, containing the same contents
// but with a new ordering, however it wishes.
func (and *AndIterator) optimizeCheck() {
subIts := and.GetSubIterators()
func (it *AndIterator) optimizeCheck() {
subIts := it.GetSubIterators()
out := list.New()
// 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)
}
and.checkList = out
it.checkList = out
}
// 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 (and *AndIterator) getSubTags() map[string]bool {
subs := and.GetSubIterators()
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)
@ -227,23 +227,23 @@ func (and *AndIterator) getSubTags() map[string]bool {
tags[tag] = true
}
}
for _, tag := range and.Tags() {
for _, tag := range it.Tags() {
tags[tag] = true
}
return tags
}
// moveTagsTo() gets the tags for all of the And's subiterators and the
// And itself, and moves them to `out`.
func moveTagsTo(out Iterator, and *AndIterator) {
tagmap := and.getSubTags()
for _, tag := range out.Tags() {
// 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()
for _, tag := range dst.Tags() {
if tagmap[tag] {
delete(tagmap, tag)
}
}
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
// in the future return different statistics based on how it is optimized.
// For now, however, it's pretty static.
func (and *AndIterator) GetStats() *IteratorStats {
primaryStats := and.primaryIt.GetStats()
func (it *AndIterator) GetStats() *IteratorStats {
primaryStats := it.primaryIt.GetStats()
CheckCost := primaryStats.CheckCost
NextCost := primaryStats.NextCost
Size := primaryStats.Size
for _, it := range and.internalIterators {
stats := it.GetStats()
for _, sub := range it.internalIterators {
stats := sub.GetStats()
NextCost += stats.CheckCost
CheckCost += stats.CheckCost
if Size > stats.Size {

View file

@ -60,98 +60,97 @@ func NewFixedIteratorWithCompare(compareFn Equality) *FixedIterator {
return &it
}
func (f *FixedIterator) Reset() {
f.lastIndex = 0
func (it *FixedIterator) Reset() {
it.lastIndex = 0
}
func (f *FixedIterator) Close() {
}
func (it *FixedIterator) Close() {}
func (f *FixedIterator) Clone() Iterator {
out := NewFixedIteratorWithCompare(f.cmp)
for _, val := range f.values {
func (it *FixedIterator) Clone() Iterator {
out := NewFixedIteratorWithCompare(it.cmp)
for _, val := range it.values {
out.AddValue(val)
}
out.CopyTagsFrom(f)
out.CopyTagsFrom(it)
return out
}
// Add a value to the iterator. The array now contains this value.
// TODO(barakmich): This ought to be a set someday, disallowing repeated values.
func (f *FixedIterator) AddValue(v TSVal) {
f.values = append(f.values, v)
func (it *FixedIterator) AddValue(v TSVal) {
it.values = append(it.values, v)
}
// Print some information about the iterator.
func (f *FixedIterator) DebugString(indent int) string {
func (it *FixedIterator) DebugString(indent int) string {
value := ""
if len(f.values) > 0 {
value = fmt.Sprint(f.values[0])
if len(it.values) > 0 {
value = fmt.Sprint(it.values[0])
}
return fmt.Sprintf("%s(%s tags: %s Size: %d id0: %d)",
strings.Repeat(" ", indent),
f.Type(),
f.FixedTags(),
len(f.values),
it.Type(),
it.FixedTags(),
len(it.values),
value,
)
}
// Register this iterator as a Fixed iterator.
func (f *FixedIterator) Type() string {
func (it *FixedIterator) Type() string {
return "fixed"
}
// 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.
// However, for fixed iterators, which are by definition kind of tiny, this
// isn't a big issue.
CheckLogIn(f, v)
for _, x := range f.values {
if f.cmp(x, v) {
f.Last = x
return CheckLogOut(f, v, true)
CheckLogIn(it, v)
for _, x := range it.values {
if it.cmp(x, v) {
it.Last = x
return CheckLogOut(it, v, true)
}
}
return CheckLogOut(f, v, false)
return CheckLogOut(it, v, false)
}
// Return the next stored value from the iterator.
func (f *FixedIterator) Next() (TSVal, bool) {
NextLogIn(f)
if f.lastIndex == len(f.values) {
return NextLogOut(f, nil, false)
func (it *FixedIterator) Next() (TSVal, bool) {
NextLogIn(it)
if it.lastIndex == len(it.values) {
return NextLogOut(it, nil, false)
}
out := f.values[f.lastIndex]
f.Last = out
f.lastIndex++
return NextLogOut(f, out, true)
out := it.values[it.lastIndex]
it.Last = out
it.lastIndex++
return NextLogOut(it, out, true)
}
// 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
// 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 f, false
return it, false
}
// Size is the number of values stored.
func (f *FixedIterator) Size() (int64, bool) {
return int64(len(f.values)), true
func (it *FixedIterator) Size() (int64, bool) {
return int64(len(it.values)), true
}
// 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.
func (a *FixedIterator) GetStats() *IteratorStats {
func (it *FixedIterator) GetStats() *IteratorStats {
return &IteratorStats{
CheckCost: int64(len(a.values)),
NextCost: int64(len(a.values)),
Size: int64(len(a.values)),
CheckCost: int64(len(it.values)),
NextCost: int64(len(it.values)),
Size: int64(len(it.values)),
}
}

View file

@ -36,8 +36,9 @@ package graph
import (
"container/list"
"fmt"
"github.com/barakmich/glog"
"strings"
"github.com/barakmich/glog"
)
// 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.
func (h *HasaIterator) GetSubIterators() *list.List {
func (it *HasaIterator) GetSubIterators() *list.List {
l := list.New()
l.PushBack(h.primaryIt)
l.PushBack(it.primaryIt)
return l
}
func (h *HasaIterator) Reset() {
h.primaryIt.Reset()
if h.resultIt != nil {
h.resultIt.Close()
func (it *HasaIterator) Reset() {
it.primaryIt.Reset()
if it.resultIt != nil {
it.resultIt.Close()
}
}
func (h *HasaIterator) Clone() Iterator {
out := NewHasaIterator(h.ts, h.primaryIt.Clone(), h.direction)
out.CopyTagsFrom(h)
func (it *HasaIterator) Clone() Iterator {
out := NewHasaIterator(it.ts, it.primaryIt.Clone(), it.direction)
out.CopyTagsFrom(it)
return out
}
// 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,
// then the HasA becomes Null (there are no triples that have any directions).
func (h *HasaIterator) Optimize() (Iterator, bool) {
newPrimary, changed := h.primaryIt.Optimize()
func (it *HasaIterator) Optimize() (Iterator, bool) {
newPrimary, changed := it.primaryIt.Optimize()
if changed {
h.primaryIt = newPrimary
if h.primaryIt.Type() == "null" {
return h.primaryIt, true
it.primaryIt = newPrimary
if it.primaryIt.Type() == "null" {
return it.primaryIt, true
}
}
return h, false
return it, false
}
// Pass the TagResults down the chain.
func (h *HasaIterator) TagResults(out *map[string]TSVal) {
h.BaseIterator.TagResults(out)
h.primaryIt.TagResults(out)
func (it *HasaIterator) TagResults(out *map[string]TSVal) {
it.BaseIterator.TagResults(out)
it.primaryIt.TagResults(out)
}
// DEPRECATED Return results in a ResultTree.
func (h *HasaIterator) GetResultTree() *ResultTree {
tree := NewResultTree(h.LastResult())
tree.AddSubtree(h.primaryIt.GetResultTree())
func (it *HasaIterator) GetResultTree() *ResultTree {
tree := NewResultTree(it.LastResult())
tree.AddSubtree(it.primaryIt.GetResultTree())
return tree
}
// Print some information about this iterator.
func (h *HasaIterator) DebugString(indent int) string {
func (it *HasaIterator) DebugString(indent int) string {
var tags string
for _, k := range h.Tags() {
for _, k := range it.Tags() {
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
// 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.
func (h *HasaIterator) Check(val TSVal) bool {
CheckLogIn(h, val)
func (it *HasaIterator) Check(val TSVal) bool {
CheckLogIn(it, val)
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
if h.resultIt != nil {
h.resultIt.Close()
if it.resultIt != nil {
it.resultIt.Close()
}
h.resultIt = h.ts.GetTripleIterator(h.direction, val)
return CheckLogOut(h, val, h.GetCheckResult())
it.resultIt = it.ts.GetTripleIterator(it.direction, val)
return CheckLogOut(it, val, it.GetCheckResult())
}
// 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
// another match is made.
func (h *HasaIterator) GetCheckResult() bool {
func (it *HasaIterator) GetCheckResult() bool {
for {
linkVal, ok := h.resultIt.Next()
linkVal, ok := it.resultIt.Next()
if !ok {
break
}
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) {
h.Last = h.ts.GetTripleDirection(linkVal, h.direction)
if it.primaryIt.Check(linkVal) {
it.Last = it.ts.GetTripleDirection(linkVal, it.direction)
return true
}
}
@ -158,37 +158,37 @@ func (h *HasaIterator) GetCheckResult() bool {
}
// 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
// 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().
//
// The upshot is, the end of NextResult() bubbles up from the bottom of the
// iterator tree up, and we need to respect that.
if h.primaryIt.NextResult() {
if it.primaryIt.NextResult() {
return true
}
return h.GetCheckResult()
return it.GetCheckResult()
}
// 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,
// pull our direction out of it, and return that.
func (h *HasaIterator) Next() (TSVal, bool) {
NextLogIn(h)
if h.resultIt != nil {
h.resultIt.Close()
func (it *HasaIterator) Next() (TSVal, bool) {
NextLogIn(it)
if it.resultIt != nil {
it.resultIt.Close()
}
h.resultIt = &NullIterator{}
it.resultIt = &NullIterator{}
tID, ok := h.primaryIt.Next()
tID, ok := it.primaryIt.Next()
if !ok {
return NextLogOut(h, 0, false)
return NextLogOut(it, 0, false)
}
name := h.ts.GetTriple(tID).Get(h.direction)
val := h.ts.GetIdFor(name)
h.Last = val
return NextLogOut(h, val, true)
name := it.ts.GetTriple(tID).Get(it.direction)
val := it.ts.GetIdFor(name)
it.Last = val
return NextLogOut(it, val, true)
}
// 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
// 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.
func (h *HasaIterator) GetStats() *IteratorStats {
subitStats := h.primaryIt.GetStats()
func (it *HasaIterator) GetStats() *IteratorStats {
subitStats := it.primaryIt.GetStats()
// TODO(barakmich): These should really come from the triplestore itself
// and be optimized.
faninFactor := int64(1)
@ -213,12 +213,12 @@ func (h *HasaIterator) GetStats() *IteratorStats {
}
// Close the subiterator, the result iterator (if any) and the HasA.
func (h *HasaIterator) Close() {
if h.resultIt != nil {
h.resultIt.Close()
func (it *HasaIterator) Close() {
if it.resultIt != nil {
it.resultIt.Close()
}
h.primaryIt.Close()
it.primaryIt.Close()
}
// Register this iterator as a HasA.
func (h *HasaIterator) Type() string { return "hasa" }
func (it *HasaIterator) Type() string { return "hasa" }

View file

@ -20,8 +20,9 @@ package graph
import (
"container/list"
"fmt"
"github.com/barakmich/glog"
"strings"
"github.com/barakmich/glog"
)
var iterator_n int = 0
@ -120,50 +121,50 @@ type BaseIterator struct {
}
// Called by subclases.
func BaseIteratorInit(b *BaseIterator) {
func BaseIteratorInit(it *BaseIterator) {
// Your basic iterator is nextable
b.nextable = true
b.uid = iterator_n
it.nextable = true
it.uid = iterator_n
if glog.V(2) {
iterator_n++
}
}
func (b *BaseIterator) GetUid() int {
return b.uid
func (it *BaseIterator) GetUid() int {
return it.uid
}
// Adds a tag to the iterator. Most iterators don't need to override.
func (b *BaseIterator) AddTag(tag string) {
if b.tags == nil {
b.tags = make([]string, 0)
func (it *BaseIterator) AddTag(tag string) {
if it.tags == nil {
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) {
if b.fixedTags == nil {
b.fixedTags = make(map[string]TSVal)
func (it *BaseIterator) AddFixedTag(tag string, value TSVal) {
if it.fixedTags == nil {
it.fixedTags = make(map[string]TSVal)
}
b.fixedTags[tag] = value
it.fixedTags[tag] = value
}
// Returns the tags.
func (b *BaseIterator) Tags() []string {
return b.tags
func (it *BaseIterator) Tags() []string {
return it.tags
}
func (b *BaseIterator) FixedTags() map[string]TSVal {
return b.fixedTags
func (it *BaseIterator) FixedTags() map[string]TSVal {
return it.fixedTags
}
func (b *BaseIterator) CopyTagsFrom(other_it Iterator) {
func (it *BaseIterator) CopyTagsFrom(other_it Iterator) {
for _, tag := range other_it.Tags() {
b.AddTag(tag)
it.AddTag(tag)
}
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
func (b *BaseIterator) GetResultTree() *ResultTree {
tree := NewResultTree(b.LastResult())
func (it *BaseIterator) GetResultTree() *ResultTree {
tree := NewResultTree(it.LastResult())
return tree
}
// Nothing in a base iterator.
func (n *BaseIterator) Next() (TSVal, bool) {
func (it *BaseIterator) Next() (TSVal, bool) {
return nil, false
}
func (n *BaseIterator) NextResult() bool {
func (it *BaseIterator) NextResult() bool {
return false
}
// Returns the last result of an iterator.
func (n *BaseIterator) LastResult() TSVal {
return n.Last
func (it *BaseIterator) LastResult() TSVal {
return it.Last
}
// 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
}
// 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
}
// 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
// functionality works well for most iterators.
func (a *BaseIterator) TagResults(out_map *map[string]TSVal) {
for _, tag := range a.Tags() {
(*out_map)[tag] = a.LastResult()
func (it *BaseIterator) TagResults(out_map *map[string]TSVal) {
for _, tag := range it.Tags() {
(*out_map)[tag] = it.LastResult()
}
for tag, value := range a.FixedTags() {
for tag, value := range it.FixedTags() {
(*out_map)[tag] = value
}
}
// Nothing to clean up.
//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.
// 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.
func NewNullIterator() *NullIterator {
var n NullIterator
return &n
return &NullIterator{}
}
func (n *NullIterator) Clone() Iterator { return NewNullIterator() }
func (it *NullIterator) Clone() Iterator { return NewNullIterator() }
// 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.
// 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.
func (n *NullIterator) DebugString(indent int) string {
func (it *NullIterator) DebugString(indent int) string {
return strings.Repeat(" ", indent) + "(null)"
}
// A null iterator costs nothing. Use it!
func (n *NullIterator) GetStats() *IteratorStats {
func (it *NullIterator) GetStats() *IteratorStats {
return &IteratorStats{0, 0, 0}
}

View file

@ -35,7 +35,7 @@ type AllIterator struct {
ro *opt.ReadOptions
}
func NewLevelDBAllIterator(prefix, dir string, ts *TripleStore) *AllIterator {
func NewAllIterator(prefix, dir string, ts *TripleStore) *AllIterator {
var it AllIterator
graph.BaseIteratorInit(&it.BaseIterator)
it.ro = &opt.ReadOptions{}
@ -53,46 +53,46 @@ func NewLevelDBAllIterator(prefix, dir string, ts *TripleStore) *AllIterator {
return &it
}
func (a *AllIterator) Reset() {
if !a.open {
a.it = a.ts.db.NewIterator(nil, a.ro)
a.open = true
func (it *AllIterator) Reset() {
if !it.open {
it.it = it.ts.db.NewIterator(nil, it.ro)
it.open = true
}
a.it.Seek(a.prefix)
if !a.it.Valid() {
a.open = false
a.it.Release()
it.it.Seek(it.prefix)
if !it.it.Valid() {
it.open = false
it.it.Release()
}
}
func (a *AllIterator) Clone() graph.Iterator {
out := NewLevelDBAllIterator(string(a.prefix), a.dir, a.ts)
out.CopyTagsFrom(a)
func (it *AllIterator) Clone() graph.Iterator {
out := NewAllIterator(string(it.prefix), it.dir, it.ts)
out.CopyTagsFrom(it)
return out
}
func (a *AllIterator) Next() (graph.TSVal, bool) {
if !a.open {
a.Last = nil
func (it *AllIterator) Next() (graph.TSVal, bool) {
if !it.open {
it.Last = nil
return nil, false
}
var out []byte
out = make([]byte, len(a.it.Key()))
copy(out, a.it.Key())
a.it.Next()
if !a.it.Valid() {
a.Close()
out = make([]byte, len(it.it.Key()))
copy(out, it.it.Key())
it.it.Next()
if !it.it.Valid() {
it.Close()
}
if !bytes.HasPrefix(out, a.prefix) {
a.Close()
if !bytes.HasPrefix(out, it.prefix) {
it.Close()
return nil, false
}
a.Last = out
it.Last = out
return out, true
}
func (a *AllIterator) Check(v graph.TSVal) bool {
a.Last = v
func (it *AllIterator) Check(v graph.TSVal) bool {
it.Last = v
return true
}
@ -103,8 +103,8 @@ func (lit *AllIterator) Close() {
}
}
func (a *AllIterator) Size() (int64, bool) {
size, err := a.ts.GetApproximateSizeForPrefix(a.prefix)
func (it *AllIterator) Size() (int64, bool) {
size, err := it.ts.GetApproximateSizeForPrefix(it.prefix)
if err == nil {
return size, false
}
@ -112,20 +112,20 @@ func (a *AllIterator) Size() (int64, bool) {
return int64(^uint64(0) >> 1), false
}
func (lit *AllIterator) DebugString(indent int) string {
size, _ := lit.Size()
return fmt.Sprintf("%s(%s tags: %v leveldb size:%d %s %p)", strings.Repeat(" ", indent), lit.Type(), lit.Tags(), size, lit.dir, lit)
func (it *AllIterator) DebugString(indent int) string {
size, _ := it.Size()
return fmt.Sprintf("%s(%s tags: %v leveldb size:%d %s %p)", strings.Repeat(" ", indent), it.Type(), it.Tags(), size, it.dir, it)
}
func (lit *AllIterator) Type() string { return "all" }
func (lit *AllIterator) Sorted() bool { return false }
func (it *AllIterator) Type() string { return "all" }
func (it *AllIterator) Sorted() bool { return false }
func (lit *AllIterator) Optimize() (graph.Iterator, bool) {
return lit, false
func (it *AllIterator) Optimize() (graph.Iterator, bool) {
return it, false
}
func (lit *AllIterator) GetStats() *graph.IteratorStats {
s, _ := lit.Size()
func (it *AllIterator) GetStats() *graph.IteratorStats {
s, _ := it.Size()
return &graph.IteratorStats{
CheckCost: 1,
NextCost: 2,

View file

@ -59,57 +59,57 @@ func NewIterator(prefix, dir string, value graph.TSVal, ts *TripleStore) *Iterat
return &it
}
func (lit *Iterator) Reset() {
if !lit.open {
lit.it = lit.ts.db.NewIterator(nil, lit.ro)
lit.open = true
func (it *Iterator) Reset() {
if !it.open {
it.it = it.ts.db.NewIterator(nil, it.ro)
it.open = true
}
ok := lit.it.Seek(lit.nextPrefix)
ok := it.it.Seek(it.nextPrefix)
if !ok {
lit.open = false
lit.it.Release()
it.open = false
it.it.Release()
}
}
func (lit *Iterator) Clone() graph.Iterator {
out := NewIterator(lit.originalPrefix, lit.dir, lit.checkId, lit.ts)
out.CopyTagsFrom(lit)
func (it *Iterator) Clone() graph.Iterator {
out := NewIterator(it.originalPrefix, it.dir, it.checkId, it.ts)
out.CopyTagsFrom(it)
return out
}
func (lit *Iterator) Close() {
if lit.open {
lit.it.Release()
lit.open = false
func (it *Iterator) Close() {
if it.open {
it.it.Release()
it.open = false
}
}
func (lit *Iterator) Next() (graph.TSVal, bool) {
if lit.it == nil {
lit.Last = nil
func (it *Iterator) Next() (graph.TSVal, bool) {
if it.it == nil {
it.Last = nil
return nil, false
}
if !lit.open {
lit.Last = nil
if !it.open {
it.Last = nil
return nil, false
}
if !lit.it.Valid() {
lit.Last = nil
lit.Close()
if !it.it.Valid() {
it.Last = nil
it.Close()
return nil, false
}
if bytes.HasPrefix(lit.it.Key(), lit.nextPrefix) {
out := make([]byte, len(lit.it.Key()))
copy(out, lit.it.Key())
lit.Last = out
ok := lit.it.Next()
if bytes.HasPrefix(it.it.Key(), it.nextPrefix) {
out := make([]byte, len(it.it.Key()))
copy(out, it.it.Key())
it.Last = out
ok := it.it.Next()
if !ok {
lit.Close()
it.Close()
}
return out, true
}
lit.Close()
lit.Last = nil
it.Close()
it.Last = nil
return nil, false
}
@ -165,44 +165,44 @@ func GetPositionFromPrefix(prefix []byte, dir string, ts *TripleStore) int {
panic("Notreached")
}
func (lit *Iterator) Check(v graph.TSVal) bool {
func (it *Iterator) Check(v graph.TSVal) bool {
val := v.([]byte)
if val[0] == 'z' {
return false
}
offset := GetPositionFromPrefix(val[0:2], lit.dir, lit.ts)
offset := GetPositionFromPrefix(val[0:2], it.dir, it.ts)
if offset != -1 {
if bytes.HasPrefix(val[offset:], lit.checkId[1:]) {
if bytes.HasPrefix(val[offset:], it.checkId[1:]) {
return true
}
} else {
nameForDir := lit.ts.GetTriple(v).Get(lit.dir)
hashForDir := lit.ts.GetIdFor(nameForDir).([]byte)
if bytes.Equal(hashForDir, lit.checkId) {
nameForDir := it.ts.GetTriple(v).Get(it.dir)
hashForDir := it.ts.GetIdFor(nameForDir).([]byte)
if bytes.Equal(hashForDir, it.checkId) {
return true
}
}
return false
}
func (lit *Iterator) Size() (int64, bool) {
return lit.ts.GetSizeFor(lit.checkId), true
func (it *Iterator) Size() (int64, bool) {
return it.ts.GetSizeFor(it.checkId), true
}
func (lit *Iterator) DebugString(indent int) string {
size, _ := lit.Size()
return fmt.Sprintf("%s(%s %d tags: %v dir: %s size:%d %s)", strings.Repeat(" ", indent), lit.Type(), lit.GetUid(), lit.Tags(), lit.dir, size, lit.ts.GetNameFor(lit.checkId))
func (it *Iterator) DebugString(indent int) string {
size, _ := it.Size()
return fmt.Sprintf("%s(%s %d tags: %v dir: %s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.GetUid(), it.Tags(), it.dir, size, it.ts.GetNameFor(it.checkId))
}
func (lit *Iterator) Type() string { return "leveldb" }
func (lit *Iterator) Sorted() bool { return false }
func (it *Iterator) Type() string { return "leveldb" }
func (it *Iterator) Sorted() bool { return false }
func (lit *Iterator) Optimize() (graph.Iterator, bool) {
return lit, false
func (it *Iterator) Optimize() (graph.Iterator, bool) {
return it, false
}
func (lit *Iterator) GetStats() *graph.IteratorStats {
s, _ := lit.Size()
func (it *Iterator) GetStats() *graph.IteratorStats {
s, _ := it.Size()
return &graph.IteratorStats{
CheckCost: 1,
NextCost: 2,

View file

@ -24,9 +24,9 @@ import (
"github.com/barakmich/glog"
"github.com/syndtr/goleveldb/leveldb"
leveldb_cache "github.com/syndtr/goleveldb/leveldb/cache"
leveldb_opt "github.com/syndtr/goleveldb/leveldb/opt"
leveldb_util "github.com/syndtr/goleveldb/leveldb/util"
cache "github.com/syndtr/goleveldb/leveldb/cache"
"github.com/syndtr/goleveldb/leveldb/opt"
util "github.com/syndtr/goleveldb/leveldb/util"
"github.com/google/cayley/graph"
)
@ -35,18 +35,18 @@ const DefaultCacheSize = 2
const DefaultWriteBufferSize = 20
type TripleStore struct {
dbOpts *leveldb_opt.Options
dbOpts *opt.Options
db *leveldb.DB
path string
open bool
size int64
hasher hash.Hash
writeopts *leveldb_opt.WriteOptions
readopts *leveldb_opt.ReadOptions
writeopts *opt.WriteOptions
readopts *opt.ReadOptions
}
func CreateNewLevelDB(path string) bool {
opts := &leveldb_opt.Options{}
opts := &opt.Options{}
db, err := leveldb.OpenFile(path, opts)
if err != nil {
glog.Errorln("Error: couldn't create database", err)
@ -55,7 +55,7 @@ func CreateNewLevelDB(path string) bool {
defer db.Close()
ts := &TripleStore{}
ts.db = db
ts.writeopts = &leveldb_opt.WriteOptions{
ts.writeopts = &opt.WriteOptions{
Sync: true,
}
ts.Close()
@ -69,8 +69,8 @@ func NewTripleStore(path string, options graph.OptionsDict) *TripleStore {
if val, ok := options.GetIntKey("cache_size_mb"); ok {
cache_size = val
}
ts.dbOpts = &leveldb_opt.Options{
BlockCache: leveldb_cache.NewLRUCache(cache_size * leveldb_opt.MiB),
ts.dbOpts = &opt.Options{
BlockCache: cache.NewLRUCache(cache_size * opt.MiB),
}
ts.dbOpts.ErrorIfMissing = true
@ -78,12 +78,12 @@ func NewTripleStore(path string, options graph.OptionsDict) *TripleStore {
if val, ok := options.GetIntKey("write_buffer_mb"); ok {
write_buffer_mb = val
}
ts.dbOpts.WriteBuffer = write_buffer_mb * leveldb_opt.MiB
ts.dbOpts.WriteBuffer = write_buffer_mb * opt.MiB
ts.hasher = sha1.New()
ts.writeopts = &leveldb_opt.WriteOptions{
ts.writeopts = &opt.WriteOptions{
Sync: false,
}
ts.readopts = &leveldb_opt.ReadOptions{}
ts.readopts = &opt.ReadOptions{}
db, err := leveldb.OpenFile(ts.path, ts.dbOpts)
if err != nil {
panic("Error, couldn't open! " + err.Error())
@ -273,19 +273,19 @@ func (ts *TripleStore) AddTripleSet(t_s []*graph.Triple) {
ts.size += int64(newTs)
}
func (ldbts *TripleStore) Close() {
func (ts *TripleStore) Close() {
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, ldbts.size)
err := binary.Write(buf, binary.LittleEndian, ts.size)
if err == nil {
werr := ldbts.db.Put([]byte("__size"), buf.Bytes(), ldbts.writeopts)
werr := ts.db.Put([]byte("__size"), buf.Bytes(), ts.writeopts)
if werr != nil {
glog.Errorf("Couldn't write size before closing!")
}
} else {
glog.Errorf("Couldn't convert size before closing!")
}
ldbts.db.Close()
ldbts.open = false
ts.db.Close()
ts.open = false
}
func (ts *TripleStore) GetTriple(k graph.TSVal) *graph.Triple {
@ -378,7 +378,7 @@ func (ts *TripleStore) GetApproximateSizeForPrefix(pre []byte) (int64, error) {
copy(limit, pre)
end := len(limit) - 1
limit[end]++
ranges := make([]leveldb_util.Range, 1)
ranges := make([]util.Range, 1)
ranges[0].Start = pre
ranges[0].Limit = limit
sizes, err := ts.db.GetApproximateSizes(ranges)
@ -403,11 +403,11 @@ func (ts *TripleStore) GetTripleIterator(dir string, val graph.TSVal) graph.Iter
}
func (ts *TripleStore) GetNodesAllIterator() graph.Iterator {
return NewLevelDBAllIterator("z", "v", ts)
return NewAllIterator("z", "v", ts)
}
func (ts *TripleStore) GetTriplesAllIterator() graph.Iterator {
return NewLevelDBAllIterator("po", "p", ts)
return NewAllIterator("po", "p", ts)
}
func (ts *TripleStore) GetTripleDirection(val graph.TSVal, direction string) graph.TSVal {

View file

@ -30,16 +30,16 @@ func NewMemstoreAllIterator(ts *TripleStore) *AllIterator {
return &out
}
func (memall *AllIterator) Next() (graph.TSVal, bool) {
next, out := memall.Int64AllIterator.Next()
func (it *AllIterator) Next() (graph.TSVal, bool) {
next, out := it.Int64AllIterator.Next()
if !out {
return next, out
}
i64 := next.(int64)
_, ok := memall.ts.revIdMap[i64]
_, ok := it.ts.revIdMap[i64]
if !ok {
return memall.Next()
return it.Next()
}
memall.Last = next
it.Last = next
return next, out
}

View file

@ -38,7 +38,7 @@ type Iterator struct {
collection string
}
func NewMongoIterator(ts *TripleStore, collection string, dir string, val graph.TSVal) *Iterator {
func NewIterator(ts *TripleStore, collection string, dir string, val graph.TSVal) *Iterator {
var m Iterator
graph.BaseIteratorInit(&m.BaseIterator)
@ -70,7 +70,7 @@ func NewMongoIterator(ts *TripleStore, collection string, dir string, val graph.
return &m
}
func NewMongoAllIterator(ts *TripleStore, collection string) *Iterator {
func NewAllIterator(ts *TripleStore, collection string) *Iterator {
var m Iterator
m.ts = ts
m.dir = "all"
@ -88,91 +88,91 @@ func NewMongoAllIterator(ts *TripleStore, collection string) *Iterator {
return &m
}
func (m *Iterator) Reset() {
m.iter.Close()
m.iter = m.ts.db.C(m.collection).Find(m.constraint).Iter()
func (it *Iterator) Reset() {
it.iter.Close()
it.iter = it.ts.db.C(it.collection).Find(it.constraint).Iter()
}
func (m *Iterator) Close() {
m.iter.Close()
func (it *Iterator) Close() {
it.iter.Close()
}
func (m *Iterator) Clone() graph.Iterator {
func (it *Iterator) Clone() graph.Iterator {
var newM graph.Iterator
if m.isAll {
newM = NewMongoAllIterator(m.ts, m.collection)
if it.isAll {
newM = NewAllIterator(it.ts, it.collection)
} else {
newM = NewMongoIterator(m.ts, m.collection, m.dir, m.hash)
newM = NewIterator(it.ts, it.collection, it.dir, it.hash)
}
newM.CopyTagsFrom(m)
newM.CopyTagsFrom(it)
return newM
}
func (m *Iterator) Next() (graph.TSVal, bool) {
func (it *Iterator) Next() (graph.TSVal, bool) {
var result struct {
Id string "_id"
//Sub string "Sub"
//Pred string "Pred"
//Obj string "Obj"
}
found := m.iter.Next(&result)
found := it.iter.Next(&result)
if !found {
err := m.iter.Err()
err := it.iter.Err()
if err != nil {
glog.Errorln("Error Nexting Iterator: ", err)
}
return nil, false
}
m.Last = result.Id
it.Last = result.Id
return result.Id, true
}
func (m *Iterator) Check(v graph.TSVal) bool {
graph.CheckLogIn(m, v)
if m.isAll {
m.Last = v
return graph.CheckLogOut(m, v, true)
func (it *Iterator) Check(v graph.TSVal) bool {
graph.CheckLogIn(it, v)
if it.isAll {
it.Last = v
return graph.CheckLogOut(it, v, true)
}
var offset int
switch m.dir {
switch it.dir {
case "s":
offset = 0
case "p":
offset = (m.ts.hasher.Size() * 2)
offset = (it.ts.hasher.Size() * 2)
case "o":
offset = (m.ts.hasher.Size() * 2) * 2
offset = (it.ts.hasher.Size() * 2) * 2
case "c":
offset = (m.ts.hasher.Size() * 2) * 3
offset = (it.ts.hasher.Size() * 2) * 3
}
val := v.(string)[offset : m.ts.hasher.Size()*2+offset]
if val == m.hash {
m.Last = v
return graph.CheckLogOut(m, v, true)
val := v.(string)[offset : it.ts.hasher.Size()*2+offset]
if val == it.hash {
it.Last = v
return graph.CheckLogOut(it, v, true)
}
return graph.CheckLogOut(m, v, false)
return graph.CheckLogOut(it, v, false)
}
func (m *Iterator) Size() (int64, bool) {
return m.size, true
func (it *Iterator) Size() (int64, bool) {
return it.size, true
}
func (m *Iterator) Type() string {
if m.isAll {
func (it *Iterator) Type() string {
if it.isAll {
return "all"
}
return "mongo"
}
func (m *Iterator) Sorted() bool { return true }
func (m *Iterator) Optimize() (graph.Iterator, bool) { return m, false }
func (it *Iterator) Sorted() bool { return true }
func (it *Iterator) Optimize() (graph.Iterator, bool) { return it, false }
func (m *Iterator) DebugString(indent int) string {
size, _ := m.Size()
return fmt.Sprintf("%s(%s size:%d %s %s)", strings.Repeat(" ", indent), m.Type(), size, m.hash, m.name)
func (it *Iterator) DebugString(indent int) string {
size, _ := it.Size()
return fmt.Sprintf("%s(%s size:%d %s %s)", strings.Repeat(" ", indent), it.Type(), size, it.hash, it.name)
}
func (m *Iterator) GetStats() *graph.IteratorStats {
size, _ := m.Size()
func (it *Iterator) GetStats() *graph.IteratorStats {
size, _ := it.Size()
return &graph.IteratorStats{
CheckCost: 1,
NextCost: 5,

View file

@ -216,15 +216,15 @@ func (ts *TripleStore) GetTriple(val graph.TSVal) *graph.Triple {
}
func (ts *TripleStore) GetTripleIterator(dir string, val graph.TSVal) graph.Iterator {
return NewMongoIterator(ts, "triples", dir, val)
return NewIterator(ts, "triples", dir, val)
}
func (ts *TripleStore) GetNodesAllIterator() graph.Iterator {
return NewMongoAllIterator(ts, "nodes")
return NewAllIterator(ts, "nodes")
}
func (ts *TripleStore) GetTriplesAllIterator() graph.Iterator {
return NewMongoAllIterator(ts, "triples")
return NewAllIterator(ts, "triples")
}
func (ts *TripleStore) GetIdFor(s string) graph.TSVal {

View file

@ -28,8 +28,9 @@ package graph
import (
"fmt"
"github.com/barakmich/glog"
"strings"
"github.com/barakmich/glog"
)
// An optional iterator has the subconstraint iterator we wish to be optional
@ -49,24 +50,24 @@ func NewOptionalIterator(it Iterator) *OptionalIterator {
return &o
}
func (o *OptionalIterator) Reset() {
o.subIt.Reset()
o.lastCheck = false
func (it *OptionalIterator) Reset() {
it.subIt.Reset()
it.lastCheck = false
}
func (o *OptionalIterator) Close() {
o.subIt.Close()
func (it *OptionalIterator) Close() {
it.subIt.Close()
}
func (o *OptionalIterator) Clone() Iterator {
out := NewOptionalIterator(o.subIt.Clone())
out.CopyTagsFrom(o)
func (it *OptionalIterator) Clone() Iterator {
out := NewOptionalIterator(it.subIt.Clone())
out.CopyTagsFrom(it)
return out
}
// Nexting the iterator is unsupported -- error and return an empty set.
// (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")
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
// we had any results whatsoever, and (b) there was another subresult in our
// optional subbranch.
func (o *OptionalIterator) NextResult() bool {
if o.lastCheck {
return o.subIt.NextResult()
func (it *OptionalIterator) NextResult() bool {
if it.lastCheck {
return it.subIt.NextResult()
}
return false
}
@ -84,48 +85,48 @@ func (o *OptionalIterator) NextResult() bool {
// 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
// matched for results purposes.
func (o *OptionalIterator) Check(val TSVal) bool {
checked := o.subIt.Check(val)
o.lastCheck = checked
o.Last = val
func (it *OptionalIterator) Check(val TSVal) bool {
checked := it.subIt.Check(val)
it.lastCheck = checked
it.Last = val
return true
}
// If we failed the check, then the subiterator should not contribute to the result
// set. Otherwise, go ahead and tag it.
func (o *OptionalIterator) TagResults(out *map[string]TSVal) {
if o.lastCheck == false {
func (it *OptionalIterator) TagResults(out *map[string]TSVal) {
if it.lastCheck == false {
return
}
o.subIt.TagResults(out)
it.subIt.TagResults(out)
}
// 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.
func (o *OptionalIterator) DebugString(indent int) string {
func (it *OptionalIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s tags:%s\n%s)",
strings.Repeat(" ", indent),
o.Type(),
o.Tags(),
o.subIt.DebugString(indent+4))
it.Type(),
it.Tags(),
it.subIt.DebugString(indent+4))
}
// There's nothing to optimize for an optional. Optimize the subiterator and
// potentially replace it.
func (o *OptionalIterator) Optimize() (Iterator, bool) {
newSub, changed := o.subIt.Optimize()
func (it *OptionalIterator) Optimize() (Iterator, bool) {
newSub, changed := it.subIt.Optimize()
if changed {
o.subIt.Close()
o.subIt = newSub
it.subIt.Close()
it.subIt = newSub
}
return o, false
return it, false
}
// We're only as expensive as our subiterator. Except, we can't be nexted.
func (o *OptionalIterator) GetStats() *IteratorStats {
subStats := o.subIt.GetStats()
func (it *OptionalIterator) GetStats() *IteratorStats {
subStats := it.subIt.GetStats()
return &IteratorStats{
CheckCost: subStats.CheckCost,
NextCost: int64(1 << 62),

View file

@ -54,68 +54,68 @@ func NewShortCircuitOrIterator() *OrIterator {
}
// Reset all internal iterators
func (or *OrIterator) Reset() {
for _, it := range or.internalIterators {
it.Reset()
func (it *OrIterator) Reset() {
for _, sub := range it.internalIterators {
sub.Reset()
}
or.currentIterator = -1
it.currentIterator = -1
}
func (or *OrIterator) Clone() Iterator {
var newOr *OrIterator
if or.isShortCircuiting {
newOr = NewShortCircuitOrIterator()
func (it *OrIterator) Clone() Iterator {
var or *OrIterator
if it.isShortCircuiting {
or = NewShortCircuitOrIterator()
} else {
newOr = NewOrIterator()
or = NewOrIterator()
}
for _, it := range or.internalIterators {
newOr.AddSubIterator(it.Clone())
for _, sub := range it.internalIterators {
or.AddSubIterator(sub.Clone())
}
or.CopyTagsFrom(or)
return newOr
it.CopyTagsFrom(it)
return or
}
// Returns a list.List of the subiterators, in order.
func (or *OrIterator) GetSubIterators() *list.List {
func (it *OrIterator) GetSubIterators() *list.List {
l := list.New()
for _, it := range or.internalIterators {
l.PushBack(it)
for _, sub := range it.internalIterators {
l.PushBack(sub)
}
return l
}
// Overrides BaseIterator TagResults, as it needs to add it's own results and
// recurse down it's subiterators.
func (or *OrIterator) TagResults(out *map[string]TSVal) {
or.BaseIterator.TagResults(out)
or.internalIterators[or.currentIterator].TagResults(out)
func (it *OrIterator) TagResults(out *map[string]TSVal) {
it.BaseIterator.TagResults(out)
it.internalIterators[it.currentIterator].TagResults(out)
}
// DEPRECATED Returns the ResultTree for this iterator, recurses to it's subiterators.
func (or *OrIterator) GetResultTree() *ResultTree {
tree := NewResultTree(or.LastResult())
for _, it := range or.internalIterators {
tree.AddSubtree(it.GetResultTree())
func (it *OrIterator) GetResultTree() *ResultTree {
tree := NewResultTree(it.LastResult())
for _, sub := range it.internalIterators {
tree.AddSubtree(sub.GetResultTree())
}
return tree
}
// Prints information about this iterator.
func (or *OrIterator) DebugString(indent int) string {
func (it *OrIterator) DebugString(indent int) string {
var total string
for i, it := range or.internalIterators {
for i, sub := range it.internalIterators {
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
for _, k := range or.Tags() {
for _, k := range it.Tags() {
tags += fmt.Sprintf("%s;", k)
}
spaces := strings.Repeat(" ", indent+2)
return fmt.Sprintf("%s(%s\n%stags:%s\n%sits:\n%s)",
strings.Repeat(" ", indent),
or.Type(),
it.Type(),
spaces,
tags,
spaces,
@ -123,49 +123,49 @@ func (or *OrIterator) DebugString(indent int) string {
}
// Add a subiterator to this Or iterator. Order matters.
func (or *OrIterator) AddSubIterator(sub Iterator) {
or.internalIterators = append(or.internalIterators, sub)
or.itCount++
func (it *OrIterator) AddSubIterator(sub Iterator) {
it.internalIterators = append(it.internalIterators, sub)
it.itCount++
}
// Returns the Next value from the Or iterator. Because the Or is the
// 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.
func (or *OrIterator) Next() (TSVal, bool) {
NextLogIn(or)
func (it *OrIterator) Next() (TSVal, bool) {
NextLogIn(it)
var curr TSVal
var exists bool
firstTime := false
for {
if or.currentIterator == -1 {
or.currentIterator = 0
if it.currentIterator == -1 {
it.currentIterator = 0
firstTime = true
}
curIt := or.internalIterators[or.currentIterator]
curIt := it.internalIterators[it.currentIterator]
curr, exists = curIt.Next()
if !exists {
if or.isShortCircuiting && !firstTime {
return NextLogOut(or, nil, false)
if it.isShortCircuiting && !firstTime {
return NextLogOut(it, nil, false)
}
or.currentIterator++
if or.currentIterator == or.itCount {
return NextLogOut(or, nil, false)
it.currentIterator++
if it.currentIterator == it.itCount {
return NextLogOut(it, nil, false)
}
} else {
or.Last = curr
return NextLogOut(or, curr, true)
it.Last = curr
return NextLogOut(it, curr, true)
}
}
panic("Somehow broke out of Next() loop in OrIterator")
}
// 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
for i, it := range or.internalIterators {
subIsGood = it.Check(val)
for i, sub := range it.internalIterators {
subIsGood = sub.Check(val)
if subIsGood {
or.currentIterator = i
it.currentIterator = i
break
}
}
@ -173,27 +173,27 @@ func (or *OrIterator) checkSubIts(val TSVal) bool {
}
// Check a value against the entire iterator, in order.
func (or *OrIterator) Check(val TSVal) bool {
CheckLogIn(or, val)
anyGood := or.checkSubIts(val)
func (it *OrIterator) Check(val TSVal) bool {
CheckLogIn(it, val)
anyGood := it.checkSubIts(val)
if !anyGood {
return CheckLogOut(or, val, false)
return CheckLogOut(it, val, false)
}
or.Last = val
return CheckLogOut(or, val, true)
it.Last = val
return CheckLogOut(it, val, true)
}
// 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,
// 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 b bool
if or.isShortCircuiting {
if it.isShortCircuiting {
val = 0
b = true
for _, it := range or.internalIterators {
newval, newb := it.Size()
for _, sub := range it.internalIterators {
newval, newb := sub.Size()
if val < newval {
val = newval
}
@ -202,8 +202,8 @@ func (or *OrIterator) Size() (int64, bool) {
} else {
val = 0
b = true
for _, it := range or.internalIterators {
newval, newb := it.Size()
for _, sub := range it.internalIterators {
newval, newb := sub.Size()
val += newval
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
// subiterators might, however, so just pass the call recursively. In the case of
// shortcircuiting, only allow new results from the currently checked iterator
func (or *OrIterator) NextResult() bool {
if or.currentIterator != -1 {
return or.internalIterators[or.currentIterator].NextResult()
func (it *OrIterator) NextResult() bool {
if it.currentIterator != -1 {
return it.internalIterators[it.currentIterator].NextResult()
}
return false
}
// 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 should be idempotent, and it follows that if it's subiterators
// follow this contract, the And follows the contract.
func (or *OrIterator) Close() {
or.cleanUp()
for _, it := range or.internalIterators {
it.Close()
func (it *OrIterator) Close() {
it.cleanUp()
for _, sub := range it.internalIterators {
sub.Close()
}
}
func (or *OrIterator) Optimize() (Iterator, bool) {
oldItList := or.GetSubIterators()
func (it *OrIterator) Optimize() (Iterator, bool) {
oldItList := it.GetSubIterators()
itList := optimizeSubIterators(oldItList)
// Close the replaced iterators (they ought to close themselves, but Close()
// is idempotent, so this just protects against any machinations).
closeIteratorList(oldItList, nil)
newOr := NewOrIterator()
newOr.isShortCircuiting = or.isShortCircuiting
newOr.isShortCircuiting = it.isShortCircuiting
// Add the subiterators in order.
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).
newOr.CopyTagsFrom(or)
newOr.CopyTagsFrom(it)
// 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
// start).
or.cleanUp()
it.cleanUp()
return newOr, true
}
func (or *OrIterator) GetStats() *IteratorStats {
func (it *OrIterator) GetStats() *IteratorStats {
CheckCost := int64(0)
NextCost := int64(0)
Size := int64(0)
for _, it := range or.internalIterators {
stats := it.GetStats()
for _, sub := range it.internalIterators {
stats := sub.GetStats()
NextCost += stats.NextCost
CheckCost += stats.CheckCost
if or.isShortCircuiting {
if it.isShortCircuiting {
if Size < stats.Size {
Size = stats.Size
}
@ -284,4 +284,4 @@ func (or *OrIterator) GetStats() *IteratorStats {
}
// Register this as an "or" iterator.
func (or *OrIterator) Type() string { return "or" }
func (it *OrIterator) Type() string { return "or" }

View file

@ -25,16 +25,16 @@ type ResultTree struct {
}
func NewResultTree(result TSVal) *ResultTree {
var tree ResultTree
tree.subtrees = list.New()
tree.result = result
return &tree
var t ResultTree
t.subtrees = list.New()
t.result = result
return &t
}
func (tree *ResultTree) ToString() string {
base := fmt.Sprintf("(%d", tree.result)
if tree.subtrees.Len() != 0 {
for e := tree.subtrees.Front(); e != nil; e = e.Next() {
func (t *ResultTree) ToString() string {
base := fmt.Sprintf("(%d", t.result)
if t.subtrees.Len() != 0 {
for e := t.subtrees.Front(); e != nil; e = e.Next() {
base += fmt.Sprintf(" %s", (e.Value.(*ResultTree)).ToString())
}
}
@ -42,8 +42,8 @@ func (tree *ResultTree) ToString() string {
return base
}
func (tree *ResultTree) AddSubtree(sub *ResultTree) {
tree.subtrees.PushBack(sub)
func (t *ResultTree) AddSubtree(sub *ResultTree) {
t.subtrees.PushBack(sub)
}
func StringResultTreeEvaluator(it Iterator) string {

View file

@ -68,30 +68,30 @@ func NewValueComparisonIterator(
// Here's the non-boilerplate part of the ValueComparison iterator. Given a value
// 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.
nodeStr := vc.ts.GetNameFor(val)
switch cVal := vc.comparisonValue.(type) {
nodeStr := it.ts.GetNameFor(val)
switch cVal := it.comparisonValue.(type) {
case int:
cInt := int64(cVal)
intVal, err := strconv.ParseInt(nodeStr, 10, 64)
if err != nil {
return false
}
return RunIntOp(intVal, vc.op, cInt)
return RunIntOp(intVal, it.op, cInt)
case int64:
intVal, err := strconv.ParseInt(nodeStr, 10, 64)
if err != nil {
return false
}
return RunIntOp(intVal, vc.op, cVal)
return RunIntOp(intVal, it.op, cVal)
default:
return true
}
}
func (vc *ValueComparisonIterator) Close() {
vc.subIt.Close()
func (it *ValueComparisonIterator) Close() {
it.subIt.Close()
}
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() {
vc.subIt.Reset()
func (it *ValueComparisonIterator) Reset() {
it.subIt.Reset()
}
func (vc *ValueComparisonIterator) Clone() Iterator {
out := NewValueComparisonIterator(vc.subIt.Clone(), vc.op, vc.comparisonValue, vc.ts)
out.CopyTagsFrom(vc)
func (it *ValueComparisonIterator) Clone() Iterator {
out := NewValueComparisonIterator(it.subIt.Clone(), it.op, it.comparisonValue, it.ts)
out.CopyTagsFrom(it)
return out
}
func (vc *ValueComparisonIterator) Next() (TSVal, bool) {
func (it *ValueComparisonIterator) Next() (TSVal, bool) {
var val TSVal
var ok bool
for {
val, ok = vc.subIt.Next()
val, ok = it.subIt.Next()
if !ok {
return nil, false
}
if vc.doComparison(val) {
if it.doComparison(val) {
break
}
}
vc.Last = val
it.Last = val
return val, ok
}
func (vc *ValueComparisonIterator) NextResult() bool {
func (it *ValueComparisonIterator) NextResult() bool {
for {
hasNext := vc.subIt.NextResult()
hasNext := it.subIt.NextResult()
if !hasNext {
return false
}
if vc.doComparison(vc.subIt.LastResult()) {
if it.doComparison(it.subIt.LastResult()) {
return true
}
}
vc.Last = vc.subIt.LastResult()
it.Last = it.subIt.LastResult()
return true
}
func (vc *ValueComparisonIterator) Check(val TSVal) bool {
if !vc.doComparison(val) {
func (it *ValueComparisonIterator) Check(val TSVal) bool {
if !it.doComparison(val) {
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
// set. Otherwise, go ahead and tag it.
func (vc *ValueComparisonIterator) TagResults(out *map[string]TSVal) {
vc.BaseIterator.TagResults(out)
vc.subIt.TagResults(out)
func (it *ValueComparisonIterator) TagResults(out *map[string]TSVal) {
it.BaseIterator.TagResults(out)
it.subIt.TagResults(out)
}
// 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.
func (vc *ValueComparisonIterator) DebugString(indent int) string {
func (it *ValueComparisonIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(%s\n%s)",
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.
// Replace the underlying iterator if need be.
// potentially replace it.
func (vc *ValueComparisonIterator) Optimize() (Iterator, bool) {
newSub, changed := vc.subIt.Optimize()
func (it *ValueComparisonIterator) Optimize() (Iterator, bool) {
newSub, changed := it.subIt.Optimize()
if changed {
vc.subIt.Close()
vc.subIt = newSub
it.subIt.Close()
it.subIt = newSub
}
return vc, false
return it, false
}
// We're only as expensive as our subiterator.
// Again, optimized value comparison iterators should do better.
func (vc *ValueComparisonIterator) GetStats() *IteratorStats {
return vc.subIt.GetStats()
func (it *ValueComparisonIterator) GetStats() *IteratorStats {
return it.subIt.GetStats()
}

View file

@ -68,39 +68,39 @@ type GremlinResult struct {
actualResults *map[string]graph.TSVal
}
func (g *Session) ToggleDebug() {
g.debug = !g.debug
func (s *Session) ToggleDebug() {
s.debug = !s.debug
}
func (g *Session) GetQuery(input string, output_struct chan map[string]interface{}) {
func (s *Session) GetQuery(input string, output_struct chan map[string]interface{}) {
defer close(output_struct)
g.queryShape = make(map[string]interface{})
g.lookingForQueryShape = true
g.env.Run(input)
output_struct <- g.queryShape
g.queryShape = nil
s.queryShape = make(map[string]interface{})
s.lookingForQueryShape = true
s.env.Run(input)
output_struct <- s.queryShape
s.queryShape = nil
}
func (g *Session) InputParses(input string) (graph.ParseResult, error) {
script, err := g.env.Compile("", input)
func (s *Session) InputParses(input string) (graph.ParseResult, error) {
script, err := s.env.Compile("", input)
if err != nil {
return graph.ParseFail, err
}
g.script = script
s.script = script
return graph.Parsed, nil
}
func (g *Session) SendResult(result *GremlinResult) bool {
if g.limit >= 0 && g.limit == g.count {
func (s *Session) SendResult(result *GremlinResult) bool {
if s.limit >= 0 && s.limit == s.count {
return false
}
if g.doHalt {
if s.doHalt {
return false
}
if g.currentChannel != nil {
g.currentChannel <- result
g.count++
if g.limit >= 0 && g.limit == g.count {
if s.currentChannel != nil {
s.currentChannel <- result
s.count++
if s.limit >= 0 && s.limit == s.count {
return false
} else {
return true
@ -111,46 +111,46 @@ func (g *Session) SendResult(result *GremlinResult) bool {
var halt = errors.New("Query Timeout")
func (g *Session) runUnsafe(input interface{}) (otto.Value, error) {
g.doHalt = false
func (s *Session) runUnsafe(input interface{}) (otto.Value, error) {
s.doHalt = false
defer func() {
if caught := recover(); caught != nil {
if caught == halt {
g.err = halt
s.err = halt
return
}
panic(caught) // Something else happened, repanic!
}
}()
g.env.Interrupt = make(chan func(), 1) // The buffer prevents blocking
s.env.Interrupt = make(chan func(), 1) // The buffer prevents blocking
if g.timeoutSec != -1 {
if s.timeoutSec != -1 {
go func() {
time.Sleep(g.timeoutSec * time.Second) // Stop after two seconds
g.doHalt = true
if g.env != nil {
g.env.Interrupt <- func() {
time.Sleep(s.timeoutSec * time.Second) // Stop after two seconds
s.doHalt = true
if s.env != nil {
s.env.Interrupt <- func() {
panic(halt)
}
g.env = g.emptyEnv
s.env = s.emptyEnv
}
}()
}
return g.env.Run(input) // Here be dragons (risky code)
return s.env.Run(input) // Here be dragons (risky code)
}
func (g *Session) ExecInput(input string, out chan interface{}, limit int) {
func (s *Session) ExecInput(input string, out chan interface{}, limit int) {
defer close(out)
g.err = nil
g.currentChannel = out
s.err = nil
s.currentChannel = out
var err error
var value otto.Value
if g.script == nil {
value, err = g.runUnsafe(input)
if s.script == nil {
value, err = s.runUnsafe(input)
} else {
value, err = g.runUnsafe(g.script)
value, err = s.runUnsafe(s.script)
}
if err != nil {
out <- &GremlinResult{metaresult: true,
@ -163,9 +163,9 @@ func (g *Session) ExecInput(input string, out chan interface{}, limit int) {
val: &value,
actualResults: nil}
}
g.currentChannel = nil
g.script = nil
g.env = g.emptyEnv
s.currentChannel = nil
s.script = nil
s.env = s.emptyEnv
return
}

View file

@ -24,31 +24,31 @@ import (
"github.com/google/cayley/graph"
)
func (m *MqlQuery) buildFixed(s string) graph.Iterator {
f := m.ses.ts.MakeFixed()
f.AddValue(m.ses.ts.GetIdFor(s))
func (q *Query) buildFixed(s string) graph.Iterator {
f := q.ses.ts.MakeFixed()
f.AddValue(q.ses.ts.GetIdFor(s))
return f
}
func (m *MqlQuery) buildResultIterator(path MqlPath) graph.Iterator {
all := m.ses.ts.GetNodesAllIterator()
func (q *Query) buildResultIterator(path Path) graph.Iterator {
all := q.ses.ts.GetNodesAllIterator()
all.AddTag(string(path))
return graph.NewOptionalIterator(all)
}
func (m *MqlQuery) BuildIteratorTree(query interface{}) {
m.isRepeated = make(map[MqlPath]bool)
m.queryStructure = make(map[MqlPath]map[string]interface{})
m.queryResult = make(map[MqlResultPath]map[string]interface{})
m.queryResult[""] = make(map[string]interface{})
func (q *Query) BuildIteratorTree(query interface{}) {
q.isRepeated = make(map[Path]bool)
q.queryStructure = make(map[Path]map[string]interface{})
q.queryResult = make(map[ResultPath]map[string]interface{})
q.queryResult[""] = make(map[string]interface{})
m.it, m.err = m.buildIteratorTreeInternal(query, NewMqlPath())
if m.err != nil {
m.isError = true
q.it, q.err = q.buildIteratorTreeInternal(query, NewPath())
if q.err != nil {
q.isError = true
}
}
func (m *MqlQuery) buildIteratorTreeInternal(query interface{}, path MqlPath) (graph.Iterator, error) {
func (q *Query) buildIteratorTreeInternal(query interface{}, path Path) (graph.Iterator, error) {
var it graph.Iterator
var err error
err = nil
@ -58,36 +58,36 @@ func (m *MqlQuery) buildIteratorTreeInternal(query interface{}, path MqlPath) (g
// Treat the bool as a string and call it a day.
// Things which are really bool-like are special cases and will be dealt with separately.
if t {
it = m.buildFixed("true")
it = q.buildFixed("true")
}
it = m.buildFixed("false")
it = q.buildFixed("false")
case float64:
// for JSON numbers
// Damn you, Javascript, and your lack of integer values.
if math.Floor(t) == t {
// Treat it like an integer.
it = m.buildFixed(fmt.Sprintf("%d", t))
it = q.buildFixed(fmt.Sprintf("%d", t))
} else {
it = m.buildFixed(fmt.Sprintf("%f", t))
it = q.buildFixed(fmt.Sprintf("%f", t))
}
case string:
// for JSON strings
it = m.buildFixed(t)
it = q.buildFixed(t)
case []interface{}:
// for JSON arrays
m.isRepeated[path] = true
q.isRepeated[path] = true
if len(t) == 0 {
it = m.buildResultIterator(path)
it = q.buildResultIterator(path)
} else if len(t) == 1 {
it, err = m.buildIteratorTreeInternal(t[0], path)
it, err = q.buildIteratorTreeInternal(t[0], path)
} else {
err = errors.New(fmt.Sprintf("Multiple fields at location root%s", path.DisplayString()))
}
case map[string]interface{}:
// for JSON objects
it, err = m.buildIteratorTreeMapInternal(t, path)
it, err = q.buildIteratorTreeMapInternal(t, path)
case nil:
it = m.buildResultIterator(path)
it = q.buildResultIterator(path)
default:
log.Fatal("Unknown JSON type?", query)
}
@ -98,9 +98,9 @@ func (m *MqlQuery) buildIteratorTreeInternal(query interface{}, path MqlPath) (g
return it, nil
}
func (m *MqlQuery) buildIteratorTreeMapInternal(query map[string]interface{}, path MqlPath) (graph.Iterator, error) {
func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path Path) (graph.Iterator, error) {
it := graph.NewAndIterator()
it.AddSubIterator(m.ses.ts.GetNodesAllIterator())
it.AddSubIterator(q.ses.ts.GetNodesAllIterator())
var err error
err = nil
outputStructure := make(map[string]interface{})
@ -122,29 +122,29 @@ func (m *MqlQuery) buildIteratorTreeMapInternal(query map[string]interface{}, pa
// Other special constructs here
var subit graph.Iterator
if key == "id" {
subit, err = m.buildIteratorTreeInternal(subquery, path.Follow(key))
subit, err = q.buildIteratorTreeInternal(subquery, path.Follow(key))
if err != nil {
return nil, err
}
it.AddSubIterator(subit)
} else {
subit, err = m.buildIteratorTreeInternal(subquery, path.Follow(key))
subit, err = q.buildIteratorTreeInternal(subquery, path.Follow(key))
if err != nil {
return nil, err
}
subAnd := graph.NewAndIterator()
predFixed := m.ses.ts.MakeFixed()
predFixed.AddValue(m.ses.ts.GetIdFor(pred))
subAnd.AddSubIterator(graph.NewLinksToIterator(m.ses.ts, predFixed, "p"))
predFixed := q.ses.ts.MakeFixed()
predFixed.AddValue(q.ses.ts.GetIdFor(pred))
subAnd.AddSubIterator(graph.NewLinksToIterator(q.ses.ts, predFixed, "p"))
if reverse {
lto := graph.NewLinksToIterator(m.ses.ts, subit, "s")
lto := graph.NewLinksToIterator(q.ses.ts, subit, "s")
subAnd.AddSubIterator(lto)
hasa := graph.NewHasaIterator(m.ses.ts, subAnd, "o")
hasa := graph.NewHasaIterator(q.ses.ts, subAnd, "o")
it.AddSubIterator(hasa)
} else {
lto := graph.NewLinksToIterator(m.ses.ts, subit, "o")
lto := graph.NewLinksToIterator(q.ses.ts, subit, "o")
subAnd.AddSubIterator(lto)
hasa := graph.NewHasaIterator(m.ses.ts, subAnd, "s")
hasa := graph.NewHasaIterator(q.ses.ts, subAnd, "s")
it.AddSubIterator(hasa)
}
}
@ -152,30 +152,30 @@ func (m *MqlQuery) buildIteratorTreeMapInternal(query map[string]interface{}, pa
if err != nil {
return nil, err
}
m.queryStructure[path] = outputStructure
q.queryStructure[path] = outputStructure
return it, nil
}
type MqlResultPathSlice []MqlResultPath
type ResultPathSlice []ResultPath
func (sl MqlResultPathSlice) Len() int {
return len(sl)
func (p ResultPathSlice) Len() int {
return len(p)
}
func (sl MqlResultPathSlice) Less(i, j int) bool {
iLen := len(strings.Split(string(sl[i]), "\x30"))
jLen := len(strings.Split(string(sl[j]), "\x30"))
func (p ResultPathSlice) Less(i, j int) bool {
iLen := len(strings.Split(string(p[i]), "\x30"))
jLen := len(strings.Split(string(p[j]), "\x30"))
if iLen < jLen {
return true
}
if iLen == jLen {
if len(string(sl[i])) < len(string(sl[j])) {
if len(string(p[i])) < len(string(p[j])) {
return true
}
}
return false
}
func (sl MqlResultPathSlice) Swap(i, j int) {
sl[i], sl[j] = sl[j], sl[i]
func (p ResultPathSlice) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}

View file

@ -20,18 +20,18 @@ import (
"github.com/google/cayley/graph"
)
func (m *MqlQuery) treeifyResult(tags map[string]graph.TSVal) map[MqlResultPath]string {
func (q *Query) treeifyResult(tags map[string]graph.TSVal) map[ResultPath]string {
// Transform the map into something a little more interesting.
results := make(map[MqlPath]string)
results := make(map[Path]string)
for k, v := range tags {
results[MqlPath(k)] = m.ses.ts.GetNameFor(v)
results[Path(k)] = q.ses.ts.GetNameFor(v)
}
resultPaths := make(map[MqlResultPath]string)
resultPaths := make(map[ResultPath]string)
for k, v := range results {
resultPaths[k.ToResultPathFromMap(results)] = v
}
var paths MqlResultPathSlice
var paths ResultPathSlice
for path, _ := range resultPaths {
paths = append(paths, path)
@ -44,32 +44,32 @@ func (m *MqlQuery) treeifyResult(tags map[string]graph.TSVal) map[MqlResultPath]
currentPath := path.getPath()
value := resultPaths[path]
namePath := path.AppendValue(value)
if _, ok := m.queryResult[namePath]; !ok {
if _, ok := q.queryResult[namePath]; !ok {
targetPath, key := path.splitLastPath()
if path == "" {
targetPath, key = "", value
if _, ok := m.queryResult[""][value]; !ok {
m.resultOrder = append(m.resultOrder, value)
if _, ok := q.queryResult[""][value]; !ok {
q.resultOrder = append(q.resultOrder, value)
}
}
if _, ok := m.queryStructure[currentPath]; ok {
if _, ok := q.queryStructure[currentPath]; ok {
// If there's substructure, then copy that in.
newStruct := m.copyPathStructure(currentPath)
if m.isRepeated[currentPath] && currentPath != "" {
switch t := m.queryResult[targetPath][key].(type) {
newStruct := q.copyPathStructure(currentPath)
if q.isRepeated[currentPath] && currentPath != "" {
switch t := q.queryResult[targetPath][key].(type) {
case nil:
x := make([]interface{}, 0)
x = append(x, newStruct)
m.queryResult[targetPath][key] = x
m.queryResult[namePath] = newStruct
q.queryResult[targetPath][key] = x
q.queryResult[namePath] = newStruct
case []interface{}:
m.queryResult[targetPath][key] = append(t, newStruct)
m.queryResult[namePath] = newStruct
q.queryResult[targetPath][key] = append(t, newStruct)
q.queryResult[namePath] = newStruct
}
} else {
m.queryResult[namePath] = newStruct
m.queryResult[targetPath][key] = newStruct
q.queryResult[namePath] = newStruct
q.queryResult[targetPath][key] = newStruct
}
}
}
@ -80,26 +80,26 @@ func (m *MqlQuery) treeifyResult(tags map[string]graph.TSVal) map[MqlResultPath]
currentPath := path.getPath()
value := resultPaths[path]
namePath := path.AppendValue(value)
if _, ok := m.queryStructure[currentPath]; ok {
if _, ok := q.queryStructure[currentPath]; ok {
// We're dealing with ids.
if _, ok := m.queryResult[namePath]["id"]; ok {
m.queryResult[namePath]["id"] = value
if _, ok := q.queryResult[namePath]["id"]; ok {
q.queryResult[namePath]["id"] = value
}
} else {
// Just a value.
targetPath, key := path.splitLastPath()
if m.isRepeated[currentPath] {
switch t := m.queryResult[targetPath][key].(type) {
if q.isRepeated[currentPath] {
switch t := q.queryResult[targetPath][key].(type) {
case nil:
x := make([]interface{}, 0)
x = append(x, value)
m.queryResult[targetPath][key] = x
q.queryResult[targetPath][key] = x
case []interface{}:
m.queryResult[targetPath][key] = append(t, value)
q.queryResult[targetPath][key] = append(t, value)
}
} else {
m.queryResult[targetPath][key] = value
q.queryResult[targetPath][key] = value
}
}
}
@ -107,8 +107,8 @@ func (m *MqlQuery) treeifyResult(tags map[string]graph.TSVal) map[MqlResultPath]
return resultPaths
}
func (m *MqlQuery) buildResults() {
for _, v := range m.resultOrder {
m.results = append(m.results, m.queryResult[""][v])
func (q *Query) buildResults() {
for _, v := range q.resultOrder {
q.results = append(q.results, q.queryResult[""][v])
}
}

View file

@ -40,7 +40,7 @@ func buildTripleStore() *Session {
return NewSession(ts)
}
func compareJsonInterfaces(actual interface{}, expected interface{}, path MqlPath, t *testing.T) {
func compareJsonInterfaces(actual interface{}, expected interface{}, path Path, t *testing.T) {
isError := false
switch ex := expected.(type) {
case bool:
@ -134,7 +134,7 @@ func runAndTestQuery(query string, expected string, t *testing.T) {
actual_struct, _ := ses.GetJson()
var expected_struct interface{}
json.Unmarshal([]byte(expected), &expected_struct)
compareJsonInterfaces(actual_struct, expected_struct, NewMqlPath(), t)
compareJsonInterfaces(actual_struct, expected_struct, NewPath(), t)
ses.ClearJson()
}

View file

@ -21,53 +21,53 @@ import (
"github.com/google/cayley/graph"
)
type MqlPath string
type MqlResultPath string
type Path string
type ResultPath string
type MqlQuery struct {
type Query struct {
ses *Session
it graph.Iterator
isRepeated map[MqlPath]bool
queryStructure map[MqlPath]map[string]interface{}
queryResult map[MqlResultPath]map[string]interface{}
isRepeated map[Path]bool
queryStructure map[Path]map[string]interface{}
queryResult map[ResultPath]map[string]interface{}
results []interface{}
resultOrder []string
isError bool
err error
}
func (mqlQuery *MqlQuery) copyPathStructure(path MqlPath) map[string]interface{} {
func (q *Query) copyPathStructure(path Path) map[string]interface{} {
output := make(map[string]interface{})
for k, v := range mqlQuery.queryStructure[path] {
for k, v := range q.queryStructure[path] {
output[k] = v
}
return output
}
func NewMqlPath() MqlPath {
func NewPath() Path {
return ""
}
func (p MqlPath) Follow(s string) MqlPath {
return MqlPath(fmt.Sprintf("%s\x1E%s", p, s))
func (p Path) Follow(s string) Path {
return Path(fmt.Sprintf("%s\x1E%s", p, s))
}
func (p MqlPath) DisplayString() string {
func (p Path) DisplayString() string {
return strings.Replace(string(p), "\x1E", ".", -1)
}
func NewMqlResultPath() MqlResultPath {
func NewResultPath() ResultPath {
return ""
}
func (p MqlResultPath) FollowPath(followPiece string, value string) MqlResultPath {
func (p ResultPath) FollowPath(followPiece string, value string) ResultPath {
if string(p) == "" {
return MqlResultPath(fmt.Sprintf("%s\x1E%s", value, followPiece))
return ResultPath(fmt.Sprintf("%s\x1E%s", value, followPiece))
}
return MqlResultPath(fmt.Sprintf("%s\x1E%s\x1E%s", p, value, followPiece))
return ResultPath(fmt.Sprintf("%s\x1E%s\x1E%s", p, value, followPiece))
}
func (p MqlResultPath) getPath() MqlPath {
out := NewMqlPath()
func (p ResultPath) getPath() Path {
out := NewPath()
pathPieces := strings.Split(string(p), "\x1E")
for len(pathPieces) > 1 {
a := pathPieces[1]
@ -77,22 +77,22 @@ func (p MqlResultPath) getPath() MqlPath {
return out
}
func (p MqlResultPath) splitLastPath() (MqlResultPath, string) {
func (p ResultPath) splitLastPath() (ResultPath, string) {
pathPieces := strings.Split(string(p), "\x1E")
return MqlResultPath(strings.Join(pathPieces[:len(pathPieces)-1], "\x1E")), pathPieces[len(pathPieces)-1]
return ResultPath(strings.Join(pathPieces[:len(pathPieces)-1], "\x1E")), pathPieces[len(pathPieces)-1]
}
func (p MqlResultPath) AppendValue(value string) MqlResultPath {
func (p ResultPath) AppendValue(value string) ResultPath {
if string(p) == "" {
return MqlResultPath(value)
return ResultPath(value)
}
return MqlResultPath(fmt.Sprintf("%s\x1E%s", p, value))
return ResultPath(fmt.Sprintf("%s\x1E%s", p, value))
}
func (p MqlPath) ToResultPathFromMap(resultMap map[MqlPath]string) MqlResultPath {
output := NewMqlResultPath()
func (p Path) ToResultPathFromMap(resultMap map[Path]string) ResultPath {
output := NewResultPath()
pathPieces := strings.Split(string(p), "\x1E")[1:]
pathSoFar := NewMqlPath()
pathSoFar := NewPath()
for _, piece := range pathPieces {
output = output.FollowPath(piece, resultMap[pathSoFar])
pathSoFar = pathSoFar.Follow(piece)
@ -100,8 +100,8 @@ func (p MqlPath) ToResultPathFromMap(resultMap map[MqlPath]string) MqlResultPath
return output
}
func NewMqlQuery(ses *Session) *MqlQuery {
var q MqlQuery
func NewQuery(ses *Session) *Query {
var q Query
q.ses = ses
q.results = make([]interface{}, 0)
q.resultOrder = make([]string, 0)

View file

@ -26,7 +26,7 @@ import (
type Session struct {
ts graph.TripleStore
currentQuery *MqlQuery
currentQuery *Query
debug bool
}
@ -47,7 +47,7 @@ func (m *Session) GetQuery(input string, output_struct chan map[string]interface
if err != nil {
return
}
m.currentQuery = NewMqlQuery(m)
m.currentQuery = NewQuery(m)
m.currentQuery.BuildIteratorTree(mqlQuery)
output := make(map[string]interface{})
graph.OutputQueryShapeForIterator(m.currentQuery.it, m.ts, &output)
@ -61,7 +61,7 @@ func (m *Session) GetQuery(input string, output_struct chan map[string]interface
output_struct <- output
}
func (m *Session) InputParses(input string) (graph.ParseResult, error) {
func (s *Session) InputParses(input string) (graph.ParseResult, error) {
var x interface{}
err := json.Unmarshal([]byte(input), &x)
if err != nil {
@ -70,19 +70,19 @@ func (m *Session) InputParses(input string) (graph.ParseResult, error) {
return graph.Parsed, nil
}
func (m *Session) ExecInput(input string, c chan interface{}, limit int) {
func (s *Session) ExecInput(input string, c chan interface{}, limit int) {
defer close(c)
var mqlQuery interface{}
err := json.Unmarshal([]byte(input), &mqlQuery)
if err != nil {
return
}
m.currentQuery = NewMqlQuery(m)
m.currentQuery.BuildIteratorTree(mqlQuery)
if m.currentQuery.isError {
s.currentQuery = NewQuery(s)
s.currentQuery.BuildIteratorTree(mqlQuery)
if s.currentQuery.isError {
return
}
it, _ := m.currentQuery.it.Optimize()
it, _ := s.currentQuery.it.Optimize()
if glog.V(2) {
glog.V(2).Infoln(it.DebugString(0))
}
@ -102,13 +102,13 @@ func (m *Session) ExecInput(input string, c chan interface{}, limit int) {
}
}
func (m *Session) ToText(result interface{}) string {
func (s *Session) ToText(result interface{}) string {
tags := *(result.(*map[string]graph.TSVal))
out := fmt.Sprintln("****")
tagKeys := make([]string, len(tags))
m.currentQuery.treeifyResult(tags)
m.currentQuery.buildResults()
r, _ := json.MarshalIndent(m.currentQuery.results, "", " ")
s.currentQuery.treeifyResult(tags)
s.currentQuery.buildResults()
r, _ := json.MarshalIndent(s.currentQuery.results, "", " ")
fmt.Println(string(r))
i := 0
for k, _ := range tags {
@ -120,25 +120,25 @@ func (m *Session) ToText(result interface{}) string {
if k == "$_" {
continue
}
out += fmt.Sprintf("%s : %s\n", k, m.ts.GetNameFor(tags[k]))
out += fmt.Sprintf("%s : %s\n", k, s.ts.GetNameFor(tags[k]))
}
return out
}
func (m *Session) BuildJson(result interface{}) {
m.currentQuery.treeifyResult(*(result.(*map[string]graph.TSVal)))
func (s *Session) BuildJson(result interface{}) {
s.currentQuery.treeifyResult(*(result.(*map[string]graph.TSVal)))
}
func (m *Session) GetJson() (interface{}, error) {
m.currentQuery.buildResults()
if m.currentQuery.isError {
return nil, m.currentQuery.err
func (s *Session) GetJson() (interface{}, error) {
s.currentQuery.buildResults()
if s.currentQuery.isError {
return nil, s.currentQuery.err
} else {
return m.currentQuery.results, nil
return s.currentQuery.results, nil
}
}
func (m *Session) ClearJson() {
// Since we create a new MqlQuery underneath every query, clearing isn't necessary.
func (s *Session) ClearJson() {
// Since we create a new Query underneath every query, clearing isn't necessary.
return
}