diff --git a/graph/iterator.go b/graph/iterator.go index 42fb71a..bb1faba 100644 --- a/graph/iterator.go +++ b/graph/iterator.go @@ -24,18 +24,46 @@ import ( "github.com/barakmich/glog" ) +type Tagger struct { + tags []string + fixedTags map[string]Value +} + +// Adds a tag to the iterator. +func (t *Tagger) Add(tag string) { + t.tags = append(t.tags, tag) +} + +func (t *Tagger) AddFixed(tag string, value Value) { + if t.fixedTags == nil { + t.fixedTags = make(map[string]Value) + } + t.fixedTags[tag] = value +} + +// Returns the tags. The returned value must not be mutated. +func (t *Tagger) Tags() []string { + return t.tags +} + +// Returns the fixed tags. The returned value must not be mutated. +func (t *Tagger) Fixed() map[string]Value { + return t.fixedTags +} + +func (t *Tagger) CopyFrom(src Iterator) { + for _, tag := range src.Tagger().Tags() { + t.Add(tag) + } + + for k, v := range src.Tagger().Fixed() { + t.AddFixed(k, v) + } + +} + type Iterator interface { - // Tags are the way we handle results. By adding a tag to an iterator, we can - // "name" it, in a sense, and at each step of iteration, get a named result. - // TagResults() is therefore the handy way of walking an iterator tree and - // getting the named results. - // - // Tag Accessors. - AddTag(string) - Tags() []string - AddFixedTag(string, Value) - FixedTags() map[string]Value - CopyTagsFrom(Iterator) + Tagger() *Tagger // Fills a tag-to-result-value map. TagResults(map[string]Value) diff --git a/graph/iterator/all_iterator.go b/graph/iterator/all_iterator.go index 22e0d1b..0548fa5 100644 --- a/graph/iterator/all_iterator.go +++ b/graph/iterator/all_iterator.go @@ -32,6 +32,7 @@ import ( // An All iterator across a range of int64 values, from `max` to `min`. type Int64 struct { Base + tags graph.Tagger max, min int64 at int64 } @@ -55,13 +56,28 @@ func (it *Int64) Close() {} func (it *Int64) Clone() graph.Iterator { out := NewInt64(it.min, it.max) - out.CopyTagsFrom(it) + out.tags.CopyFrom(it) return out } +func (it *Int64) Tagger() *graph.Tagger { + return &it.tags +} + +// Fill the map based on the tags assigned to this iterator. +func (it *Int64) TagResults(dst map[string]graph.Value) { + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } +} + // Prints the All iterator as just an "all". func (it *Int64) DebugString(indent int) string { - return fmt.Sprintf("%s(%s tags: %v)", strings.Repeat(" ", indent), it.Type(), it.Tags()) + return fmt.Sprintf("%s(%s tags: %v)", strings.Repeat(" ", indent), it.Type(), it.tags.Tags()) } // Next() on an Int64 all iterator is a simple incrementing counter. diff --git a/graph/iterator/and_iterator.go b/graph/iterator/and_iterator.go index 819cb42..3f129cc 100644 --- a/graph/iterator/and_iterator.go +++ b/graph/iterator/and_iterator.go @@ -26,6 +26,7 @@ import ( // be Next()ed if next is called. type And struct { Base + tags graph.Tagger internalIterators []graph.Iterator itCount int primaryIt graph.Iterator @@ -50,10 +51,33 @@ func (it *And) Reset() { it.checkList = nil } +func (it *And) Tagger() *graph.Tagger { + return &it.tags +} + +// Overrides Base TagResults, as it needs to add it's own results and +// recurse down it's subiterators. +func (it *And) TagResults(dst map[string]graph.Value) { + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } + + if it.primaryIt != nil { + it.primaryIt.TagResults(dst) + } + for _, sub := range it.internalIterators { + sub.TagResults(dst) + } +} + func (it *And) Clone() graph.Iterator { and := NewAnd() and.AddSubIterator(it.primaryIt.Clone()) - and.CopyTagsFrom(it) + and.tags.CopyFrom(it) for _, sub := range it.internalIterators { and.AddSubIterator(sub.Clone()) } @@ -71,18 +95,6 @@ func (it *And) SubIterators() []graph.Iterator { return iters } -// Overrides Base TagResults, as it needs to add it's own results and -// recurse down it's subiterators. -func (it *And) TagResults(dst map[string]graph.Value) { - it.Base.TagResults(dst) - if it.primaryIt != nil { - it.primaryIt.TagResults(dst) - } - for _, sub := range it.internalIterators { - sub.TagResults(dst) - } -} - // DEPRECATED Returns the ResultTree for this iterator, recurses to it's subiterators. func (it *And) ResultTree() *graph.ResultTree { tree := graph.NewResultTree(it.Result()) @@ -101,7 +113,7 @@ func (it *And) DebugString(indent int) string { total += fmt.Sprintf("%d:\n%s\n", i, sub.DebugString(indent+4)) } var tags string - for _, k := range it.Tags() { + for _, k := range it.tags.Tags() { tags += fmt.Sprintf("%s;", k) } spaces := strings.Repeat(" ", indent+2) diff --git a/graph/iterator/and_iterator_optimize.go b/graph/iterator/and_iterator_optimize.go index b556094..06def9f 100644 --- a/graph/iterator/and_iterator_optimize.go +++ b/graph/iterator/and_iterator_optimize.go @@ -82,7 +82,7 @@ func (it *And) Optimize() (graph.Iterator, bool) { } // Move the tags hanging on us (like any good replacement). - newAnd.CopyTagsFrom(it) + newAnd.tags.CopyFrom(it) newAnd.optimizeCheck() @@ -213,11 +213,11 @@ func (it *And) optimizeCheck() { func (it *And) getSubTags() map[string]struct{} { tags := make(map[string]struct{}) for _, sub := range it.SubIterators() { - for _, tag := range sub.Tags() { + for _, tag := range sub.Tagger().Tags() { tags[tag] = struct{}{} } } - for _, tag := range it.Tags() { + for _, tag := range it.tags.Tags() { tags[tag] = struct{}{} } return tags @@ -227,13 +227,14 @@ func (it *And) getSubTags() map[string]struct{} { // src itself, and moves them to dst. func moveTagsTo(dst graph.Iterator, src *And) { tags := src.getSubTags() - for _, tag := range dst.Tags() { + for _, tag := range dst.Tagger().Tags() { if _, ok := tags[tag]; ok { delete(tags, tag) } } + dt := dst.Tagger() for k := range tags { - dst.AddTag(k) + dt.Add(k) } } diff --git a/graph/iterator/and_iterator_optimize_test.go b/graph/iterator/and_iterator_optimize_test.go index e8d6a80..00b3807 100644 --- a/graph/iterator/and_iterator_optimize_test.go +++ b/graph/iterator/and_iterator_optimize_test.go @@ -32,9 +32,9 @@ func TestIteratorPromotion(t *testing.T) { a := NewAnd() a.AddSubIterator(all) a.AddSubIterator(fixed) - all.AddTag("a") - fixed.AddTag("b") - a.AddTag("c") + all.Tagger().Add("a") + fixed.Tagger().Add("b") + a.Tagger().Add("c") newIt, changed := a.Optimize() if !changed { t.Error("Iterator didn't optimize") @@ -43,7 +43,7 @@ func TestIteratorPromotion(t *testing.T) { t.Error("Expected fixed iterator") } tagsExpected := []string{"a", "b", "c"} - tags := newIt.Tags() + tags := newIt.Tagger().Tags() sort.Strings(tags) if !reflect.DeepEqual(tags, tagsExpected) { t.Fatal("Tags don't match") @@ -67,9 +67,9 @@ func TestNullIteratorAnd(t *testing.T) { func TestReorderWithTag(t *testing.T) { all := NewInt64(100, 300) - all.AddTag("good") + all.Tagger().Add("good") all2 := NewInt64(1, 30000) - all2.AddTag("slow") + all2.Tagger().Add("slow") a := NewAnd() // Make all2 the default iterator a.AddSubIterator(all2) @@ -82,7 +82,7 @@ func TestReorderWithTag(t *testing.T) { expectedTags := []string{"good", "slow"} tagsOut := make([]string, 0) for _, sub := range newIt.SubIterators() { - for _, x := range sub.Tags() { + for _, x := range sub.Tagger().Tags() { tagsOut = append(tagsOut, x) } } @@ -93,9 +93,9 @@ func TestReorderWithTag(t *testing.T) { func TestAndStatistics(t *testing.T) { all := NewInt64(100, 300) - all.AddTag("good") + all.Tagger().Add("good") all2 := NewInt64(1, 30000) - all2.AddTag("slow") + all2.Tagger().Add("slow") a := NewAnd() // Make all2 the default iterator a.AddSubIterator(all2) diff --git a/graph/iterator/and_iterator_test.go b/graph/iterator/and_iterator_test.go index 60633e1..82d290a 100644 --- a/graph/iterator/and_iterator_test.go +++ b/graph/iterator/and_iterator_test.go @@ -24,11 +24,11 @@ import ( func TestTag(t *testing.T) { fix1 := newFixed() fix1.Add(234) - fix1.AddTag("foo") + fix1.Tagger().Add("foo") and := NewAnd() and.AddSubIterator(fix1) - and.AddTag("bar") - out := fix1.Tags() + and.Tagger().Add("bar") + out := fix1.Tagger().Tags() if len(out) != 1 { t.Errorf("Expected length 1, got %d", len(out)) } diff --git a/graph/iterator/fixed_iterator.go b/graph/iterator/fixed_iterator.go index c69869b..760962f 100644 --- a/graph/iterator/fixed_iterator.go +++ b/graph/iterator/fixed_iterator.go @@ -31,6 +31,7 @@ import ( // an equality function. type Fixed struct { Base + tags graph.Tagger values []graph.Value lastIndex int cmp Equality @@ -68,12 +69,26 @@ func (it *Fixed) Reset() { func (it *Fixed) Close() {} +func (it *Fixed) Tagger() *graph.Tagger { + return &it.tags +} + +func (it *Fixed) TagResults(dst map[string]graph.Value) { + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } +} + func (it *Fixed) Clone() graph.Iterator { out := NewFixedIteratorWithCompare(it.cmp) for _, val := range it.values { out.Add(val) } - out.CopyTagsFrom(it) + out.tags.CopyFrom(it) return out } @@ -92,7 +107,7 @@ func (it *Fixed) DebugString(indent int) string { return fmt.Sprintf("%s(%s tags: %s Size: %d id0: %d)", strings.Repeat(" ", indent), it.Type(), - it.FixedTags(), + it.tags.Fixed(), len(it.values), value, ) diff --git a/graph/iterator/hasa_iterator.go b/graph/iterator/hasa_iterator.go index b434288..07883e3 100644 --- a/graph/iterator/hasa_iterator.go +++ b/graph/iterator/hasa_iterator.go @@ -47,6 +47,7 @@ import ( // and a temporary holder for the iterator generated on Check(). type HasA struct { Base + tags graph.Tagger ts graph.TripleStore primaryIt graph.Iterator dir graph.Direction @@ -76,9 +77,13 @@ func (it *HasA) Reset() { } } +func (it *HasA) Tagger() *graph.Tagger { + return &it.tags +} + func (it *HasA) Clone() graph.Iterator { out := NewHasA(it.ts, it.primaryIt.Clone(), it.dir) - out.CopyTagsFrom(it) + out.tags.CopyFrom(it) return out } @@ -100,7 +105,14 @@ func (it *HasA) Optimize() (graph.Iterator, bool) { // Pass the TagResults down the chain. func (it *HasA) TagResults(dst map[string]graph.Value) { - it.Base.TagResults(dst) + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } + it.primaryIt.TagResults(dst) } @@ -114,7 +126,7 @@ func (it *HasA) ResultTree() *graph.ResultTree { // Print some information about this iterator. func (it *HasA) DebugString(indent int) string { var tags string - for _, k := range it.Tags() { + for _, k := range it.tags.Tags() { tags += fmt.Sprintf("%s;", k) } return fmt.Sprintf("%s(%s %d tags:%s direction:%s\n%s)", strings.Repeat(" ", indent), it.Type(), it.UID(), tags, it.dir, it.primaryIt.DebugString(indent+4)) diff --git a/graph/iterator/iterator.go b/graph/iterator/iterator.go index 93f5105..24a7a7f 100644 --- a/graph/iterator/iterator.go +++ b/graph/iterator/iterator.go @@ -36,11 +36,9 @@ func NextUID() uint64 { // The Base iterator is the iterator other iterators inherit from to get some // default functionality. type Base struct { - Last graph.Value - tags []string - fixedTags map[string]graph.Value - canNext bool - uid uint64 + Last graph.Value + canNext bool + uid uint64 } // Called by subclases. @@ -56,41 +54,6 @@ func (it *Base) UID() uint64 { return it.uid } -// Adds a tag to the iterator. Most iterators don't need to override. -func (it *Base) AddTag(tag string) { - if it.tags == nil { - it.tags = make([]string, 0) - } - it.tags = append(it.tags, tag) -} - -func (it *Base) AddFixedTag(tag string, value graph.Value) { - if it.fixedTags == nil { - it.fixedTags = make(map[string]graph.Value) - } - it.fixedTags[tag] = value -} - -// Returns the tags. -func (it *Base) Tags() []string { - return it.tags -} - -func (it *Base) FixedTags() map[string]graph.Value { - return it.fixedTags -} - -func (it *Base) CopyTagsFrom(other_it graph.Iterator) { - for _, tag := range other_it.Tags() { - it.AddTag(tag) - } - - for k, v := range other_it.FixedTags() { - it.AddFixedTag(k, v) - } - -} - // Prints a silly debug string. Most classes override. func (it *Base) DebugString(indent int) string { return fmt.Sprintf("%s(base)", strings.Repeat(" ", indent)) @@ -140,18 +103,6 @@ func (it *Base) SubIterators() []graph.Iterator { // Accessor func (it *Base) CanNext() bool { return it.canNext } -// Fill the map based on the tags assigned to this iterator. Default -// functionality works well for most iterators. -func (it *Base) TagResults(dst map[string]graph.Value) { - for _, tag := range it.Tags() { - dst[tag] = it.Result() - } - - for tag, value := range it.FixedTags() { - dst[tag] = value - } -} - // Nothing to clean up. // func (it *Base) Close() {} @@ -164,6 +115,7 @@ func (it *Base) Reset() {} // so it's important to give it a special iterator. type Null struct { Base + tags graph.Tagger } // Fairly useless New function. @@ -171,6 +123,21 @@ func NewNull() *Null { return &Null{} } +func (it *Null) Tagger() *graph.Tagger { + return &it.tags +} + +// Fill the map based on the tags assigned to this iterator. +func (it *Null) TagResults(dst map[string]graph.Value) { + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } +} + func (it *Null) Clone() graph.Iterator { return NewNull() } // Name the null iterator. diff --git a/graph/iterator/linksto_iterator.go b/graph/iterator/linksto_iterator.go index 8488954..63757ae 100644 --- a/graph/iterator/linksto_iterator.go +++ b/graph/iterator/linksto_iterator.go @@ -41,6 +41,7 @@ import ( // `next_it` is the tempoarary iterator held per result in `primary_it`. type LinksTo struct { Base + tags graph.Tagger ts graph.TripleStore primaryIt graph.Iterator dir graph.Direction @@ -67,9 +68,13 @@ func (it *LinksTo) Reset() { it.nextIt = &Null{} } +func (it *LinksTo) Tagger() *graph.Tagger { + return &it.tags +} + func (it *LinksTo) Clone() graph.Iterator { out := NewLinksTo(it.ts, it.primaryIt.Clone(), it.dir) - out.CopyTagsFrom(it) + out.tags.CopyFrom(it) return out } @@ -78,7 +83,14 @@ func (it *LinksTo) Direction() graph.Direction { return it.dir } // Tag these results, and our subiterator's results. func (it *LinksTo) TagResults(dst map[string]graph.Value) { - it.Base.TagResults(dst) + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } + it.primaryIt.TagResults(dst) } diff --git a/graph/iterator/optional_iterator.go b/graph/iterator/optional_iterator.go index 46c6dd6..fc3c41b 100644 --- a/graph/iterator/optional_iterator.go +++ b/graph/iterator/optional_iterator.go @@ -39,6 +39,7 @@ import ( // and whether the last check we received was true or false. type Optional struct { Base + tags graph.Tagger subIt graph.Iterator lastCheck bool } @@ -61,9 +62,13 @@ func (it *Optional) Close() { it.subIt.Close() } +func (it *Optional) Tagger() *graph.Tagger { + return &it.tags +} + func (it *Optional) Clone() graph.Iterator { out := NewOptional(it.subIt.Clone()) - out.CopyTagsFrom(it) + out.tags.CopyFrom(it) return out } @@ -111,7 +116,7 @@ func (it *Optional) DebugString(indent int) string { return fmt.Sprintf("%s(%s tags:%s\n%s)", strings.Repeat(" ", indent), it.Type(), - it.Tags(), + it.tags.Tags(), it.subIt.DebugString(indent+4)) } diff --git a/graph/iterator/or_iterator.go b/graph/iterator/or_iterator.go index 31306eb..da1da5c 100644 --- a/graph/iterator/or_iterator.go +++ b/graph/iterator/or_iterator.go @@ -30,6 +30,7 @@ import ( type Or struct { Base + tags graph.Tagger isShortCircuiting bool internalIterators []graph.Iterator itCount int @@ -62,6 +63,10 @@ func (it *Or) Reset() { it.currentIterator = -1 } +func (it *Or) Tagger() *graph.Tagger { + return &it.tags +} + func (it *Or) Clone() graph.Iterator { var or *Or if it.isShortCircuiting { @@ -72,7 +77,7 @@ func (it *Or) Clone() graph.Iterator { for _, sub := range it.internalIterators { or.AddSubIterator(sub.Clone()) } - or.CopyTagsFrom(it) + or.tags.CopyFrom(it) return or } @@ -84,7 +89,14 @@ func (it *Or) SubIterators() []graph.Iterator { // Overrides BaseIterator TagResults, as it needs to add it's own results and // recurse down it's subiterators. func (it *Or) TagResults(dst map[string]graph.Value) { - it.Base.TagResults(dst) + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } + it.internalIterators[it.currentIterator].TagResults(dst) } @@ -105,7 +117,7 @@ func (it *Or) DebugString(indent int) string { total += fmt.Sprintf("%d:\n%s\n", i, sub.DebugString(indent+4)) } var tags string - for _, k := range it.Tags() { + for _, k := range it.tags.Tags() { tags += fmt.Sprintf("%s;", k) } spaces := strings.Repeat(" ", indent+2) @@ -247,7 +259,7 @@ func (it *Or) Optimize() (graph.Iterator, bool) { } // Move the tags hanging on us (like any good replacement). - newOr.CopyTagsFrom(it) + newOr.tags.CopyFrom(it) // And close ourselves but not our subiterators -- some may still be alive in // the new And (they were unchanged upon calling Optimize() on them, at the diff --git a/graph/iterator/query_shape.go b/graph/iterator/query_shape.go index 3e7eba1..acdbfa8 100644 --- a/graph/iterator/query_shape.go +++ b/graph/iterator/query_shape.go @@ -107,10 +107,10 @@ func (qs *queryShape) StealNode(left *Node, right *Node) { func (qs *queryShape) MakeNode(it graph.Iterator) *Node { n := Node{Id: qs.nodeId} - for _, tag := range it.Tags() { + for _, tag := range it.Tagger().Tags() { n.Tags = append(n.Tags, tag) } - for k, _ := range it.FixedTags() { + for k, _ := range it.Tagger().Fixed() { n.Tags = append(n.Tags, k) } diff --git a/graph/iterator/query_shape_test.go b/graph/iterator/query_shape_test.go index a67ba62..6ebadab 100644 --- a/graph/iterator/query_shape_test.go +++ b/graph/iterator/query_shape_test.go @@ -26,7 +26,7 @@ func hasaWithTag(ts graph.TripleStore, tag string, target string) *HasA { obj := ts.FixedIterator() obj.Add(ts.ValueOf(target)) - obj.AddTag(tag) + obj.Tagger().Add(tag) and.AddSubIterator(NewLinksTo(ts, obj, graph.Object)) pred := ts.FixedIterator() @@ -48,7 +48,7 @@ func TestQueryShape(t *testing.T) { // Given a single linkage iterator's shape. hasa := hasaWithTag(ts, "tag", "cool") - hasa.AddTag("top") + hasa.Tagger().Add("top") shape := make(map[string]interface{}) OutputQueryShapeForIterator(hasa, ts, shape) @@ -93,11 +93,11 @@ func TestQueryShape(t *testing.T) { andInternal := NewAnd() hasa1 := hasaWithTag(ts, "tag1", "cool") - hasa1.AddTag("hasa1") + hasa1.Tagger().Add("hasa1") andInternal.AddSubIterator(hasa1) hasa2 := hasaWithTag(ts, "tag2", "fun") - hasa2.AddTag("hasa2") + hasa2.Tagger().Add("hasa2") andInternal.AddSubIterator(hasa2) pred := ts.FixedIterator() diff --git a/graph/iterator/value_comparison_iterator.go b/graph/iterator/value_comparison_iterator.go index 611d549..6b89cd7 100644 --- a/graph/iterator/value_comparison_iterator.go +++ b/graph/iterator/value_comparison_iterator.go @@ -47,6 +47,7 @@ const ( type Comparison struct { Base + tags graph.Tagger subIt graph.Iterator op Operator val interface{} @@ -111,9 +112,13 @@ func (it *Comparison) Reset() { it.subIt.Reset() } +func (it *Comparison) Tagger() *graph.Tagger { + return &it.tags +} + func (it *Comparison) Clone() graph.Iterator { out := NewComparison(it.subIt.Clone(), it.op, it.val, it.ts) - out.CopyTagsFrom(it) + out.tags.CopyFrom(it) return out } @@ -157,7 +162,14 @@ func (it *Comparison) Check(val graph.Value) bool { // If we failed the check, then the subiterator should not contribute to the result // set. Otherwise, go ahead and tag it. func (it *Comparison) TagResults(dst map[string]graph.Value) { - it.Base.TagResults(dst) + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } + it.subIt.TagResults(dst) } diff --git a/graph/leveldb/all_iterator.go b/graph/leveldb/all_iterator.go index 88a1386..177eec0 100644 --- a/graph/leveldb/all_iterator.go +++ b/graph/leveldb/all_iterator.go @@ -28,6 +28,7 @@ import ( type AllIterator struct { iterator.Base + tags graph.Tagger prefix []byte dir graph.Direction open bool @@ -66,9 +67,23 @@ func (it *AllIterator) Reset() { } } +func (it *AllIterator) Tagger() *graph.Tagger { + return &it.tags +} + +func (it *AllIterator) TagResults(dst map[string]graph.Value) { + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } +} + func (it *AllIterator) Clone() graph.Iterator { out := NewAllIterator(string(it.prefix), it.dir, it.ts) - out.CopyTagsFrom(it) + out.tags.CopyFrom(it) return out } @@ -115,7 +130,7 @@ func (it *AllIterator) Size() (int64, bool) { 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) + return fmt.Sprintf("%s(%s tags: %v leveldb size:%d %s %p)", strings.Repeat(" ", indent), it.Type(), it.tags.Tags(), size, it.dir, it) } func (it *AllIterator) Type() graph.Type { return graph.All } diff --git a/graph/leveldb/iterator.go b/graph/leveldb/iterator.go index 0e3f6e0..53301cc 100644 --- a/graph/leveldb/iterator.go +++ b/graph/leveldb/iterator.go @@ -28,6 +28,7 @@ import ( type Iterator struct { iterator.Base + tags graph.Tagger nextPrefix []byte checkId []byte dir graph.Direction @@ -72,9 +73,23 @@ func (it *Iterator) Reset() { } } +func (it *Iterator) Tagger() *graph.Tagger { + return &it.tags +} + +func (it *Iterator) TagResults(dst map[string]graph.Value) { + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } +} + func (it *Iterator) Clone() graph.Iterator { out := NewIterator(it.originalPrefix, it.dir, it.checkId, it.ts) - out.CopyTagsFrom(it) + out.tags.CopyFrom(it) return out } @@ -192,7 +207,7 @@ func (it *Iterator) Size() (int64, bool) { 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.UID(), it.Tags(), it.dir, size, it.ts.NameOf(it.checkId)) + return fmt.Sprintf("%s(%s %d tags: %v dir: %s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.UID(), it.tags.Tags(), it.dir, size, it.ts.NameOf(it.checkId)) } var levelDBType graph.Type diff --git a/graph/leveldb/leveldb_test.go b/graph/leveldb/leveldb_test.go index 59b32b0..e2855fb 100644 --- a/graph/leveldb/leveldb_test.go +++ b/graph/leveldb/leveldb_test.go @@ -415,7 +415,7 @@ func TestOptimize(t *testing.T) { // With an linksto-fixed pair fixed := ts.FixedIterator() fixed.Add(ts.ValueOf("F")) - fixed.AddTag("internal") + fixed.Tagger().Add("internal") lto := iterator.NewLinksTo(ts, fixed, graph.Object) oldIt := lto.Clone() diff --git a/graph/leveldb/triplestore_iterator_optimize.go b/graph/leveldb/triplestore_iterator_optimize.go index 2234060..f449e0e 100644 --- a/graph/leveldb/triplestore_iterator_optimize.go +++ b/graph/leveldb/triplestore_iterator_optimize.go @@ -42,9 +42,10 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo panic("Sizes lie") } newIt := ts.TripleIterator(it.Direction(), val) - newIt.CopyTagsFrom(it) - for _, tag := range primary.Tags() { - newIt.AddFixedTag(tag, val) + nt := newIt.Tagger() + nt.CopyFrom(it) + for _, tag := range primary.Tagger().Tags() { + nt.AddFixed(tag, val) } it.Close() return newIt, true diff --git a/graph/memstore/iterator.go b/graph/memstore/iterator.go index 3b7bfa8..a5db108 100644 --- a/graph/memstore/iterator.go +++ b/graph/memstore/iterator.go @@ -27,6 +27,7 @@ import ( type Iterator struct { iterator.Base + tags graph.Tagger tree *llrb.LLRB data string isRunning bool @@ -65,10 +66,24 @@ func (it *Iterator) Reset() { it.iterLast = Int64(-1) } +func (it *Iterator) Tagger() *graph.Tagger { + return &it.tags +} + +func (it *Iterator) TagResults(dst map[string]graph.Value) { + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() + } + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } +} + func (it *Iterator) Clone() graph.Iterator { - var new_it = NewLlrbIterator(it.tree, it.data) - new_it.CopyTagsFrom(it) - return new_it + m := NewLlrbIterator(it.tree, it.data) + m.tags.CopyFrom(it) + return m } func (it *Iterator) Close() {} @@ -98,7 +113,7 @@ func (it *Iterator) Check(v graph.Value) bool { func (it *Iterator) DebugString(indent int) string { size, _ := it.Size() - return fmt.Sprintf("%s(%s tags:%s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.Tags(), size, it.data) + return fmt.Sprintf("%s(%s tags:%s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.tags.Tags(), size, it.data) } var memType graph.Type diff --git a/graph/memstore/triplestore_iterator_optimize.go b/graph/memstore/triplestore_iterator_optimize.go index 1a89a16..d7510e3 100644 --- a/graph/memstore/triplestore_iterator_optimize.go +++ b/graph/memstore/triplestore_iterator_optimize.go @@ -42,9 +42,10 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo panic("Sizes lie") } newIt := ts.TripleIterator(it.Direction(), val) - newIt.CopyTagsFrom(it) - for _, tag := range primary.Tags() { - newIt.AddFixedTag(tag, val) + nt := newIt.Tagger() + nt.CopyFrom(it) + for _, tag := range primary.Tagger().Tags() { + nt.AddFixed(tag, val) } return newIt, true } diff --git a/graph/memstore/triplestore_test.go b/graph/memstore/triplestore_test.go index 8b911f1..aa8f93b 100644 --- a/graph/memstore/triplestore_test.go +++ b/graph/memstore/triplestore_test.go @@ -150,7 +150,7 @@ func TestLinksToOptimization(t *testing.T) { fixed.Add(ts.ValueOf("cool")) lto := iterator.NewLinksTo(ts, fixed, graph.Object) - lto.AddTag("foo") + lto.Tagger().Add("foo") newIt, changed := lto.Optimize() if !changed { @@ -165,7 +165,8 @@ func TestLinksToOptimization(t *testing.T) { if v_clone.DebugString(0) != v.DebugString(0) { t.Fatal("Wrong iterator. Got ", v_clone.DebugString(0)) } - if len(v_clone.Tags()) < 1 || v_clone.Tags()[0] != "foo" { + vt := v_clone.Tagger() + if len(vt.Tags()) < 1 || vt.Tags()[0] != "foo" { t.Fatal("Tag on LinksTo did not persist") } } diff --git a/graph/mongo/iterator.go b/graph/mongo/iterator.go index fcf2c5d..4996572 100644 --- a/graph/mongo/iterator.go +++ b/graph/mongo/iterator.go @@ -28,6 +28,7 @@ import ( type Iterator struct { iterator.Base + tags graph.Tagger ts *TripleStore dir graph.Direction iter *mgo.Iter @@ -98,15 +99,29 @@ func (it *Iterator) Close() { it.iter.Close() } -func (it *Iterator) Clone() graph.Iterator { - var newM graph.Iterator - if it.isAll { - newM = NewAllIterator(it.ts, it.collection) - } else { - newM = NewIterator(it.ts, it.collection, it.dir, it.hash) +func (it *Iterator) Tagger() *graph.Tagger { + return &it.tags +} + +func (it *Iterator) TagResults(dst map[string]graph.Value) { + for _, tag := range it.tags.Tags() { + dst[tag] = it.Result() } - newM.CopyTagsFrom(it) - return newM + + for tag, value := range it.tags.Fixed() { + dst[tag] = value + } +} + +func (it *Iterator) Clone() graph.Iterator { + var m *Iterator + if it.isAll { + m = NewAllIterator(it.ts, it.collection) + } else { + m = NewIterator(it.ts, it.collection, it.dir, it.hash) + } + m.tags.CopyFrom(it) + return m } func (it *Iterator) Next() (graph.Value, bool) { diff --git a/graph/mongo/triplestore_iterator_optimize.go b/graph/mongo/triplestore_iterator_optimize.go index 766e84c..76c50d8 100644 --- a/graph/mongo/triplestore_iterator_optimize.go +++ b/graph/mongo/triplestore_iterator_optimize.go @@ -42,9 +42,10 @@ func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bo panic("Sizes lie") } newIt := ts.TripleIterator(it.Direction(), val) - newIt.CopyTagsFrom(it) - for _, tag := range primary.Tags() { - newIt.AddFixedTag(tag, val) + nt := newIt.Tagger() + nt.CopyFrom(it) + for _, tag := range primary.Tagger().Tags() { + nt.AddFixed(tag, val) } it.Close() return newIt, true diff --git a/graph/sexp/parser.go b/graph/sexp/parser.go index 5a3c86a..cfc4956 100644 --- a/graph/sexp/parser.go +++ b/graph/sexp/parser.go @@ -189,7 +189,7 @@ func buildIteratorTree(tree *peg.ExpressionTree, ts graph.TripleStore) graph.Ite nodeID := getIdentString(tree) if tree.Children[0].Name == "Variable" { allIt := ts.NodesAllIterator() - allIt.AddTag(nodeID) + allIt.Tagger().Add(nodeID) out = allIt } else { n := nodeID diff --git a/query/gremlin/build_iterator.go b/query/gremlin/build_iterator.go index 1634368..8231fbb 100644 --- a/query/gremlin/build_iterator.go +++ b/query/gremlin/build_iterator.go @@ -135,7 +135,7 @@ func buildInOutIterator(obj *otto.Object, ts graph.TripleStore, base graph.Itera tags = makeListOfStringsFromArrayValue(one.Object()) } for _, tag := range tags { - predicateNodeIterator.AddTag(tag) + predicateNodeIterator.Tagger().Add(tag) } } @@ -179,7 +179,7 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph. case "tag": it = subIt for _, tag := range stringArgs { - it.AddTag(tag) + it.Tagger().Add(tag) } case "save": all := ts.NodesAllIterator() @@ -187,9 +187,9 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph. return iterator.NewNull() } if len(stringArgs) == 2 { - all.AddTag(stringArgs[1]) + all.Tagger().Add(stringArgs[1]) } else { - all.AddTag(stringArgs[0]) + all.Tagger().Add(stringArgs[0]) } predFixed := ts.FixedIterator() predFixed.Add(ts.ValueOf(stringArgs[0])) @@ -207,9 +207,9 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph. return iterator.NewNull() } if len(stringArgs) == 2 { - all.AddTag(stringArgs[1]) + all.Tagger().Add(stringArgs[1]) } else { - all.AddTag(stringArgs[0]) + all.Tagger().Add(stringArgs[0]) } predFixed := ts.FixedIterator() predFixed.Add(ts.ValueOf(stringArgs[0])) diff --git a/query/gremlin/finals.go b/query/gremlin/finals.go index 7ad0886..414d780 100644 --- a/query/gremlin/finals.go +++ b/query/gremlin/finals.go @@ -38,7 +38,7 @@ func embedFinals(env *otto.Otto, ses *Session, obj *otto.Object) { func allFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { it := buildIteratorTree(obj, ses.ts) - it.AddTag(TopResultTag) + it.Tagger().Add(TopResultTag) ses.limit = -1 ses.count = 0 runIteratorOnSession(it, ses) @@ -51,7 +51,7 @@ func limitFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.Functio if len(call.ArgumentList) > 0 { limitVal, _ := call.Argument(0).ToInteger() it := buildIteratorTree(obj, ses.ts) - it.AddTag(TopResultTag) + it.Tagger().Add(TopResultTag) ses.limit = int(limitVal) ses.count = 0 runIteratorOnSession(it, ses) @@ -63,7 +63,7 @@ func limitFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.Functio func toArrayFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { it := buildIteratorTree(obj, ses.ts) - it.AddTag(TopResultTag) + it.Tagger().Add(TopResultTag) limit := -1 if len(call.ArgumentList) > 0 { limitParsed, _ := call.Argument(0).ToInteger() @@ -90,7 +90,7 @@ func toArrayFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool) func toValueFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { it := buildIteratorTree(obj, ses.ts) - it.AddTag(TopResultTag) + it.Tagger().Add(TopResultTag) limit := 1 var val otto.Value var err error @@ -120,7 +120,7 @@ func toValueFunc(env *otto.Otto, ses *Session, obj *otto.Object, withTags bool) func mapFunc(env *otto.Otto, ses *Session, obj *otto.Object) func(otto.FunctionCall) otto.Value { return func(call otto.FunctionCall) otto.Value { it := buildIteratorTree(obj, ses.ts) - it.AddTag(TopResultTag) + it.Tagger().Add(TopResultTag) limit := -1 if len(call.ArgumentList) == 0 { return otto.NullValue() diff --git a/query/mql/build_iterator.go b/query/mql/build_iterator.go index 2a0d124..108d628 100644 --- a/query/mql/build_iterator.go +++ b/query/mql/build_iterator.go @@ -33,7 +33,7 @@ func (q *Query) buildFixed(s string) graph.Iterator { func (q *Query) buildResultIterator(path Path) graph.Iterator { all := q.ses.ts.NodesAllIterator() - all.AddTag(string(path)) + all.Tagger().Add(string(path)) return all } @@ -97,7 +97,7 @@ func (q *Query) buildIteratorTreeInternal(query interface{}, path Path) (it grap if err != nil { return nil, false, err } - it.AddTag(string(path)) + it.Tagger().Add(string(path)) return it, optional, nil }