Move iterators into separate package

Also reduce API exposure and use standard library more - and fix bugs I
previously introduces in mongo.
This commit is contained in:
kortschak 2014-06-30 22:22:50 +09:30
parent 88be6bee37
commit 1768e593a8
62 changed files with 3240 additions and 3130 deletions

View file

@ -18,7 +18,6 @@ package graph
// iterators can "inherit" from to get default iterator functionality. // iterators can "inherit" from to get default iterator functionality.
import ( import (
"fmt"
"strings" "strings"
"github.com/barakmich/glog" "github.com/barakmich/glog"
@ -104,170 +103,17 @@ type Iterator interface {
GetUid() int GetUid() int
} }
type FixedIterator interface {
Iterator
AddValue(TSVal)
}
type IteratorStats struct { type IteratorStats struct {
CheckCost int64 CheckCost int64
NextCost int64 NextCost int64
Size int64 Size int64
} }
// The Base iterator is the iterator other iterators inherit from to get some
// default functionality.
type BaseIterator struct {
Last TSVal
tags []string
fixedTags map[string]TSVal
nextable bool
uid int
}
// Called by subclases.
func BaseIteratorInit(it *BaseIterator) {
// Your basic iterator is nextable
it.nextable = true
it.uid = iterator_n
if glog.V(2) {
iterator_n++
}
}
func (it *BaseIterator) GetUid() int {
return it.uid
}
// Adds a tag to the iterator. Most iterators don't need to override.
func (it *BaseIterator) AddTag(tag string) {
if it.tags == nil {
it.tags = make([]string, 0)
}
it.tags = append(it.tags, tag)
}
func (it *BaseIterator) AddFixedTag(tag string, value TSVal) {
if it.fixedTags == nil {
it.fixedTags = make(map[string]TSVal)
}
it.fixedTags[tag] = value
}
// Returns the tags.
func (it *BaseIterator) Tags() []string {
return it.tags
}
func (it *BaseIterator) FixedTags() map[string]TSVal {
return it.fixedTags
}
func (it *BaseIterator) CopyTagsFrom(other_it 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 *BaseIterator) DebugString(indent int) string {
return fmt.Sprintf("%s(base)", strings.Repeat(" ", indent))
}
// Nothing in a base iterator.
func (it *BaseIterator) Check(v TSVal) bool {
return false
}
// Base iterators should never appear in a tree if they are, select against
// them.
func (it *BaseIterator) GetStats() *IteratorStats {
return &IteratorStats{100000, 100000, 100000}
}
// DEPRECATED
func (it *BaseIterator) GetResultTree() *ResultTree {
tree := NewResultTree(it.LastResult())
return tree
}
// Nothing in a base iterator.
func (it *BaseIterator) Next() (TSVal, bool) {
return nil, false
}
func (it *BaseIterator) NextResult() bool {
return false
}
// Returns the last result of an iterator.
func (it *BaseIterator) LastResult() TSVal {
return it.Last
}
// If you're empty and you know it, clap your hands.
func (it *BaseIterator) Size() (int64, bool) {
return 0, true
}
// No subiterators. Only those with subiterators need to do anything here.
func (it *BaseIterator) GetSubIterators() []Iterator {
return nil
}
// Accessor
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 (it *BaseIterator) TagResults(out_map *map[string]TSVal) {
for _, tag := range it.Tags() {
(*out_map)[tag] = it.LastResult()
}
for tag, value := range it.FixedTags() {
(*out_map)[tag] = value
}
}
// Nothing to clean up.
// func (it *BaseIterator) Close() {}
func (it *NullIterator) Close() {}
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,
// so it's important to give it a special iterator.
type NullIterator struct {
BaseIterator
}
// Fairly useless New function.
func NewNullIterator() *NullIterator {
return &NullIterator{}
}
func (it *NullIterator) Clone() Iterator { return NewNullIterator() }
// Name the null iterator.
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 (it *NullIterator) Optimize() (Iterator, bool) { return it, false }
// Print the null iterator.
func (it *NullIterator) DebugString(indent int) string {
return strings.Repeat(" ", indent) + "(null)"
}
// A null iterator costs nothing. Use it!
func (it *NullIterator) GetStats() *IteratorStats {
return &IteratorStats{0, 0, 0}
}
// Utility logging functions for when an iterator gets called Next upon, or Check upon, as // Utility logging functions for when an iterator gets called Next upon, or Check upon, as
// well as what they return. Highly useful for tracing the execution path of a query. // well as what they return. Highly useful for tracing the execution path of a query.
func CheckLogIn(it Iterator, val TSVal) { func CheckLogIn(it Iterator, val TSVal) {

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// Defines one of the base iterators, the All iterator. Which, logically // Defines one of the base iterators, the All iterator. Which, logically
// enough, represents all nodes or all links in the graph. // enough, represents all nodes or all links in the graph.
@ -25,19 +25,21 @@ package graph
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/google/cayley/graph"
) )
// An All iterator across a range of int64 values, from `max` to `min`. // An All iterator across a range of int64 values, from `max` to `min`.
type Int64AllIterator struct { type Int64 struct {
BaseIterator Base
max, min int64 max, min int64
at int64 at int64
} }
// Creates a new Int64AllIterator with the given range. // Creates a new Int64 with the given range.
func NewInt64AllIterator(min, max int64) *Int64AllIterator { func NewInt64(min, max int64) *Int64 {
var all Int64AllIterator var all Int64
BaseIteratorInit(&all.BaseIterator) BaseInit(&all.Base)
all.max = max all.max = max
all.min = min all.min = min
all.at = min all.at = min
@ -45,26 +47,26 @@ func NewInt64AllIterator(min, max int64) *Int64AllIterator {
} }
// Start back at the beginning // Start back at the beginning
func (it *Int64AllIterator) Reset() { func (it *Int64) Reset() {
it.at = it.min it.at = it.min
} }
func (it *Int64AllIterator) Close() {} func (it *Int64) Close() {}
func (it *Int64AllIterator) Clone() Iterator { func (it *Int64) Clone() graph.Iterator {
out := NewInt64AllIterator(it.min, it.max) out := NewInt64(it.min, it.max)
out.CopyTagsFrom(it) out.CopyTagsFrom(it)
return out return out
} }
// Prints the All iterator as just an "all". // Prints the All iterator as just an "all".
func (it *Int64AllIterator) DebugString(indent int) string { 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())
} }
// Next() on an Int64 all iterator is a simple incrementing counter. // Next() on an Int64 all iterator is a simple incrementing counter.
// Return the next integer, and mark it as the result. // Return the next integer, and mark it as the result.
func (it *Int64AllIterator) Next() (TSVal, bool) { func (it *Int64) Next() (graph.TSVal, bool) {
NextLogIn(it) NextLogIn(it)
if it.at == -1 { if it.at == -1 {
return NextLogOut(it, nil, false) return NextLogOut(it, nil, false)
@ -78,16 +80,16 @@ func (it *Int64AllIterator) Next() (TSVal, bool) {
return NextLogOut(it, val, true) return NextLogOut(it, val, true)
} }
// The number of elements in an Int64AllIterator is the size of the range. // The number of elements in an Int64 is the size of the range.
// The size is exact. // The size is exact.
func (it *Int64AllIterator) Size() (int64, bool) { func (it *Int64) Size() (int64, bool) {
Size := ((it.max - it.min) + 1) Size := ((it.max - it.min) + 1)
return Size, true return Size, true
} }
// Check() for an Int64AllIterator is merely seeing if the passed value is // Check() for an Int64 is merely seeing if the passed value is
// withing the range, assuming the value is an int64. // withing the range, assuming the value is an int64.
func (it *Int64AllIterator) Check(tsv TSVal) bool { func (it *Int64) Check(tsv graph.TSVal) bool {
CheckLogIn(it, tsv) CheckLogIn(it, tsv)
v := tsv.(int64) v := tsv.(int64)
if it.min <= v && v <= it.max { if it.min <= v && v <= it.max {
@ -99,16 +101,16 @@ func (it *Int64AllIterator) Check(tsv TSVal) bool {
// The type of this iterator is an "all". This is important, as it puts it in // The type of this iterator is an "all". This is important, as it puts it in
// the class of "all iterators. // the class of "all iterators.
func (it *Int64AllIterator) Type() string { return "all" } func (it *Int64) Type() string { return "all" }
// There's nothing to optimize about this little iterator. // There's nothing to optimize about this little iterator.
func (it *Int64AllIterator) Optimize() (Iterator, bool) { return it, false } func (it *Int64) Optimize() (graph.Iterator, bool) { return it, false }
// Stats for an Int64AllIterator are simple. Super cheap to do any operation, // Stats for an Int64 are simple. Super cheap to do any operation,
// and as big as the range. // and as big as the range.
func (it *Int64AllIterator) GetStats() *IteratorStats { func (it *Int64) GetStats() *graph.IteratorStats {
s, _ := it.Size() s, _ := it.Size()
return &IteratorStats{ return &graph.IteratorStats{
CheckCost: 1, CheckCost: 1,
NextCost: 1, NextCost: 1,
Size: s, Size: s,

View file

@ -13,34 +13,36 @@
// If it's on a Check() path, it merely Check()s every iterator, and returns the // If it's on a Check() path, it merely Check()s every iterator, and returns the
// logical AND of each result. // logical AND of each result.
package graph package iterator
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/google/cayley/graph"
) )
// The And iterator. Consists of a BaseIterator and a number of subiterators, the primary of which will // The And iterator. Consists of a Base and a number of subiterators, the primary of which will
// be Next()ed if next is called. // be Next()ed if next is called.
type AndIterator struct { type And struct {
BaseIterator Base
internalIterators []Iterator internalIterators []graph.Iterator
itCount int itCount int
primaryIt Iterator primaryIt graph.Iterator
checkList []Iterator checkList []graph.Iterator
} }
// Creates a new And iterator. // Creates a new And iterator.
func NewAndIterator() *AndIterator { func NewAnd() *And {
var and AndIterator var and And
BaseIteratorInit(&and.BaseIterator) BaseInit(&and.Base)
and.internalIterators = make([]Iterator, 0, 20) and.internalIterators = make([]graph.Iterator, 0, 20)
and.checkList = nil and.checkList = nil
return &and return &and
} }
// Reset all internal iterators // Reset all internal iterators
func (it *AndIterator) Reset() { func (it *And) Reset() {
it.primaryIt.Reset() it.primaryIt.Reset()
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
sub.Reset() sub.Reset()
@ -48,8 +50,8 @@ func (it *AndIterator) Reset() {
it.checkList = nil it.checkList = nil
} }
func (it *AndIterator) Clone() Iterator { func (it *And) Clone() graph.Iterator {
and := NewAndIterator() and := NewAnd()
and.AddSubIterator(it.primaryIt.Clone()) and.AddSubIterator(it.primaryIt.Clone())
and.CopyTagsFrom(it) and.CopyTagsFrom(it)
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
@ -62,17 +64,17 @@ func (it *AndIterator) Clone() Iterator {
} }
// Returns a slice of the subiterators, in order (primary iterator first). // Returns a slice of the subiterators, in order (primary iterator first).
func (it *AndIterator) GetSubIterators() []Iterator { func (it *And) GetSubIterators() []graph.Iterator {
iters := make([]Iterator, len(it.internalIterators)+1) iters := make([]graph.Iterator, len(it.internalIterators)+1)
iters[0] = it.primaryIt iters[0] = it.primaryIt
copy(iters[1:], it.internalIterators) copy(iters[1:], it.internalIterators)
return iters return iters
} }
// Overrides BaseIterator TagResults, as it needs to add it's own results and // Overrides Base TagResults, as it needs to add it's own results and
// recurse down it's subiterators. // recurse down it's subiterators.
func (it *AndIterator) TagResults(out *map[string]TSVal) { func (it *And) TagResults(out *map[string]graph.TSVal) {
it.BaseIterator.TagResults(out) it.Base.TagResults(out)
if it.primaryIt != nil { if it.primaryIt != nil {
it.primaryIt.TagResults(out) it.primaryIt.TagResults(out)
} }
@ -82,8 +84,8 @@ func (it *AndIterator) TagResults(out *map[string]TSVal) {
} }
// DEPRECATED Returns the ResultTree for this iterator, recurses to it's subiterators. // DEPRECATED Returns the ResultTree for this iterator, recurses to it's subiterators.
func (it *AndIterator) GetResultTree() *ResultTree { func (it *And) GetResultTree() *graph.ResultTree {
tree := NewResultTree(it.LastResult()) tree := graph.NewResultTree(it.LastResult())
tree.AddSubtree(it.primaryIt.GetResultTree()) tree.AddSubtree(it.primaryIt.GetResultTree())
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
tree.AddSubtree(sub.GetResultTree()) tree.AddSubtree(sub.GetResultTree())
@ -92,7 +94,7 @@ func (it *AndIterator) GetResultTree() *ResultTree {
} }
// Prints information about this iterator. // Prints information about this iterator.
func (it *AndIterator) DebugString(indent int) string { func (it *And) DebugString(indent int) string {
var total string var total string
for i, sub := range it.internalIterators { for i, sub := range it.internalIterators {
total += strings.Repeat(" ", indent+2) total += strings.Repeat(" ", indent+2)
@ -122,7 +124,7 @@ func (it *AndIterator) DebugString(indent int) string {
// important. Calling Optimize() is the way to change the order based on // important. Calling Optimize() is the way to change the order based on
// subiterator statistics. Without Optimize(), the order added is the order // subiterator statistics. Without Optimize(), the order added is the order
// used. // used.
func (it *AndIterator) AddSubIterator(sub Iterator) { func (it *And) AddSubIterator(sub graph.Iterator) {
if it.itCount > 0 { if it.itCount > 0 {
it.internalIterators = append(it.internalIterators, sub) it.internalIterators = append(it.internalIterators, sub)
it.itCount++ it.itCount++
@ -136,9 +138,9 @@ func (it *AndIterator) AddSubIterator(sub Iterator) {
// intersection of its subiterators, it must choose one subiterator to produce a // intersection of its subiterators, it must choose one subiterator to produce a
// candidate, and check this value against the subiterators. A productive choice // candidate, and check this value against the subiterators. A productive choice
// of primary iterator is therefore very important. // of primary iterator is therefore very important.
func (it *AndIterator) Next() (TSVal, bool) { func (it *And) Next() (graph.TSVal, bool) {
NextLogIn(it) NextLogIn(it)
var curr TSVal var curr graph.TSVal
var exists bool var exists bool
for { for {
curr, exists = it.primaryIt.Next() curr, exists = it.primaryIt.Next()
@ -150,11 +152,11 @@ func (it *AndIterator) Next() (TSVal, bool) {
return NextLogOut(it, curr, true) return NextLogOut(it, curr, true)
} }
} }
panic("Somehow broke out of Next() loop in AndIterator") panic("Somehow broke out of Next() loop in And")
} }
// Checks a value against the non-primary iterators, in order. // Checks a value against the non-primary iterators, in order.
func (it *AndIterator) checkSubIts(val TSVal) bool { func (it *And) checkSubIts(val graph.TSVal) bool {
var subIsGood = true var subIsGood = true
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
subIsGood = sub.Check(val) subIsGood = sub.Check(val)
@ -165,7 +167,7 @@ func (it *AndIterator) checkSubIts(val TSVal) bool {
return subIsGood return subIsGood
} }
func (it *AndIterator) checkCheckList(val TSVal) bool { func (it *And) checkCheckList(val graph.TSVal) bool {
ok := true ok := true
for _, c := range it.checkList { for _, c := range it.checkList {
ok = c.Check(val) ok = c.Check(val)
@ -180,7 +182,7 @@ func (it *AndIterator) checkCheckList(val TSVal) bool {
} }
// Check a value against the entire iterator, in order. // Check a value against the entire iterator, in order.
func (it *AndIterator) Check(val TSVal) bool { func (it *And) Check(val graph.TSVal) bool {
CheckLogIn(it, val) CheckLogIn(it, val)
if it.checkList != nil { if it.checkList != nil {
return it.checkCheckList(val) return it.checkCheckList(val)
@ -201,7 +203,7 @@ func (it *AndIterator) Check(val TSVal) bool {
// with an intersection, we know that the largest we can be is the size of the // with an intersection, we know that the largest we can be is the size of the
// smallest iterator. This is the heuristic we shall follow. Better heuristics // smallest iterator. This is the heuristic we shall follow. Better heuristics
// welcome. // welcome.
func (it *AndIterator) Size() (int64, bool) { func (it *And) Size() (int64, bool) {
val, b := it.primaryIt.Size() val, b := it.primaryIt.Size()
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
newval, newb := sub.Size() newval, newb := sub.Size()
@ -216,7 +218,7 @@ func (it *AndIterator) Size() (int64, bool) {
// An And has no NextResult of its own -- that is, there are no other values // An And has no NextResult of its own -- that is, there are no other values
// which satisfy our previous result that are not the result itself. Our // which satisfy our previous result that are not the result itself. Our
// subiterators might, however, so just pass the call recursively. // subiterators might, however, so just pass the call recursively.
func (it *AndIterator) NextResult() bool { func (it *And) NextResult() bool {
if it.primaryIt.NextResult() { if it.primaryIt.NextResult() {
return true return true
} }
@ -229,12 +231,12 @@ func (it *AndIterator) NextResult() bool {
} }
// Perform and-specific cleanup, of which there currently is none. // Perform and-specific cleanup, of which there currently is none.
func (it *AndIterator) cleanUp() {} func (it *And) cleanUp() {}
// Close this iterator, and, by extension, close the subiterators. // Close this iterator, and, by extension, close the subiterators.
// Close should be idempotent, and it follows that if it's subiterators // Close should be idempotent, and it follows that if it's subiterators
// follow this contract, the And follows the contract. // follow this contract, the And follows the contract.
func (it *AndIterator) Close() { func (it *And) Close() {
it.cleanUp() it.cleanUp()
it.primaryIt.Close() it.primaryIt.Close()
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
@ -243,4 +245,4 @@ func (it *AndIterator) Close() {
} }
// Register this as an "and" iterator. // Register this as an "and" iterator.
func (it *AndIterator) Type() string { return "and" } func (it *And) Type() string { return "and" }

View file

@ -12,14 +12,16 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
import ( import (
"sort" "sort"
"github.com/google/cayley/graph"
) )
// Perhaps the most tricky file in this entire module. Really a method on the // Perhaps the most tricky file in this entire module. Really a method on the
// AndIterator, but important enough to deserve its own file. // And, but important enough to deserve its own file.
// //
// Calling Optimize() on an And iterator, like any iterator, requires that we // Calling Optimize() on an And iterator, like any iterator, requires that we
// preserve the underlying meaning. However, the And has many choices, namely, // preserve the underlying meaning. However, the And has many choices, namely,
@ -35,9 +37,9 @@ import (
// //
// In short, tread lightly. // In short, tread lightly.
// Optimizes the AndIterator, by picking the most efficient way to Next() and // Optimizes the And, by picking the most efficient way to Next() and
// Check() its subiterators. For SQL fans, this is equivalent to JOIN. // Check() its subiterators. For SQL fans, this is equivalent to JOIN.
func (it *AndIterator) Optimize() (Iterator, bool) { func (it *And) Optimize() (graph.Iterator, bool) {
// First, let's get the slice of iterators, in order (first one is Next()ed, // First, let's get the slice of iterators, in order (first one is Next()ed,
// the rest are Check()ed) // the rest are Check()ed)
old := it.GetSubIterators() old := it.GetSubIterators()
@ -72,7 +74,7 @@ func (it *AndIterator) Optimize() (Iterator, bool) {
// The easiest thing to do at this point is merely to create a new And iterator // The easiest thing to do at this point is merely to create a new And iterator
// and replace ourselves with our (reordered, optimized) clone. // and replace ourselves with our (reordered, optimized) clone.
newAnd := NewAndIterator() newAnd := NewAnd()
// Add the subiterators in order. // Add the subiterators in order.
for _, sub := range its { for _, sub := range its {
@ -93,7 +95,7 @@ func (it *AndIterator) Optimize() (Iterator, bool) {
// Closes a list of iterators, except the one passed in `except`. Closes all // Closes a list of iterators, except the one passed in `except`. Closes all
// of the iterators in the list if `except` is nil. // of the iterators in the list if `except` is nil.
func closeIteratorList(its []Iterator, except Iterator) { func closeIteratorList(its []graph.Iterator, except graph.Iterator) {
for _, it := range its { for _, it := range its {
if it != except { if it != except {
it.Close() it.Close()
@ -102,11 +104,11 @@ func closeIteratorList(its []Iterator, except Iterator) {
} }
// Find if there is a single subiterator which is a valid replacement for this // Find if there is a single subiterator which is a valid replacement for this
// AndIterator. // And.
func (_ *AndIterator) optimizeReplacement(its []Iterator) Iterator { func (_ *And) optimizeReplacement(its []graph.Iterator) graph.Iterator {
// If we were created with no SubIterators, we're as good as Null. // If we were created with no SubIterators, we're as good as Null.
if len(its) == 0 { if len(its) == 0 {
return &NullIterator{} return &Null{}
} }
if len(its) == 1 { if len(its) == 1 {
// When there's only one iterator, there's only one choice. // When there's only one iterator, there's only one choice.
@ -116,7 +118,7 @@ func (_ *AndIterator) optimizeReplacement(its []Iterator) Iterator {
// there's no point in continuing the branch, we will have no results // there's no point in continuing the branch, we will have no results
// and we are null as well. // and we are null as well.
if hasAnyNullIterators(its) { if hasAnyNullIterators(its) {
return &NullIterator{} return &Null{}
} }
// If we have one useful iterator, use that. // If we have one useful iterator, use that.
@ -129,12 +131,12 @@ func (_ *AndIterator) optimizeReplacement(its []Iterator) Iterator {
// optimizeOrder(l) takes a list and returns a list, containing the same contents // optimizeOrder(l) takes a list and returns a list, containing the same contents
// but with a new ordering, however it wishes. // but with a new ordering, however it wishes.
func optimizeOrder(its []Iterator) []Iterator { func optimizeOrder(its []graph.Iterator) []graph.Iterator {
var ( var (
// bad contains iterators that can't be (efficiently) nexted, such as // bad contains iterators that can't be (efficiently) nexted, such as
// "optional" or "not". Separate them out and tack them on at the end. // "optional" or "not". Separate them out and tack them on at the end.
out, bad []Iterator out, bad []graph.Iterator
best Iterator best graph.Iterator
bestCost = int64(1 << 62) bestCost = int64(1 << 62)
) )
@ -187,7 +189,7 @@ func optimizeOrder(its []Iterator) []Iterator {
return append(out, bad...) return append(out, bad...)
} }
type byCost []Iterator type byCost []graph.Iterator
func (c byCost) Len() int { return len(c) } func (c byCost) Len() int { return len(c) }
func (c byCost) Less(i, j int) bool { return c[i].GetStats().CheckCost < c[j].GetStats().CheckCost } func (c byCost) Less(i, j int) bool { return c[i].GetStats().CheckCost < c[j].GetStats().CheckCost }
@ -195,7 +197,7 @@ func (c byCost) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
// optimizeCheck(l) creates an alternate check list, containing the same contents // optimizeCheck(l) creates an alternate check list, containing the same contents
// but with a new ordering, however it wishes. // but with a new ordering, however it wishes.
func (it *AndIterator) optimizeCheck() { func (it *And) optimizeCheck() {
// GetSubIterators allocates, so this is currently safe. // GetSubIterators allocates, so this is currently safe.
// TODO(kortschak) Reuse it.checkList if possible. // TODO(kortschak) Reuse it.checkList if possible.
// This involves providing GetSubIterators with a slice to fill. // This involves providing GetSubIterators with a slice to fill.
@ -208,7 +210,7 @@ func (it *AndIterator) optimizeCheck() {
// result tags from the iterators that, while still valid and would hold // result tags from the iterators that, while still valid and would hold
// the same values as this and, are not going to stay. // the same values as this and, are not going to stay.
// getSubTags() returns a map of the tags for all the subiterators. // getSubTags() returns a map of the tags for all the subiterators.
func (it *AndIterator) getSubTags() map[string]struct{} { func (it *And) getSubTags() map[string]struct{} {
tags := make(map[string]struct{}) tags := make(map[string]struct{})
for _, sub := range it.GetSubIterators() { for _, sub := range it.GetSubIterators() {
for _, tag := range sub.Tags() { for _, tag := range sub.Tags() {
@ -223,7 +225,7 @@ func (it *AndIterator) getSubTags() map[string]struct{} {
// moveTagsTo() gets the tags for all of the src's subiterators and the // moveTagsTo() gets the tags for all of the src's subiterators and the
// src itself, and moves them to dst. // src itself, and moves them to dst.
func moveTagsTo(dst Iterator, src *AndIterator) { func moveTagsTo(dst graph.Iterator, src *And) {
tags := src.getSubTags() tags := src.getSubTags()
for _, tag := range dst.Tags() { for _, tag := range dst.Tags() {
if _, ok := tags[tag]; ok { if _, ok := tags[tag]; ok {
@ -239,8 +241,8 @@ func moveTagsTo(dst Iterator, src *AndIterator) {
// of them. It returns two lists -- the first contains the same list as l, where // of them. It returns two lists -- the first contains the same list as l, where
// any replacements are made by Optimize() and the second contains the originals // any replacements are made by Optimize() and the second contains the originals
// which were replaced. // which were replaced.
func optimizeSubIterators(its []Iterator) []Iterator { func optimizeSubIterators(its []graph.Iterator) []graph.Iterator {
var optIts []Iterator var optIts []graph.Iterator
for _, it := range its { for _, it := range its {
o, changed := it.Optimize() o, changed := it.Optimize()
if changed { if changed {
@ -253,7 +255,7 @@ func optimizeSubIterators(its []Iterator) []Iterator {
} }
// Check a list of iterators for any Null iterators. // Check a list of iterators for any Null iterators.
func hasAnyNullIterators(its []Iterator) bool { func hasAnyNullIterators(its []graph.Iterator) bool {
for _, it := range its { for _, it := range its {
if it.Type() == "null" { if it.Type() == "null" {
return true return true
@ -266,9 +268,9 @@ func hasAnyNullIterators(its []Iterator) bool {
// nothing, and "all" which returns everything. Particularly, we want // nothing, and "all" which returns everything. Particularly, we want
// to see if we're intersecting with a bunch of "all" iterators, and, // to see if we're intersecting with a bunch of "all" iterators, and,
// if we are, then we have only one useful iterator. // if we are, then we have only one useful iterator.
func hasOneUsefulIterator(its []Iterator) Iterator { func hasOneUsefulIterator(its []graph.Iterator) graph.Iterator {
usefulCount := 0 usefulCount := 0
var usefulIt Iterator var usefulIt graph.Iterator
for _, it := range its { for _, it := range its {
switch it.Type() { switch it.Type() {
case "null", "all": case "null", "all":
@ -293,7 +295,7 @@ func hasOneUsefulIterator(its []Iterator) Iterator {
// and.GetStats() lives here in and-iterator-optimize.go because it may // and.GetStats() lives here in and-iterator-optimize.go because it may
// in the future return different statistics based on how it is optimized. // in the future return different statistics based on how it is optimized.
// For now, however, it's pretty static. // For now, however, it's pretty static.
func (it *AndIterator) GetStats() *IteratorStats { func (it *And) GetStats() *graph.IteratorStats {
primaryStats := it.primaryIt.GetStats() primaryStats := it.primaryIt.GetStats()
CheckCost := primaryStats.CheckCost CheckCost := primaryStats.CheckCost
NextCost := primaryStats.NextCost NextCost := primaryStats.NextCost
@ -306,7 +308,7 @@ func (it *AndIterator) GetStats() *IteratorStats {
Size = stats.Size Size = stats.Size
} }
} }
return &IteratorStats{ return &graph.IteratorStats{
CheckCost: CheckCost, CheckCost: CheckCost,
NextCost: NextCost, NextCost: NextCost,
Size: Size, Size: Size,

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// Tests relating to methods in and-iterator-optimize. Many are pretty simplistic, but // Tests relating to methods in and-iterator-optimize. Many are pretty simplistic, but
// nonetheless cover a lot of basic cases. // nonetheless cover a lot of basic cases.
@ -24,10 +24,10 @@ import (
) )
func TestIteratorPromotion(t *testing.T) { func TestIteratorPromotion(t *testing.T) {
all := NewInt64AllIterator(1, 3) all := NewInt64(1, 3)
fixed := newFixedIterator() fixed := newFixed()
fixed.AddValue(3) fixed.AddValue(3)
a := NewAndIterator() a := NewAnd()
a.AddSubIterator(all) a.AddSubIterator(all)
a.AddSubIterator(fixed) a.AddSubIterator(fixed)
all.AddTag("a") all.AddTag("a")
@ -49,9 +49,9 @@ func TestIteratorPromotion(t *testing.T) {
} }
func TestNullIteratorAnd(t *testing.T) { func TestNullIteratorAnd(t *testing.T) {
all := NewInt64AllIterator(1, 3) all := NewInt64(1, 3)
null := NewNullIterator() null := NewNull()
a := NewAndIterator() a := NewAnd()
a.AddSubIterator(all) a.AddSubIterator(all)
a.AddSubIterator(null) a.AddSubIterator(null)
newIt, changed := a.Optimize() newIt, changed := a.Optimize()
@ -64,11 +64,11 @@ func TestNullIteratorAnd(t *testing.T) {
} }
func TestReorderWithTag(t *testing.T) { func TestReorderWithTag(t *testing.T) {
all := NewInt64AllIterator(100, 300) all := NewInt64(100, 300)
all.AddTag("good") all.AddTag("good")
all2 := NewInt64AllIterator(1, 30000) all2 := NewInt64(1, 30000)
all2.AddTag("slow") all2.AddTag("slow")
a := NewAndIterator() a := NewAnd()
// Make all2 the default iterator // Make all2 the default iterator
a.AddSubIterator(all2) a.AddSubIterator(all2)
a.AddSubIterator(all) a.AddSubIterator(all)
@ -90,11 +90,11 @@ func TestReorderWithTag(t *testing.T) {
} }
func TestAndStatistics(t *testing.T) { func TestAndStatistics(t *testing.T) {
all := NewInt64AllIterator(100, 300) all := NewInt64(100, 300)
all.AddTag("good") all.AddTag("good")
all2 := NewInt64AllIterator(1, 30000) all2 := NewInt64(1, 30000)
all2.AddTag("slow") all2.AddTag("slow")
a := NewAndIterator() a := NewAnd()
// Make all2 the default iterator // Make all2 the default iterator
a.AddSubIterator(all2) a.AddSubIterator(all2)
a.AddSubIterator(all) a.AddSubIterator(all)

View file

@ -12,18 +12,20 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
import ( import (
"testing" "testing"
"github.com/google/cayley/graph"
) )
// Make sure that tags work on the And. // Make sure that tags work on the And.
func TestTag(t *testing.T) { func TestTag(t *testing.T) {
fix1 := newFixedIterator() fix1 := newFixed()
fix1.AddValue(234) fix1.AddValue(234)
fix1.AddTag("foo") fix1.AddTag("foo")
and := NewAndIterator() and := NewAnd()
and.AddSubIterator(fix1) and.AddSubIterator(fix1)
and.AddTag("bar") and.AddTag("bar")
out := fix1.Tags() out := fix1.Tags()
@ -41,7 +43,7 @@ func TestTag(t *testing.T) {
if val != 234 { if val != 234 {
t.Errorf("Unexpected value") t.Errorf("Unexpected value")
} }
tags := make(map[string]TSVal) tags := make(map[string]graph.TSVal)
and.TagResults(&tags) and.TagResults(&tags)
if tags["bar"] != 234 { if tags["bar"] != 234 {
t.Errorf("no bar tag") t.Errorf("no bar tag")
@ -53,16 +55,16 @@ func TestTag(t *testing.T) {
// Do a simple itersection of fixed values. // Do a simple itersection of fixed values.
func TestAndAndFixedIterators(t *testing.T) { func TestAndAndFixedIterators(t *testing.T) {
fix1 := newFixedIterator() fix1 := newFixed()
fix1.AddValue(1) fix1.AddValue(1)
fix1.AddValue(2) fix1.AddValue(2)
fix1.AddValue(3) fix1.AddValue(3)
fix1.AddValue(4) fix1.AddValue(4)
fix2 := newFixedIterator() fix2 := newFixed()
fix2.AddValue(3) fix2.AddValue(3)
fix2.AddValue(4) fix2.AddValue(4)
fix2.AddValue(5) fix2.AddValue(5)
and := NewAndIterator() and := NewAnd()
and.AddSubIterator(fix1) and.AddSubIterator(fix1)
and.AddSubIterator(fix2) and.AddSubIterator(fix2)
// Should be as big as smallest subiterator // Should be as big as smallest subiterator
@ -94,16 +96,16 @@ func TestAndAndFixedIterators(t *testing.T) {
// If there's no intersection, the size should still report the same, // If there's no intersection, the size should still report the same,
// but there should be nothing to Next() // but there should be nothing to Next()
func TestNonOverlappingFixedIterators(t *testing.T) { func TestNonOverlappingFixedIterators(t *testing.T) {
fix1 := newFixedIterator() fix1 := newFixed()
fix1.AddValue(1) fix1.AddValue(1)
fix1.AddValue(2) fix1.AddValue(2)
fix1.AddValue(3) fix1.AddValue(3)
fix1.AddValue(4) fix1.AddValue(4)
fix2 := newFixedIterator() fix2 := newFixed()
fix2.AddValue(5) fix2.AddValue(5)
fix2.AddValue(6) fix2.AddValue(6)
fix2.AddValue(7) fix2.AddValue(7)
and := NewAndIterator() and := NewAnd()
and.AddSubIterator(fix1) and.AddSubIterator(fix1)
and.AddSubIterator(fix2) and.AddSubIterator(fix2)
// Should be as big as smallest subiterator // Should be as big as smallest subiterator
@ -123,9 +125,9 @@ func TestNonOverlappingFixedIterators(t *testing.T) {
} }
func TestAllIterators(t *testing.T) { func TestAllIterators(t *testing.T) {
all1 := NewInt64AllIterator(1, 5) all1 := NewInt64(1, 5)
all2 := NewInt64AllIterator(4, 10) all2 := NewInt64(4, 10)
and := NewAndIterator() and := NewAnd()
and.AddSubIterator(all2) and.AddSubIterator(all2)
and.AddSubIterator(all1) and.AddSubIterator(all1)

View file

@ -12,33 +12,35 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// Defines one of the base iterators, the Fixed iterator. A fixed iterator is quite simple; it // Defines one of the base iterators, the Fixed iterator. A fixed iterator is quite simple; it
// contains an explicit fixed array of values. // contains an explicit fixed array of values.
// //
// A fixed iterator requires an Equality function to be passed to it, by reason that TSVal, the // A fixed iterator requires an Equality function to be passed to it, by reason that graph.TSVal, the
// opaque Triple store value, may not answer to ==. // opaque Triple store value, may not answer to ==.
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/google/cayley/graph"
) )
// A Fixed iterator consists of it's values, an index (where it is in the process of Next()ing) and // A Fixed iterator consists of it's values, an index (where it is in the process of Next()ing) and
// an equality function. // an equality function.
type FixedIterator struct { type Fixed struct {
BaseIterator Base
values []TSVal values []graph.TSVal
lastIndex int lastIndex int
cmp Equality cmp Equality
} }
// Define the signature of an equality function. // Define the signature of an equality function.
type Equality func(a, b TSVal) bool type Equality func(a, b graph.TSVal) bool
// Define an equality function of purely ==, which works for native types. // Define an equality function of purely ==, which works for native types.
func BasicEquality(a, b TSVal) bool { func BasicEquality(a, b graph.TSVal) bool {
if a == b { if a == b {
return true return true
} }
@ -46,27 +48,27 @@ func BasicEquality(a, b TSVal) bool {
} }
// Creates a new Fixed iterator based around == equality. // Creates a new Fixed iterator based around == equality.
func newFixedIterator() *FixedIterator { func newFixed() *Fixed {
return NewFixedIteratorWithCompare(BasicEquality) return NewFixedIteratorWithCompare(BasicEquality)
} }
// Creates a new Fixed iterator with a custom comparitor. // Creates a new Fixed iterator with a custom comparitor.
func NewFixedIteratorWithCompare(compareFn Equality) *FixedIterator { func NewFixedIteratorWithCompare(compareFn Equality) *Fixed {
var it FixedIterator var it Fixed
BaseIteratorInit(&it.BaseIterator) BaseInit(&it.Base)
it.values = make([]TSVal, 0, 20) it.values = make([]graph.TSVal, 0, 20)
it.lastIndex = 0 it.lastIndex = 0
it.cmp = compareFn it.cmp = compareFn
return &it return &it
} }
func (it *FixedIterator) Reset() { func (it *Fixed) Reset() {
it.lastIndex = 0 it.lastIndex = 0
} }
func (it *FixedIterator) Close() {} func (it *Fixed) Close() {}
func (it *FixedIterator) Clone() Iterator { func (it *Fixed) Clone() graph.Iterator {
out := NewFixedIteratorWithCompare(it.cmp) out := NewFixedIteratorWithCompare(it.cmp)
for _, val := range it.values { for _, val := range it.values {
out.AddValue(val) out.AddValue(val)
@ -77,12 +79,12 @@ func (it *FixedIterator) Clone() Iterator {
// Add a value to the iterator. The array now contains this value. // Add a value to the iterator. The array now contains this value.
// TODO(barakmich): This ought to be a set someday, disallowing repeated values. // TODO(barakmich): This ought to be a set someday, disallowing repeated values.
func (it *FixedIterator) AddValue(v TSVal) { func (it *Fixed) AddValue(v graph.TSVal) {
it.values = append(it.values, v) it.values = append(it.values, v)
} }
// Print some information about the iterator. // Print some information about the iterator.
func (it *FixedIterator) DebugString(indent int) string { func (it *Fixed) DebugString(indent int) string {
value := "" value := ""
if len(it.values) > 0 { if len(it.values) > 0 {
value = fmt.Sprint(it.values[0]) value = fmt.Sprint(it.values[0])
@ -97,12 +99,12 @@ func (it *FixedIterator) DebugString(indent int) string {
} }
// Register this iterator as a Fixed iterator. // Register this iterator as a Fixed iterator.
func (it *FixedIterator) Type() string { func (it *Fixed) Type() string {
return "fixed" return "fixed"
} }
// Check if the passed value is equal to one of the values stored in the iterator. // Check if the passed value is equal to one of the values stored in the iterator.
func (it *FixedIterator) Check(v TSVal) bool { func (it *Fixed) Check(v graph.TSVal) bool {
// Could be optimized by keeping it sorted or using a better datastructure. // Could be optimized by keeping it sorted or using a better datastructure.
// However, for fixed iterators, which are by definition kind of tiny, this // However, for fixed iterators, which are by definition kind of tiny, this
// isn't a big issue. // isn't a big issue.
@ -117,7 +119,7 @@ func (it *FixedIterator) Check(v TSVal) bool {
} }
// Return the next stored value from the iterator. // Return the next stored value from the iterator.
func (it *FixedIterator) Next() (TSVal, bool) { func (it *Fixed) Next() (graph.TSVal, bool) {
NextLogIn(it) NextLogIn(it)
if it.lastIndex == len(it.values) { if it.lastIndex == len(it.values) {
return NextLogOut(it, nil, false) return NextLogOut(it, nil, false)
@ -131,24 +133,23 @@ func (it *FixedIterator) Next() (TSVal, bool) {
// Optimize() for a Fixed iterator is simple. Returns a Null iterator if it's empty // Optimize() for a Fixed iterator is simple. Returns a Null iterator if it's empty
// (so that other iterators upstream can treat this as null) or there is no // (so that other iterators upstream can treat this as null) or there is no
// optimization. // optimization.
func (it *FixedIterator) Optimize() (Iterator, bool) { func (it *Fixed) Optimize() (graph.Iterator, bool) {
if len(it.values) == 1 && it.values[0] == nil { if len(it.values) == 1 && it.values[0] == nil {
return &NullIterator{}, true return &Null{}, true
} }
return it, false return it, false
} }
// Size is the number of values stored. // Size is the number of values stored.
func (it *FixedIterator) Size() (int64, bool) { func (it *Fixed) Size() (int64, bool) {
return int64(len(it.values)), true return int64(len(it.values)), true
} }
// As we right now have to scan the entire list, Next and Check are linear with the // As we right now have to scan the entire list, Next and Check are linear with the
// size. However, a better data structure could remove these limits. // size. However, a better data structure could remove these limits.
func (it *FixedIterator) GetStats() *IteratorStats { func (it *Fixed) GetStats() *graph.IteratorStats {
return &IteratorStats{ return &graph.IteratorStats{
CheckCost: int64(len(it.values)), CheckCost: int64(len(it.values)),
NextCost: int64(len(it.values)), NextCost: int64(len(it.values)),
Size: int64(len(it.values)), Size: int64(len(it.values)),

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// Defines one of the base iterators, the HasA iterator. The HasA takes a // Defines one of the base iterators, the HasA iterator. The HasA takes a
// subiterator of links, and acts as an iterator of nodes in the given // subiterator of links, and acts as an iterator of nodes in the given
@ -38,24 +38,26 @@ import (
"strings" "strings"
"github.com/barakmich/glog" "github.com/barakmich/glog"
"github.com/google/cayley/graph"
) )
// A HasaIterator consists of a reference back to the TripleStore that it references, // A HasA consists of a reference back to the graph.TripleStore that it references,
// a primary subiterator, a direction in which the triples for that subiterator point, // a primary subiterator, a direction in which the triples for that subiterator point,
// and a temporary holder for the iterator generated on Check(). // and a temporary holder for the iterator generated on Check().
type HasaIterator struct { type HasA struct {
BaseIterator Base
ts TripleStore ts graph.TripleStore
primaryIt Iterator primaryIt graph.Iterator
dir Direction dir graph.Direction
resultIt Iterator resultIt graph.Iterator
} }
// Construct a new HasA iterator, given the triple subiterator, and the triple // Construct a new HasA iterator, given the triple subiterator, and the triple
// direction for which it stands. // direction for which it stands.
func NewHasaIterator(ts TripleStore, subIt Iterator, d Direction) *HasaIterator { func NewHasA(ts graph.TripleStore, subIt graph.Iterator, d graph.Direction) *HasA {
var hasa HasaIterator var hasa HasA
BaseIteratorInit(&hasa.BaseIterator) BaseInit(&hasa.Base)
hasa.ts = ts hasa.ts = ts
hasa.primaryIt = subIt hasa.primaryIt = subIt
hasa.dir = d hasa.dir = d
@ -63,29 +65,29 @@ func NewHasaIterator(ts TripleStore, subIt Iterator, d Direction) *HasaIterator
} }
// Return our sole subiterator. // Return our sole subiterator.
func (it *HasaIterator) GetSubIterators() []Iterator { func (it *HasA) GetSubIterators() []graph.Iterator {
return []Iterator{it.primaryIt} return []graph.Iterator{it.primaryIt}
} }
func (it *HasaIterator) Reset() { func (it *HasA) Reset() {
it.primaryIt.Reset() it.primaryIt.Reset()
if it.resultIt != nil { if it.resultIt != nil {
it.resultIt.Close() it.resultIt.Close()
} }
} }
func (it *HasaIterator) Clone() Iterator { func (it *HasA) Clone() graph.Iterator {
out := NewHasaIterator(it.ts, it.primaryIt.Clone(), it.dir) out := NewHasA(it.ts, it.primaryIt.Clone(), it.dir)
out.CopyTagsFrom(it) out.CopyTagsFrom(it)
return out return out
} }
// Direction accessor. // Direction accessor.
func (it *HasaIterator) Direction() Direction { return it.dir } func (it *HasA) Direction() graph.Direction { return it.dir }
// Pass the Optimize() call along to the subiterator. If it becomes Null, // Pass the Optimize() call along to the subiterator. If it becomes Null,
// then the HasA becomes Null (there are no triples that have any directions). // then the HasA becomes Null (there are no triples that have any directions).
func (it *HasaIterator) Optimize() (Iterator, bool) { func (it *HasA) Optimize() (graph.Iterator, bool) {
newPrimary, changed := it.primaryIt.Optimize() newPrimary, changed := it.primaryIt.Optimize()
if changed { if changed {
it.primaryIt = newPrimary it.primaryIt = newPrimary
@ -97,20 +99,20 @@ func (it *HasaIterator) Optimize() (Iterator, bool) {
} }
// Pass the TagResults down the chain. // Pass the TagResults down the chain.
func (it *HasaIterator) TagResults(out *map[string]TSVal) { func (it *HasA) TagResults(out *map[string]graph.TSVal) {
it.BaseIterator.TagResults(out) it.Base.TagResults(out)
it.primaryIt.TagResults(out) it.primaryIt.TagResults(out)
} }
// DEPRECATED Return results in a ResultTree. // DEPRECATED Return results in a ResultTree.
func (it *HasaIterator) GetResultTree() *ResultTree { func (it *HasA) GetResultTree() *graph.ResultTree {
tree := NewResultTree(it.LastResult()) tree := graph.NewResultTree(it.LastResult())
tree.AddSubtree(it.primaryIt.GetResultTree()) tree.AddSubtree(it.primaryIt.GetResultTree())
return tree return tree
} }
// Print some information about this iterator. // Print some information about this iterator.
func (it *HasaIterator) DebugString(indent int) string { func (it *HasA) DebugString(indent int) string {
var tags string var tags string
for _, k := range it.Tags() { for _, k := range it.Tags() {
tags += fmt.Sprintf("%s;", k) tags += fmt.Sprintf("%s;", k)
@ -121,7 +123,7 @@ func (it *HasaIterator) DebugString(indent int) string {
// Check a value against our internal iterator. In order to do this, we must first open a new // Check a value against our internal iterator. In order to do this, we must first open a new
// iterator of "triples that have `val` in our direction", given to us by the triple store, // iterator of "triples that have `val` in our direction", given to us by the triple store,
// and then Next() values out of that iterator and Check() them against our subiterator. // and then Next() values out of that iterator and Check() them against our subiterator.
func (it *HasaIterator) Check(val TSVal) bool { func (it *HasA) Check(val graph.TSVal) bool {
CheckLogIn(it, val) CheckLogIn(it, val)
if glog.V(4) { if glog.V(4) {
glog.V(4).Infoln("Id is", it.ts.GetNameFor(val)) glog.V(4).Infoln("Id is", it.ts.GetNameFor(val))
@ -137,14 +139,14 @@ func (it *HasaIterator) Check(val TSVal) bool {
// GetCheckResult() is shared code between Check() and GetNextResult() -- calls next on the // GetCheckResult() is shared code between Check() and GetNextResult() -- calls next on the
// result iterator (a triple iterator based on the last checked value) and returns true if // result iterator (a triple iterator based on the last checked value) and returns true if
// another match is made. // another match is made.
func (it *HasaIterator) GetCheckResult() bool { func (it *HasA) GetCheckResult() bool {
for { for {
linkVal, ok := it.resultIt.Next() linkVal, ok := it.resultIt.Next()
if !ok { if !ok {
break break
} }
if glog.V(4) { if glog.V(4) {
glog.V(4).Infoln("Triple is", it.ts.GetTriple(linkVal).ToString()) glog.V(4).Infoln("Triple is", it.ts.GetTriple(linkVal))
} }
if it.primaryIt.Check(linkVal) { if it.primaryIt.Check(linkVal) {
it.Last = it.ts.GetTripleDirection(linkVal, it.dir) it.Last = it.ts.GetTripleDirection(linkVal, it.dir)
@ -155,7 +157,7 @@ func (it *HasaIterator) GetCheckResult() bool {
} }
// Get the next result that matches this branch. // Get the next result that matches this branch.
func (it *HasaIterator) NextResult() bool { func (it *HasA) NextResult() bool {
// Order here is important. If the subiterator has a NextResult, then we // Order here is important. If the subiterator has a NextResult, then we
// need do nothing -- there is a next result, and we shouldn't move forward. // need do nothing -- there is a next result, and we shouldn't move forward.
// However, we then need to get the next result from our last Check(). // However, we then need to get the next result from our last Check().
@ -171,12 +173,12 @@ func (it *HasaIterator) NextResult() bool {
// Get the next result from this iterator. This is simpler than Check. We have a // Get the next result from this iterator. This is simpler than Check. We have a
// subiterator we can get a value from, and we can take that resultant triple, // subiterator we can get a value from, and we can take that resultant triple,
// pull our direction out of it, and return that. // pull our direction out of it, and return that.
func (it *HasaIterator) Next() (TSVal, bool) { func (it *HasA) Next() (graph.TSVal, bool) {
NextLogIn(it) NextLogIn(it)
if it.resultIt != nil { if it.resultIt != nil {
it.resultIt.Close() it.resultIt.Close()
} }
it.resultIt = &NullIterator{} it.resultIt = &Null{}
tID, ok := it.primaryIt.Next() tID, ok := it.primaryIt.Next()
if !ok { if !ok {
@ -190,11 +192,11 @@ func (it *HasaIterator) Next() (TSVal, bool) {
// GetStats() returns the statistics on the HasA iterator. This is curious. Next // GetStats() returns the statistics on the HasA iterator. This is curious. Next
// cost is easy, it's an extra call or so on top of the subiterator Next cost. // cost is easy, it's an extra call or so on top of the subiterator Next cost.
// CheckCost involves going to the TripleStore, iterating out values, and hoping // CheckCost involves going to the graph.TripleStore, iterating out values, and hoping
// one sticks -- potentially expensive, depending on fanout. Size, however, is // one sticks -- potentially expensive, depending on fanout. Size, however, is
// potentially smaller. we know at worst it's the size of the subiterator, but // potentially smaller. we know at worst it's the size of the subiterator, but
// if there are many repeated values, it could be much smaller in totality. // if there are many repeated values, it could be much smaller in totality.
func (it *HasaIterator) GetStats() *IteratorStats { func (it *HasA) GetStats() *graph.IteratorStats {
subitStats := it.primaryIt.GetStats() subitStats := it.primaryIt.GetStats()
// TODO(barakmich): These should really come from the triplestore itself // TODO(barakmich): These should really come from the triplestore itself
// and be optimized. // and be optimized.
@ -202,7 +204,7 @@ func (it *HasaIterator) GetStats() *IteratorStats {
fanoutFactor := int64(30) fanoutFactor := int64(30)
nextConstant := int64(2) nextConstant := int64(2)
tripleConstant := int64(1) tripleConstant := int64(1)
return &IteratorStats{ return &graph.IteratorStats{
NextCost: tripleConstant + subitStats.NextCost, NextCost: tripleConstant + subitStats.NextCost,
CheckCost: (fanoutFactor * nextConstant) * subitStats.CheckCost, CheckCost: (fanoutFactor * nextConstant) * subitStats.CheckCost,
Size: faninFactor * subitStats.Size, Size: faninFactor * subitStats.Size,
@ -210,7 +212,7 @@ func (it *HasaIterator) GetStats() *IteratorStats {
} }
// Close the subiterator, the result iterator (if any) and the HasA. // Close the subiterator, the result iterator (if any) and the HasA.
func (it *HasaIterator) Close() { func (it *HasA) Close() {
if it.resultIt != nil { if it.resultIt != nil {
it.resultIt.Close() it.resultIt.Close()
} }
@ -218,4 +220,4 @@ func (it *HasaIterator) Close() {
} }
// Register this iterator as a HasA. // Register this iterator as a HasA.
func (it *HasaIterator) Type() string { return "hasa" } func (it *HasA) Type() string { return "hasa" }

223
graph/iterator/iterator.go Normal file
View file

@ -0,0 +1,223 @@
// Copyright 2014 The Cayley Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package iterator
// Define the general iterator interface, as well as the Base which all
// iterators can "inherit" from to get default iterator functionality.
import (
"fmt"
"strings"
"github.com/barakmich/glog"
"github.com/google/cayley/graph"
)
var iterator_n int = 0
// The Base iterator is the iterator other iterators inherit from to get some
// default functionality.
type Base struct {
Last graph.TSVal
tags []string
fixedTags map[string]graph.TSVal
nextable bool
uid int
}
// Called by subclases.
func BaseInit(it *Base) {
// Your basic iterator is nextable
it.nextable = true
it.uid = iterator_n
if glog.V(2) {
iterator_n++
}
}
func (it *Base) GetUid() int {
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.TSVal) {
if it.fixedTags == nil {
it.fixedTags = make(map[string]graph.TSVal)
}
it.fixedTags[tag] = value
}
// Returns the tags.
func (it *Base) Tags() []string {
return it.tags
}
func (it *Base) FixedTags() map[string]graph.TSVal {
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))
}
// Nothing in a base iterator.
func (it *Base) Check(v graph.TSVal) bool {
return false
}
// Base iterators should never appear in a tree if they are, select against
// them.
func (it *Base) GetStats() *graph.IteratorStats {
return &graph.IteratorStats{100000, 100000, 100000}
}
// DEPRECATED
func (it *Base) GetResultTree() *graph.ResultTree {
tree := graph.NewResultTree(it.LastResult())
return tree
}
// Nothing in a base iterator.
func (it *Base) Next() (graph.TSVal, bool) {
return nil, false
}
func (it *Base) NextResult() bool {
return false
}
// Returns the last result of an iterator.
func (it *Base) LastResult() graph.TSVal {
return it.Last
}
// If you're empty and you know it, clap your hands.
func (it *Base) Size() (int64, bool) {
return 0, true
}
// No subiterators. Only those with subiterators need to do anything here.
func (it *Base) GetSubIterators() []graph.Iterator {
return nil
}
// Accessor
func (it *Base) Nextable() bool { return it.nextable }
// Fill the map based on the tags assigned to this iterator. Default
// functionality works well for most iterators.
func (it *Base) TagResults(out_map *map[string]graph.TSVal) {
for _, tag := range it.Tags() {
(*out_map)[tag] = it.LastResult()
}
for tag, value := range it.FixedTags() {
(*out_map)[tag] = value
}
}
// Nothing to clean up.
// func (it *Base) Close() {}
func (it *Null) Close() {}
func (it *Base) 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,
// so it's important to give it a special iterator.
type Null struct {
Base
}
// Fairly useless New function.
func NewNull() *Null {
return &Null{}
}
func (it *Null) Clone() graph.Iterator { return NewNull() }
// Name the null iterator.
func (it *Null) Type() string { return "null" }
// A good iterator will close itself when it returns true.
// Null has nothing it needs to do.
func (it *Null) Optimize() (graph.Iterator, bool) { return it, false }
// Print the null iterator.
func (it *Null) DebugString(indent int) string {
return strings.Repeat(" ", indent) + "(null)"
}
// A null iterator costs nothing. Use it!
func (it *Null) GetStats() *graph.IteratorStats {
return &graph.IteratorStats{}
}
// Utility logging functions for when an iterator gets called Next upon, or Check upon, as
// well as what they return. Highly useful for tracing the execution path of a query.
func CheckLogIn(it graph.Iterator, val graph.TSVal) {
if glog.V(4) {
glog.V(4).Infof("%s %d CHECK %d", strings.ToUpper(it.Type()), it.GetUid(), val)
}
}
func CheckLogOut(it graph.Iterator, val graph.TSVal, good bool) bool {
if glog.V(4) {
if good {
glog.V(4).Infof("%s %d CHECK %d GOOD", strings.ToUpper(it.Type()), it.GetUid(), val)
} else {
glog.V(4).Infof("%s %d CHECK %d BAD", strings.ToUpper(it.Type()), it.GetUid(), val)
}
}
return good
}
func NextLogIn(it graph.Iterator) {
if glog.V(4) {
glog.V(4).Infof("%s %d NEXT", strings.ToUpper(it.Type()), it.GetUid())
}
}
func NextLogOut(it graph.Iterator, val graph.TSVal, ok bool) (graph.TSVal, bool) {
if glog.V(4) {
if ok {
glog.V(4).Infof("%s %d NEXT IS %d", strings.ToUpper(it.Type()), it.GetUid(), val)
} else {
glog.V(4).Infof("%s %d NEXT DONE", strings.ToUpper(it.Type()), it.GetUid())
}
}
return val, ok
}

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// Defines one of the base iterators, the LinksTo iterator. A LinksTo takes a // Defines one of the base iterators, the LinksTo iterator. A LinksTo takes a
// subiterator of nodes, and contains an iteration of links which "link to" // subiterator of nodes, and contains an iteration of links which "link to"
@ -32,63 +32,65 @@ package graph
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/google/cayley/graph"
) )
// A LinksTo has a reference back to the TripleStore (to create the iterators // A LinksTo has a reference back to the graph.TripleStore (to create the iterators
// for each node) the subiterator, and the direction the iterator comes from. // for each node) the subiterator, and the direction the iterator comes from.
// `next_it` is the tempoarary iterator held per result in `primary_it`. // `next_it` is the tempoarary iterator held per result in `primary_it`.
type LinksToIterator struct { type LinksTo struct {
BaseIterator Base
ts TripleStore ts graph.TripleStore
primaryIt Iterator primaryIt graph.Iterator
dir Direction dir graph.Direction
nextIt Iterator nextIt graph.Iterator
} }
// Construct a new LinksTo iterator around a direction and a subiterator of // Construct a new LinksTo iterator around a direction and a subiterator of
// nodes. // nodes.
func NewLinksToIterator(ts TripleStore, it Iterator, d Direction) *LinksToIterator { func NewLinksTo(ts graph.TripleStore, it graph.Iterator, d graph.Direction) *LinksTo {
var lto LinksToIterator var lto LinksTo
BaseIteratorInit(&lto.BaseIterator) BaseInit(&lto.Base)
lto.ts = ts lto.ts = ts
lto.primaryIt = it lto.primaryIt = it
lto.dir = d lto.dir = d
lto.nextIt = &NullIterator{} lto.nextIt = &Null{}
return &lto return &lto
} }
func (it *LinksToIterator) Reset() { func (it *LinksTo) Reset() {
it.primaryIt.Reset() it.primaryIt.Reset()
if it.nextIt != nil { if it.nextIt != nil {
it.nextIt.Close() it.nextIt.Close()
} }
it.nextIt = &NullIterator{} it.nextIt = &Null{}
} }
func (it *LinksToIterator) Clone() Iterator { func (it *LinksTo) Clone() graph.Iterator {
out := NewLinksToIterator(it.ts, it.primaryIt.Clone(), it.dir) out := NewLinksTo(it.ts, it.primaryIt.Clone(), it.dir)
out.CopyTagsFrom(it) out.CopyTagsFrom(it)
return out return out
} }
// Return the direction under consideration. // Return the direction under consideration.
func (it *LinksToIterator) Direction() Direction { return it.dir } func (it *LinksTo) Direction() graph.Direction { return it.dir }
// Tag these results, and our subiterator's results. // Tag these results, and our subiterator's results.
func (it *LinksToIterator) TagResults(out *map[string]TSVal) { func (it *LinksTo) TagResults(out *map[string]graph.TSVal) {
it.BaseIterator.TagResults(out) it.Base.TagResults(out)
it.primaryIt.TagResults(out) it.primaryIt.TagResults(out)
} }
// DEPRECATED // DEPRECATED
func (it *LinksToIterator) GetResultTree() *ResultTree { func (it *LinksTo) GetResultTree() *graph.ResultTree {
tree := NewResultTree(it.LastResult()) tree := graph.NewResultTree(it.LastResult())
tree.AddSubtree(it.primaryIt.GetResultTree()) tree.AddSubtree(it.primaryIt.GetResultTree())
return tree return tree
} }
// Print the iterator. // Print the iterator.
func (it *LinksToIterator) DebugString(indent int) string { func (it *LinksTo) DebugString(indent int) string {
return fmt.Sprintf("%s(%s %d direction:%s\n%s)", return fmt.Sprintf("%s(%s %d direction:%s\n%s)",
strings.Repeat(" ", indent), strings.Repeat(" ", indent),
it.Type(), it.GetUid(), it.dir, it.primaryIt.DebugString(indent+4)) it.Type(), it.GetUid(), it.dir, it.primaryIt.DebugString(indent+4))
@ -96,7 +98,7 @@ func (it *LinksToIterator) DebugString(indent int) string {
// If it checks in the right direction for the subiterator, it is a valid link // If it checks in the right direction for the subiterator, it is a valid link
// for the LinksTo. // for the LinksTo.
func (it *LinksToIterator) Check(val TSVal) bool { func (it *LinksTo) Check(val graph.TSVal) bool {
CheckLogIn(it, val) CheckLogIn(it, val)
node := it.ts.GetTripleDirection(val, it.dir) node := it.ts.GetTripleDirection(val, it.dir)
if it.primaryIt.Check(node) { if it.primaryIt.Check(node) {
@ -107,12 +109,12 @@ func (it *LinksToIterator) Check(val TSVal) bool {
} }
// Return a list containing only our subiterator. // Return a list containing only our subiterator.
func (it *LinksToIterator) GetSubIterators() []Iterator { func (it *LinksTo) GetSubIterators() []graph.Iterator {
return []Iterator{it.primaryIt} return []graph.Iterator{it.primaryIt}
} }
// Optimize the LinksTo, by replacing it if it can be. // Optimize the LinksTo, by replacing it if it can be.
func (it *LinksToIterator) Optimize() (Iterator, bool) { func (it *LinksTo) Optimize() (graph.Iterator, bool) {
newPrimary, changed := it.primaryIt.Optimize() newPrimary, changed := it.primaryIt.Optimize()
if changed { if changed {
it.primaryIt = newPrimary it.primaryIt = newPrimary
@ -121,7 +123,7 @@ func (it *LinksToIterator) Optimize() (Iterator, bool) {
return it.primaryIt, true return it.primaryIt, true
} }
} }
// Ask the TripleStore if we can be replaced. Often times, this is a great // Ask the graph.TripleStore if we can be replaced. Often times, this is a great
// optimization opportunity (there's a fixed iterator underneath us, for // optimization opportunity (there's a fixed iterator underneath us, for
// example). // example).
newReplacement, hasOne := it.ts.OptimizeIterator(it) newReplacement, hasOne := it.ts.OptimizeIterator(it)
@ -133,7 +135,7 @@ func (it *LinksToIterator) Optimize() (Iterator, bool) {
} }
// Next()ing a LinksTo operates as described above. // Next()ing a LinksTo operates as described above.
func (it *LinksToIterator) Next() (TSVal, bool) { func (it *LinksTo) Next() (graph.TSVal, bool) {
NextLogIn(it) NextLogIn(it)
val, ok := it.nextIt.Next() val, ok := it.nextIt.Next()
if !ok { if !ok {
@ -153,27 +155,27 @@ func (it *LinksToIterator) Next() (TSVal, bool) {
} }
// Close our subiterators. // Close our subiterators.
func (it *LinksToIterator) Close() { func (it *LinksTo) Close() {
it.nextIt.Close() it.nextIt.Close()
it.primaryIt.Close() it.primaryIt.Close()
} }
// We won't ever have a new result, but our subiterators might. // We won't ever have a new result, but our subiterators might.
func (it *LinksToIterator) NextResult() bool { func (it *LinksTo) NextResult() bool {
return it.primaryIt.NextResult() return it.primaryIt.NextResult()
} }
// Register the LinksTo. // Register the LinksTo.
func (it *LinksToIterator) Type() string { return "linksto" } func (it *LinksTo) Type() string { return "linksto" }
// Return a guess as to how big or costly it is to next the iterator. // Return a guess as to how big or costly it is to next the iterator.
func (it *LinksToIterator) GetStats() *IteratorStats { func (it *LinksTo) GetStats() *graph.IteratorStats {
subitStats := it.primaryIt.GetStats() subitStats := it.primaryIt.GetStats()
// TODO(barakmich): These should really come from the triplestore itself // TODO(barakmich): These should really come from the triplestore itself
fanoutFactor := int64(20) fanoutFactor := int64(20)
checkConstant := int64(1) checkConstant := int64(1)
nextConstant := int64(2) nextConstant := int64(2)
return &IteratorStats{ return &graph.IteratorStats{
NextCost: nextConstant + subitStats.NextCost, NextCost: nextConstant + subitStats.NextCost,
CheckCost: checkConstant + subitStats.CheckCost, CheckCost: checkConstant + subitStats.CheckCost,
Size: fanoutFactor * subitStats.Size, Size: fanoutFactor * subitStats.Size,

View file

@ -12,21 +12,23 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
import ( import (
"testing" "testing"
"github.com/google/cayley/graph"
) )
func TestLinksTo(t *testing.T) { func TestLinksTo(t *testing.T) {
ts := new(TestTripleStore) ts := new(TestTripleStore)
tsFixed := newFixedIterator() tsFixed := newFixed()
tsFixed.AddValue(2) tsFixed.AddValue(2)
ts.On("GetIdFor", "cool").Return(1) ts.On("GetIdFor", "cool").Return(1)
ts.On("GetTripleIterator", Object, 1).Return(tsFixed) ts.On("GetTripleIterator", graph.Object, 1).Return(tsFixed)
fixed := newFixedIterator() fixed := newFixed()
fixed.AddValue(ts.GetIdFor("cool")) fixed.AddValue(ts.GetIdFor("cool"))
lto := NewLinksToIterator(ts, fixed, Object) lto := NewLinksTo(ts, fixed, graph.Object)
val, ok := lto.Next() val, ok := lto.Next()
if !ok { if !ok {
t.Error("At least one triple matches the fixed object") t.Error("At least one triple matches the fixed object")

View file

@ -12,47 +12,49 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// A quickly mocked version of the TripleStore interface, for use in tests. // A quickly mocked version of the TripleStore interface, for use in tests.
// Can better used Mock.Called but will fill in as needed. // Can better used Mock.Called but will fill in as needed.
import ( import (
"github.com/stretchrcom/testify/mock" "github.com/stretchrcom/testify/mock"
"github.com/google/cayley/graph"
) )
type TestTripleStore struct { type TestTripleStore struct {
mock.Mock mock.Mock
} }
func (ts *TestTripleStore) GetIdFor(s string) TSVal { func (ts *TestTripleStore) GetIdFor(s string) graph.TSVal {
args := ts.Mock.Called(s) args := ts.Mock.Called(s)
return args.Get(0) return args.Get(0)
} }
func (ts *TestTripleStore) AddTriple(*Triple) {} func (ts *TestTripleStore) AddTriple(*graph.Triple) {}
func (ts *TestTripleStore) AddTripleSet([]*Triple) {} func (ts *TestTripleStore) AddTripleSet([]*graph.Triple) {}
func (ts *TestTripleStore) GetTriple(TSVal) *Triple { return &Triple{} } func (ts *TestTripleStore) GetTriple(graph.TSVal) *graph.Triple { return &graph.Triple{} }
func (ts *TestTripleStore) GetTripleIterator(d Direction, i TSVal) Iterator { func (ts *TestTripleStore) GetTripleIterator(d graph.Direction, i graph.TSVal) graph.Iterator {
args := ts.Mock.Called(d, i) args := ts.Mock.Called(d, i)
return args.Get(0).(Iterator) return args.Get(0).(graph.Iterator)
} }
func (ts *TestTripleStore) GetNodesAllIterator() Iterator { return &NullIterator{} } func (ts *TestTripleStore) GetNodesAllIterator() graph.Iterator { return &Null{} }
func (ts *TestTripleStore) GetTriplesAllIterator() Iterator { return &NullIterator{} } func (ts *TestTripleStore) GetTriplesAllIterator() graph.Iterator { return &Null{} }
func (ts *TestTripleStore) GetIteratorByString(string, string, string) Iterator { func (ts *TestTripleStore) GetIteratorByString(string, string, string) graph.Iterator {
return &NullIterator{} return &Null{}
} }
func (ts *TestTripleStore) GetNameFor(v TSVal) string { func (ts *TestTripleStore) GetNameFor(v graph.TSVal) string {
args := ts.Mock.Called(v) args := ts.Mock.Called(v)
return args.Get(0).(string) return args.Get(0).(string)
} }
func (ts *TestTripleStore) Size() int64 { return 0 } func (ts *TestTripleStore) Size() int64 { return 0 }
func (ts *TestTripleStore) DebugPrint() {} func (ts *TestTripleStore) DebugPrint() {}
func (ts *TestTripleStore) OptimizeIterator(it Iterator) (Iterator, bool) { func (ts *TestTripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) {
return &NullIterator{}, false return &Null{}, false
} }
func (ts *TestTripleStore) MakeFixed() *FixedIterator { func (ts *TestTripleStore) FixedIterator() graph.FixedIterator {
return NewFixedIteratorWithCompare(BasicEquality) return NewFixedIteratorWithCompare(BasicEquality)
} }
func (ts *TestTripleStore) Close() {} func (ts *TestTripleStore) Close() {}
func (ts *TestTripleStore) GetTripleDirection(TSVal, Direction) TSVal { return 0 } func (ts *TestTripleStore) GetTripleDirection(graph.TSVal, graph.Direction) graph.TSVal { return 0 }
func (ts *TestTripleStore) RemoveTriple(t *Triple) {} func (ts *TestTripleStore) RemoveTriple(t *graph.Triple) {}

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// "Optional" is kind of odd. It's not an iterator in the strictest sense, but // "Optional" is kind of odd. It's not an iterator in the strictest sense, but
// it's easier to implement as an iterator. // it's easier to implement as an iterator.
@ -31,43 +31,45 @@ import (
"strings" "strings"
"github.com/barakmich/glog" "github.com/barakmich/glog"
"github.com/google/cayley/graph"
) )
// An optional iterator has the subconstraint iterator we wish to be optional // An optional iterator has the subconstraint iterator we wish to be optional
// and whether the last check we received was true or false. // and whether the last check we received was true or false.
type OptionalIterator struct { type Optional struct {
BaseIterator Base
subIt Iterator subIt graph.Iterator
lastCheck bool lastCheck bool
} }
// Creates a new optional iterator. // Creates a new optional iterator.
func NewOptionalIterator(it Iterator) *OptionalIterator { func NewOptional(it graph.Iterator) *Optional {
var o OptionalIterator var o Optional
BaseIteratorInit(&o.BaseIterator) BaseInit(&o.Base)
o.nextable = false o.nextable = false
o.subIt = it o.subIt = it
return &o return &o
} }
func (it *OptionalIterator) Reset() { func (it *Optional) Reset() {
it.subIt.Reset() it.subIt.Reset()
it.lastCheck = false it.lastCheck = false
} }
func (it *OptionalIterator) Close() { func (it *Optional) Close() {
it.subIt.Close() it.subIt.Close()
} }
func (it *OptionalIterator) Clone() Iterator { func (it *Optional) Clone() graph.Iterator {
out := NewOptionalIterator(it.subIt.Clone()) out := NewOptional(it.subIt.Clone())
out.CopyTagsFrom(it) out.CopyTagsFrom(it)
return out return out
} }
// Nexting the iterator is unsupported -- error and return an empty set. // Nexting the iterator is unsupported -- error and return an empty set.
// (As above, a reasonable alternative would be to Next() an all iterator) // (As above, a reasonable alternative would be to Next() an all iterator)
func (it *OptionalIterator) Next() (TSVal, bool) { func (it *Optional) Next() (graph.TSVal, bool) {
glog.Errorln("Nexting an un-nextable iterator") glog.Errorln("Nexting an un-nextable iterator")
return nil, false return nil, false
} }
@ -75,7 +77,7 @@ func (it *OptionalIterator) Next() (TSVal, bool) {
// An optional iterator only has a next result if, (a) last time we checked // An optional iterator only has a next result if, (a) last time we checked
// we had any results whatsoever, and (b) there was another subresult in our // we had any results whatsoever, and (b) there was another subresult in our
// optional subbranch. // optional subbranch.
func (it *OptionalIterator) NextResult() bool { func (it *Optional) NextResult() bool {
if it.lastCheck { if it.lastCheck {
return it.subIt.NextResult() return it.subIt.NextResult()
} }
@ -85,7 +87,7 @@ func (it *OptionalIterator) NextResult() bool {
// Check() is the real hack of this iterator. It always returns true, regardless // Check() is the real hack of this iterator. It always returns true, regardless
// of whether the subiterator matched. But we keep track of whether the subiterator // of whether the subiterator matched. But we keep track of whether the subiterator
// matched for results purposes. // matched for results purposes.
func (it *OptionalIterator) Check(val TSVal) bool { func (it *Optional) Check(val graph.TSVal) bool {
checked := it.subIt.Check(val) checked := it.subIt.Check(val)
it.lastCheck = checked it.lastCheck = checked
it.Last = val it.Last = val
@ -94,7 +96,7 @@ func (it *OptionalIterator) Check(val TSVal) bool {
// If we failed the check, then the subiterator should not contribute to the result // If we failed the check, then the subiterator should not contribute to the result
// set. Otherwise, go ahead and tag it. // set. Otherwise, go ahead and tag it.
func (it *OptionalIterator) TagResults(out *map[string]TSVal) { func (it *Optional) TagResults(out *map[string]graph.TSVal) {
if it.lastCheck == false { if it.lastCheck == false {
return return
} }
@ -102,10 +104,10 @@ func (it *OptionalIterator) TagResults(out *map[string]TSVal) {
} }
// Registers the optional iterator. // Registers the optional iterator.
func (it *OptionalIterator) Type() string { return "optional" } func (it *Optional) Type() string { return "optional" }
// Prints the optional and it's subiterator. // Prints the optional and it's subiterator.
func (it *OptionalIterator) DebugString(indent int) string { func (it *Optional) DebugString(indent int) string {
return fmt.Sprintf("%s(%s tags:%s\n%s)", return fmt.Sprintf("%s(%s tags:%s\n%s)",
strings.Repeat(" ", indent), strings.Repeat(" ", indent),
it.Type(), it.Type(),
@ -115,7 +117,7 @@ func (it *OptionalIterator) DebugString(indent int) string {
// There's nothing to optimize for an optional. Optimize the subiterator and // There's nothing to optimize for an optional. Optimize the subiterator and
// potentially replace it. // potentially replace it.
func (it *OptionalIterator) Optimize() (Iterator, bool) { func (it *Optional) Optimize() (graph.Iterator, bool) {
newSub, changed := it.subIt.Optimize() newSub, changed := it.subIt.Optimize()
if changed { if changed {
it.subIt.Close() it.subIt.Close()
@ -125,9 +127,9 @@ func (it *OptionalIterator) Optimize() (Iterator, bool) {
} }
// We're only as expensive as our subiterator. Except, we can't be nexted. // We're only as expensive as our subiterator. Except, we can't be nexted.
func (it *OptionalIterator) GetStats() *IteratorStats { func (it *Optional) GetStats() *graph.IteratorStats {
subStats := it.subIt.GetStats() subStats := it.subIt.GetStats()
return &IteratorStats{ return &graph.IteratorStats{
CheckCost: subStats.CheckCost, CheckCost: subStats.CheckCost,
NextCost: int64(1 << 62), NextCost: int64(1 << 62),
Size: subStats.Size, Size: subStats.Size,

View file

@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// Defines the or and short-circuiting or iterator. Or is the union operator for it's subiterators. // Defines the or and short-circuiting or iterator. Or is the union operator for it's subiterators.
// Short-circuiting-or is a little different. It will return values from the first iterator that returns // Short-circuiting-or is a little different. It will return values from the first graph.iterator that returns
// values at all, and then stops. // values at all, and then stops.
// //
// Never reorders the iterators from the order they arrive. It is either the union or the first one. // Never reorders the iterators from the order they arrive. It is either the union or the first one.
@ -24,48 +24,50 @@ package graph
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/google/cayley/graph"
) )
type OrIterator struct { type Or struct {
BaseIterator Base
isShortCircuiting bool isShortCircuiting bool
internalIterators []Iterator internalIterators []graph.Iterator
itCount int itCount int
currentIterator int currentIterator int
} }
func NewOrIterator() *OrIterator { func NewOr() *Or {
var or OrIterator var or Or
BaseIteratorInit(&or.BaseIterator) BaseInit(&or.Base)
or.internalIterators = make([]Iterator, 0, 20) or.internalIterators = make([]graph.Iterator, 0, 20)
or.isShortCircuiting = false or.isShortCircuiting = false
or.currentIterator = -1 or.currentIterator = -1
return &or return &or
} }
func NewShortCircuitOrIterator() *OrIterator { func NewShortCircuitOr() *Or {
var or OrIterator var or Or
BaseIteratorInit(&or.BaseIterator) BaseInit(&or.Base)
or.internalIterators = make([]Iterator, 0, 20) or.internalIterators = make([]graph.Iterator, 0, 20)
or.isShortCircuiting = true or.isShortCircuiting = true
or.currentIterator = -1 or.currentIterator = -1
return &or return &or
} }
// Reset all internal iterators // Reset all internal iterators
func (it *OrIterator) Reset() { func (it *Or) Reset() {
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
sub.Reset() sub.Reset()
} }
it.currentIterator = -1 it.currentIterator = -1
} }
func (it *OrIterator) Clone() Iterator { func (it *Or) Clone() graph.Iterator {
var or *OrIterator var or *Or
if it.isShortCircuiting { if it.isShortCircuiting {
or = NewShortCircuitOrIterator() or = NewShortCircuitOr()
} else { } else {
or = NewOrIterator() or = NewOr()
} }
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
or.AddSubIterator(sub.Clone()) or.AddSubIterator(sub.Clone())
@ -75,28 +77,28 @@ func (it *OrIterator) Clone() Iterator {
} }
// Returns a list.List of the subiterators, in order. The returned slice must not be modified. // Returns a list.List of the subiterators, in order. The returned slice must not be modified.
func (it *OrIterator) GetSubIterators() []Iterator { func (it *Or) GetSubIterators() []graph.Iterator {
return it.internalIterators return it.internalIterators
} }
// Overrides BaseIterator TagResults, as it needs to add it's own results and // Overrides BaseIterator TagResults, as it needs to add it's own results and
// recurse down it's subiterators. // recurse down it's subiterators.
func (it *OrIterator) TagResults(out *map[string]TSVal) { func (it *Or) TagResults(out *map[string]graph.TSVal) {
it.BaseIterator.TagResults(out) it.Base.TagResults(out)
it.internalIterators[it.currentIterator].TagResults(out) it.internalIterators[it.currentIterator].TagResults(out)
} }
// DEPRECATED Returns the ResultTree for this iterator, recurses to it's subiterators. // DEPRECATED Returns the ResultTree for this graph.iterator, recurses to it's subiterators.
func (it *OrIterator) GetResultTree() *ResultTree { func (it *Or) GetResultTree() *graph.ResultTree {
tree := NewResultTree(it.LastResult()) tree := graph.NewResultTree(it.LastResult())
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
tree.AddSubtree(sub.GetResultTree()) tree.AddSubtree(sub.GetResultTree())
} }
return tree return tree
} }
// Prints information about this iterator. // Prints information about this graph.iterator.
func (it *OrIterator) DebugString(indent int) string { func (it *Or) DebugString(indent int) string {
var total string var total string
for i, sub := range it.internalIterators { for i, sub := range it.internalIterators {
total += strings.Repeat(" ", indent+2) total += strings.Repeat(" ", indent+2)
@ -117,18 +119,18 @@ func (it *OrIterator) DebugString(indent int) string {
total) total)
} }
// Add a subiterator to this Or iterator. Order matters. // Add a subiterator to this Or graph.iterator. Order matters.
func (it *OrIterator) AddSubIterator(sub Iterator) { func (it *Or) AddSubIterator(sub graph.Iterator) {
it.internalIterators = append(it.internalIterators, sub) it.internalIterators = append(it.internalIterators, sub)
it.itCount++ it.itCount++
} }
// Returns the Next value from the Or iterator. Because the Or is the // Returns the Next value from the Or graph.iterator. Because the Or is the
// union of its subiterators, it must produce from all subiterators -- unless // union of its subiterators, it must produce from all subiterators -- unless
// it's shortcircuiting, in which case, it's the first one that returns anything. // it's shortcircuiting, in which case, it's the first one that returns anything.
func (it *OrIterator) Next() (TSVal, bool) { func (it *Or) Next() (graph.TSVal, bool) {
NextLogIn(it) NextLogIn(it)
var curr TSVal var curr graph.TSVal
var exists bool var exists bool
firstTime := false firstTime := false
for { for {
@ -151,11 +153,11 @@ func (it *OrIterator) Next() (TSVal, bool) {
return NextLogOut(it, curr, true) return NextLogOut(it, curr, true)
} }
} }
panic("Somehow broke out of Next() loop in OrIterator") panic("Somehow broke out of Next() loop in Or")
} }
// Checks a value against the iterators, in order. // Checks a value against the iterators, in order.
func (it *OrIterator) checkSubIts(val TSVal) bool { func (it *Or) checkSubIts(val graph.TSVal) bool {
var subIsGood = false var subIsGood = false
for i, sub := range it.internalIterators { for i, sub := range it.internalIterators {
subIsGood = sub.Check(val) subIsGood = sub.Check(val)
@ -167,8 +169,8 @@ func (it *OrIterator) checkSubIts(val TSVal) bool {
return subIsGood return subIsGood
} }
// Check a value against the entire iterator, in order. // Check a value against the entire graph.iterator, in order.
func (it *OrIterator) Check(val TSVal) bool { func (it *Or) Check(val graph.TSVal) bool {
CheckLogIn(it, val) CheckLogIn(it, val)
anyGood := it.checkSubIts(val) anyGood := it.checkSubIts(val)
if !anyGood { if !anyGood {
@ -178,10 +180,10 @@ func (it *OrIterator) Check(val TSVal) bool {
return CheckLogOut(it, val, true) return CheckLogOut(it, val, true)
} }
// Returns the approximate size of the Or iterator. Because we're dealing // Returns the approximate size of the Or graph.iterator. Because we're dealing
// with a union, we know that the largest we can be is the sum of all the iterators, // with a union, we know that the largest we can be is the sum of all the iterators,
// or in the case of short-circuiting, the longest. // or in the case of short-circuiting, the longest.
func (it *OrIterator) Size() (int64, bool) { func (it *Or) Size() (int64, bool) {
var val int64 var val int64
var b bool var b bool
if it.isShortCircuiting { if it.isShortCircuiting {
@ -209,8 +211,8 @@ func (it *OrIterator) Size() (int64, bool) {
// An Or has no NextResult of its own -- that is, there are no other values // An Or has no NextResult of its own -- that is, there are no other values
// which satisfy our previous result that are not the result itself. Our // which satisfy our previous result that are not the result itself. Our
// subiterators might, however, so just pass the call recursively. In the case of // subiterators might, however, so just pass the call recursively. In the case of
// shortcircuiting, only allow new results from the currently checked iterator // shortcircuiting, only allow new results from the currently checked graph.iterator
func (it *OrIterator) NextResult() bool { func (it *Or) NextResult() bool {
if it.currentIterator != -1 { if it.currentIterator != -1 {
return it.internalIterators[it.currentIterator].NextResult() return it.internalIterators[it.currentIterator].NextResult()
} }
@ -218,25 +220,25 @@ func (it *OrIterator) NextResult() bool {
} }
// Perform or-specific cleanup, of which there currently is none. // Perform or-specific cleanup, of which there currently is none.
func (it *OrIterator) cleanUp() {} func (it *Or) cleanUp() {}
// Close this iterator, and, by extension, close the subiterators. // Close this graph.iterator, and, by extension, close the subiterators.
// Close should be idempotent, and it follows that if it's subiterators // Close should be idempotent, and it follows that if it's subiterators
// follow this contract, the And follows the contract. // follow this contract, the And follows the contract.
func (it *OrIterator) Close() { func (it *Or) Close() {
it.cleanUp() it.cleanUp()
for _, sub := range it.internalIterators { for _, sub := range it.internalIterators {
sub.Close() sub.Close()
} }
} }
func (it *OrIterator) Optimize() (Iterator, bool) { func (it *Or) Optimize() (graph.Iterator, bool) {
old := it.GetSubIterators() old := it.GetSubIterators()
optIts := optimizeSubIterators(old) optIts := optimizeSubIterators(old)
// Close the replaced iterators (they ought to close themselves, but Close() // Close the replaced iterators (they ought to close themselves, but Close()
// is idempotent, so this just protects against any machinations). // is idempotent, so this just protects against any machinations).
closeIteratorList(old, nil) closeIteratorList(old, nil)
newOr := NewOrIterator() newOr := NewOr()
newOr.isShortCircuiting = it.isShortCircuiting newOr.isShortCircuiting = it.isShortCircuiting
// Add the subiterators in order. // Add the subiterators in order.
@ -254,7 +256,7 @@ func (it *OrIterator) Optimize() (Iterator, bool) {
return newOr, true return newOr, true
} }
func (it *OrIterator) GetStats() *IteratorStats { func (it *Or) GetStats() *graph.IteratorStats {
CheckCost := int64(0) CheckCost := int64(0)
NextCost := int64(0) NextCost := int64(0)
Size := int64(0) Size := int64(0)
@ -270,7 +272,7 @@ func (it *OrIterator) GetStats() *IteratorStats {
Size += stats.Size Size += stats.Size
} }
} }
return &IteratorStats{ return &graph.IteratorStats{
CheckCost: CheckCost, CheckCost: CheckCost,
NextCost: NextCost, NextCost: NextCost,
Size: Size, Size: Size,
@ -278,5 +280,5 @@ func (it *OrIterator) GetStats() *IteratorStats {
} }
// Register this as an "or" iterator. // Register this as an "or" graph.iterator.
func (it *OrIterator) Type() string { return "or" } func (it *Or) Type() string { return "or" }

View file

@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
import ( import (
. "github.com/smartystreets/goconvey/convey"
"testing" "testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/google/cayley/graph"
) )
func extractNumbersFromIterator(it Iterator) []int { func extractNumbersFromIterator(it graph.Iterator) []int {
var outputNumbers []int var outputNumbers []int
for { for {
val, ok := it.Next() val, ok := it.Next()
@ -32,15 +35,15 @@ func extractNumbersFromIterator(it Iterator) []int {
} }
func TestOrIteratorBasics(t *testing.T) { func TestOrIteratorBasics(t *testing.T) {
var orIt *OrIterator var orIt *Or
Convey("Given an Or Iterator of two fixed iterators", t, func() { Convey("Given an Or Iterator of two fixed iterators", t, func() {
orIt = NewOrIterator() orIt = NewOr()
fixed1 := newFixedIterator() fixed1 := newFixed()
fixed1.AddValue(1) fixed1.AddValue(1)
fixed1.AddValue(2) fixed1.AddValue(2)
fixed1.AddValue(3) fixed1.AddValue(3)
fixed2 := newFixedIterator() fixed2 := newFixed()
fixed2.AddValue(3) fixed2.AddValue(3)
fixed2.AddValue(9) fixed2.AddValue(9)
fixed2.AddValue(20) fixed2.AddValue(20)
@ -80,15 +83,15 @@ func TestOrIteratorBasics(t *testing.T) {
} }
func TestShortCircuitingOrBasics(t *testing.T) { func TestShortCircuitingOrBasics(t *testing.T) {
var orIt *OrIterator var orIt *Or
Convey("Given a short-circuiting Or of two fixed iterators", t, func() { Convey("Given a short-circuiting Or of two fixed iterators", t, func() {
orIt = NewShortCircuitOrIterator() orIt = NewShortCircuitOr()
fixed1 := newFixedIterator() fixed1 := newFixed()
fixed1.AddValue(1) fixed1.AddValue(1)
fixed1.AddValue(2) fixed1.AddValue(2)
fixed1.AddValue(3) fixed1.AddValue(3)
fixed2 := newFixedIterator() fixed2 := newFixed()
fixed2.AddValue(3) fixed2.AddValue(3)
fixed2.AddValue(9) fixed2.AddValue(9)
fixed2.AddValue(20) fixed2.AddValue(20)
@ -126,7 +129,7 @@ func TestShortCircuitingOrBasics(t *testing.T) {
}) })
Convey("It should check that it pulls the second iterator's numbers if the first is empty.", func() { Convey("It should check that it pulls the second iterator's numbers if the first is empty.", func() {
orIt.AddSubIterator(newFixedIterator()) orIt.AddSubIterator(newFixed())
orIt.AddSubIterator(fixed2) orIt.AddSubIterator(fixed2)
allNumbers := []int{3, 9, 20, 21} allNumbers := []int{3, 9, 20, 21}
So(extractNumbersFromIterator(orIt), ShouldResemble, allNumbers) So(extractNumbersFromIterator(orIt), ShouldResemble, allNumbers)

View file

@ -12,7 +12,11 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
import (
"github.com/google/cayley/graph"
)
type Node struct { type Node struct {
Id int `json:"id"` Id int `json:"id"`
@ -32,13 +36,13 @@ type Link struct {
type queryShape struct { type queryShape struct {
nodes []Node nodes []Node
links []Link links []Link
ts TripleStore ts graph.TripleStore
nodeId int nodeId int
hasaIds []int hasaIds []int
hasaDirs []Direction hasaDirs []graph.Direction
} }
func OutputQueryShapeForIterator(it Iterator, ts TripleStore, outputMap *map[string]interface{}) { func OutputQueryShapeForIterator(it graph.Iterator, ts graph.TripleStore, outputMap *map[string]interface{}) {
qs := &queryShape{ qs := &queryShape{
ts: ts, ts: ts,
nodeId: 1, nodeId: 1,
@ -58,11 +62,11 @@ func (qs *queryShape) AddLink(l *Link) {
qs.links = append(qs.links, *l) qs.links = append(qs.links, *l)
} }
func (qs *queryShape) LastHasa() (int, Direction) { func (qs *queryShape) LastHasa() (int, graph.Direction) {
return qs.hasaIds[len(qs.hasaIds)-1], qs.hasaDirs[len(qs.hasaDirs)-1] return qs.hasaIds[len(qs.hasaIds)-1], qs.hasaDirs[len(qs.hasaDirs)-1]
} }
func (qs *queryShape) PushHasa(i int, d Direction) { func (qs *queryShape) PushHasa(i int, d graph.Direction) {
qs.hasaIds = append(qs.hasaIds, i) qs.hasaIds = append(qs.hasaIds, i)
qs.hasaDirs = append(qs.hasaDirs, d) qs.hasaDirs = append(qs.hasaDirs, d)
} }
@ -101,7 +105,7 @@ func (qs *queryShape) StealNode(left *Node, right *Node) {
} }
} }
func (qs *queryShape) MakeNode(it Iterator) *Node { func (qs *queryShape) MakeNode(it graph.Iterator) *Node {
n := Node{Id: qs.nodeId} n := Node{Id: qs.nodeId}
for _, tag := range it.Tags() { for _, tag := range it.Tags() {
n.Tags = append(n.Tags, tag) n.Tags = append(n.Tags, tag)
@ -132,7 +136,7 @@ func (qs *queryShape) MakeNode(it Iterator) *Node {
n.Values = append(n.Values, qs.ts.GetNameFor(val)) n.Values = append(n.Values, qs.ts.GetNameFor(val))
} }
case "hasa": case "hasa":
hasa := it.(*HasaIterator) hasa := it.(*HasA)
qs.PushHasa(n.Id, hasa.dir) qs.PushHasa(n.Id, hasa.dir)
qs.nodeId++ qs.nodeId++
newNode := qs.MakeNode(hasa.primaryIt) newNode := qs.MakeNode(hasa.primaryIt)
@ -151,14 +155,14 @@ func (qs *queryShape) MakeNode(it Iterator) *Node {
} }
case "linksto": case "linksto":
n.IsLinkNode = true n.IsLinkNode = true
lto := it.(*LinksToIterator) lto := it.(*LinksTo)
qs.nodeId++ qs.nodeId++
newNode := qs.MakeNode(lto.primaryIt) newNode := qs.MakeNode(lto.primaryIt)
hasaID, hasaDir := qs.LastHasa() hasaID, hasaDir := qs.LastHasa()
if (hasaDir == Subject && lto.dir == Object) || if (hasaDir == graph.Subject && lto.dir == graph.Object) ||
(hasaDir == Object && lto.dir == Subject) { (hasaDir == graph.Object && lto.dir == graph.Subject) {
qs.AddNode(newNode) qs.AddNode(newNode)
if hasaDir == Subject { if hasaDir == graph.Subject {
qs.AddLink(&Link{hasaID, newNode.Id, 0, n.Id}) qs.AddLink(&Link{hasaID, newNode.Id, 0, n.Id})
} else { } else {
qs.AddLink(&Link{newNode.Id, hasaID, 0, n.Id}) qs.AddLink(&Link{newNode.Id, hasaID, 0, n.Id})

View file

@ -12,33 +12,34 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
import ( import (
"testing" "testing"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"github.com/google/cayley/graph"
) )
func buildHasaWithTag(ts TripleStore, tag string, target string) *HasaIterator { func buildHasaWithTag(ts graph.TripleStore, tag string, target string) *HasA {
fixed_obj := ts.MakeFixed() fixed_obj := ts.FixedIterator()
fixed_pred := ts.MakeFixed() fixed_pred := ts.FixedIterator()
fixed_obj.AddValue(ts.GetIdFor(target)) fixed_obj.AddValue(ts.GetIdFor(target))
fixed_pred.AddValue(ts.GetIdFor("status")) fixed_pred.AddValue(ts.GetIdFor("status"))
fixed_obj.AddTag(tag) fixed_obj.AddTag(tag)
lto1 := NewLinksToIterator(ts, fixed_obj, Object) lto1 := NewLinksTo(ts, fixed_obj, graph.Object)
lto2 := NewLinksToIterator(ts, fixed_pred, Predicate) lto2 := NewLinksTo(ts, fixed_pred, graph.Predicate)
and := NewAndIterator() and := NewAnd()
and.AddSubIterator(lto1) and.AddSubIterator(lto1)
and.AddSubIterator(lto2) and.AddSubIterator(lto2)
hasa := NewHasaIterator(ts, and, Subject) hasa := NewHasA(ts, and, graph.Subject)
return hasa return hasa
} }
func TestQueryShape(t *testing.T) { func TestQueryShape(t *testing.T) {
var queryShape map[string]interface{} var queryShape map[string]interface{}
var ts *TestTripleStore ts := new(TestTripleStore)
ts = new(TestTripleStore)
ts.On("GetIdFor", "cool").Return(1) ts.On("GetIdFor", "cool").Return(1)
ts.On("GetNameFor", 1).Return("cool") ts.On("GetNameFor", 1).Return("cool")
ts.On("GetIdFor", "status").Return(2) ts.On("GetIdFor", "status").Return(2)
@ -87,17 +88,17 @@ func TestQueryShape(t *testing.T) {
hasa1.AddTag("hasa1") hasa1.AddTag("hasa1")
hasa2 := buildHasaWithTag(ts, "tag2", "fun") hasa2 := buildHasaWithTag(ts, "tag2", "fun")
hasa1.AddTag("hasa2") hasa1.AddTag("hasa2")
andInternal := NewAndIterator() andInternal := NewAnd()
andInternal.AddSubIterator(hasa1) andInternal.AddSubIterator(hasa1)
andInternal.AddSubIterator(hasa2) andInternal.AddSubIterator(hasa2)
fixed_pred := ts.MakeFixed() fixed_pred := ts.FixedIterator()
fixed_pred.AddValue(ts.GetIdFor("name")) fixed_pred.AddValue(ts.GetIdFor("name"))
lto1 := NewLinksToIterator(ts, andInternal, Subject) lto1 := NewLinksTo(ts, andInternal, graph.Subject)
lto2 := NewLinksToIterator(ts, fixed_pred, Predicate) lto2 := NewLinksTo(ts, fixed_pred, graph.Predicate)
and := NewAndIterator() and := NewAnd()
and.AddSubIterator(lto1) and.AddSubIterator(lto1)
and.AddSubIterator(lto2) and.AddSubIterator(lto2)
hasa := NewHasaIterator(ts, and, Object) hasa := NewHasA(ts, and, graph.Object)
OutputQueryShapeForIterator(hasa, ts, &queryShape) OutputQueryShapeForIterator(hasa, ts, &queryShape)
Convey("It should have seven nodes and three links", func() { Convey("It should have seven nodes and three links", func() {

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
// "Value Comparison" is a unary operator -- a filter across the values in the // "Value Comparison" is a unary operator -- a filter across the values in the
// relevant subiterator. // relevant subiterator.
@ -31,47 +31,44 @@ import (
"log" "log"
"strconv" "strconv"
"strings" "strings"
"github.com/google/cayley/graph"
) )
type ComparisonOperator int type Operator int
const ( const (
kCompareLT ComparisonOperator = iota kCompareLT Operator = iota
kCompareLTE kCompareLTE
kCompareGT kCompareGT
kCompareGTE kCompareGTE
// Why no Equals? Because that's usually an AndIterator. // Why no Equals? Because that's usually an AndIterator.
) )
type ValueComparisonIterator struct { type Comparison struct {
BaseIterator Base
subIt Iterator subIt graph.Iterator
op ComparisonOperator op Operator
comparisonValue interface{} val interface{}
ts TripleStore ts graph.TripleStore
} }
func NewValueComparisonIterator( func NewComparison(sub graph.Iterator, op Operator, val interface{}, ts graph.TripleStore) *Comparison {
subIt Iterator, var vc Comparison
operator ComparisonOperator, BaseInit(&vc.Base)
value interface{}, vc.subIt = sub
ts TripleStore) *ValueComparisonIterator { vc.op = op
vc.val = val
var vc ValueComparisonIterator
BaseIteratorInit(&vc.BaseIterator)
vc.subIt = subIt
vc.op = operator
vc.comparisonValue = value
vc.ts = ts vc.ts = ts
return &vc return &vc
} }
// Here's the non-boilerplate part of the ValueComparison iterator. Given a value // Here's the non-boilerplate part of the ValueComparison iterator. Given a value
// and our operator, determine whether or not we meet the requirement. // and our operator, determine whether or not we meet the requirement.
func (it *ValueComparisonIterator) doComparison(val TSVal) bool { func (it *Comparison) doComparison(val graph.TSVal) bool {
//TODO(barakmich): Implement string comparison. //TODO(barakmich): Implement string comparison.
nodeStr := it.ts.GetNameFor(val) nodeStr := it.ts.GetNameFor(val)
switch cVal := it.comparisonValue.(type) { switch cVal := it.val.(type) {
case int: case int:
cInt := int64(cVal) cInt := int64(cVal)
intVal, err := strconv.ParseInt(nodeStr, 10, 64) intVal, err := strconv.ParseInt(nodeStr, 10, 64)
@ -90,11 +87,11 @@ func (it *ValueComparisonIterator) doComparison(val TSVal) bool {
} }
} }
func (it *ValueComparisonIterator) Close() { func (it *Comparison) Close() {
it.subIt.Close() it.subIt.Close()
} }
func RunIntOp(a int64, op ComparisonOperator, b int64) bool { func RunIntOp(a int64, op Operator, b int64) bool {
switch op { switch op {
case kCompareLT: case kCompareLT:
return a < b return a < b
@ -110,18 +107,18 @@ func RunIntOp(a int64, op ComparisonOperator, b int64) bool {
} }
} }
func (it *ValueComparisonIterator) Reset() { func (it *Comparison) Reset() {
it.subIt.Reset() it.subIt.Reset()
} }
func (it *ValueComparisonIterator) Clone() Iterator { func (it *Comparison) Clone() graph.Iterator {
out := NewValueComparisonIterator(it.subIt.Clone(), it.op, it.comparisonValue, it.ts) out := NewComparison(it.subIt.Clone(), it.op, it.val, it.ts)
out.CopyTagsFrom(it) out.CopyTagsFrom(it)
return out return out
} }
func (it *ValueComparisonIterator) Next() (TSVal, bool) { func (it *Comparison) Next() (graph.TSVal, bool) {
var val TSVal var val graph.TSVal
var ok bool var ok bool
for { for {
val, ok = it.subIt.Next() val, ok = it.subIt.Next()
@ -136,7 +133,7 @@ func (it *ValueComparisonIterator) Next() (TSVal, bool) {
return val, ok return val, ok
} }
func (it *ValueComparisonIterator) NextResult() bool { func (it *Comparison) NextResult() bool {
for { for {
hasNext := it.subIt.NextResult() hasNext := it.subIt.NextResult()
if !hasNext { if !hasNext {
@ -150,7 +147,7 @@ func (it *ValueComparisonIterator) NextResult() bool {
return true return true
} }
func (it *ValueComparisonIterator) Check(val TSVal) bool { func (it *Comparison) Check(val graph.TSVal) bool {
if !it.doComparison(val) { if !it.doComparison(val) {
return false return false
} }
@ -159,16 +156,16 @@ func (it *ValueComparisonIterator) Check(val TSVal) bool {
// If we failed the check, then the subiterator should not contribute to the result // If we failed the check, then the subiterator should not contribute to the result
// set. Otherwise, go ahead and tag it. // set. Otherwise, go ahead and tag it.
func (it *ValueComparisonIterator) TagResults(out *map[string]TSVal) { func (it *Comparison) TagResults(out *map[string]graph.TSVal) {
it.BaseIterator.TagResults(out) it.Base.TagResults(out)
it.subIt.TagResults(out) it.subIt.TagResults(out)
} }
// Registers the value-comparison iterator. // Registers the value-comparison iterator.
func (it *ValueComparisonIterator) Type() string { return "value-comparison" } func (it *Comparison) Type() string { return "value-comparison" }
// Prints the value-comparison and its subiterator. // Prints the value-comparison and its subiterator.
func (it *ValueComparisonIterator) DebugString(indent int) string { func (it *Comparison) DebugString(indent int) string {
return fmt.Sprintf("%s(%s\n%s)", return fmt.Sprintf("%s(%s\n%s)",
strings.Repeat(" ", indent), strings.Repeat(" ", indent),
it.Type(), it.subIt.DebugString(indent+4)) it.Type(), it.subIt.DebugString(indent+4))
@ -177,7 +174,7 @@ func (it *ValueComparisonIterator) DebugString(indent int) string {
// There's nothing to optimize, locally, for a value-comparison iterator. // There's nothing to optimize, locally, for a value-comparison iterator.
// Replace the underlying iterator if need be. // Replace the underlying iterator if need be.
// potentially replace it. // potentially replace it.
func (it *ValueComparisonIterator) Optimize() (Iterator, bool) { func (it *Comparison) Optimize() (graph.Iterator, bool) {
newSub, changed := it.subIt.Optimize() newSub, changed := it.subIt.Optimize()
if changed { if changed {
it.subIt.Close() it.subIt.Close()
@ -188,6 +185,6 @@ func (it *ValueComparisonIterator) Optimize() (Iterator, bool) {
// We're only as expensive as our subiterator. // We're only as expensive as our subiterator.
// Again, optimized value comparison iterators should do better. // Again, optimized value comparison iterators should do better.
func (it *ValueComparisonIterator) GetStats() *IteratorStats { func (it *Comparison) GetStats() *graph.IteratorStats {
return it.subIt.GetStats() return it.subIt.GetStats()
} }

View file

@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package iterator
import ( import (
"testing" "testing"
"github.com/google/cayley/graph"
) )
func SetupMockTripleStore(nameMap map[string]int) *TestTripleStore { func SetupMockTripleStore(nameMap map[string]int) *TestTripleStore {
@ -39,8 +41,8 @@ func SimpleValueTripleStore() *TestTripleStore {
return ts return ts
} }
func BuildFixedIterator() *FixedIterator { func BuildFixedIterator() *Fixed {
fixed := newFixedIterator() fixed := newFixed()
fixed.AddValue(0) fixed.AddValue(0)
fixed.AddValue(1) fixed.AddValue(1)
fixed.AddValue(2) fixed.AddValue(2)
@ -49,7 +51,7 @@ func BuildFixedIterator() *FixedIterator {
return fixed return fixed
} }
func checkIteratorContains(ts TripleStore, it Iterator, expected []string, t *testing.T) { func checkIteratorContains(ts graph.TripleStore, it graph.Iterator, expected []string, t *testing.T) {
var actual []string var actual []string
actual = nil actual = nil
for { for {
@ -82,35 +84,35 @@ func checkIteratorContains(ts TripleStore, it Iterator, expected []string, t *te
func TestWorkingIntValueComparison(t *testing.T) { func TestWorkingIntValueComparison(t *testing.T) {
ts := SimpleValueTripleStore() ts := SimpleValueTripleStore()
fixed := BuildFixedIterator() fixed := BuildFixedIterator()
vc := NewValueComparisonIterator(fixed, kCompareLT, int64(3), ts) vc := NewComparison(fixed, kCompareLT, int64(3), ts)
checkIteratorContains(ts, vc, []string{"0", "1", "2"}, t) checkIteratorContains(ts, vc, []string{"0", "1", "2"}, t)
} }
func TestFailingIntValueComparison(t *testing.T) { func TestFailingIntValueComparison(t *testing.T) {
ts := SimpleValueTripleStore() ts := SimpleValueTripleStore()
fixed := BuildFixedIterator() fixed := BuildFixedIterator()
vc := NewValueComparisonIterator(fixed, kCompareLT, int64(0), ts) vc := NewComparison(fixed, kCompareLT, int64(0), ts)
checkIteratorContains(ts, vc, []string{}, t) checkIteratorContains(ts, vc, []string{}, t)
} }
func TestWorkingGT(t *testing.T) { func TestWorkingGT(t *testing.T) {
ts := SimpleValueTripleStore() ts := SimpleValueTripleStore()
fixed := BuildFixedIterator() fixed := BuildFixedIterator()
vc := NewValueComparisonIterator(fixed, kCompareGT, int64(2), ts) vc := NewComparison(fixed, kCompareGT, int64(2), ts)
checkIteratorContains(ts, vc, []string{"3", "4"}, t) checkIteratorContains(ts, vc, []string{"3", "4"}, t)
} }
func TestWorkingGTE(t *testing.T) { func TestWorkingGTE(t *testing.T) {
ts := SimpleValueTripleStore() ts := SimpleValueTripleStore()
fixed := BuildFixedIterator() fixed := BuildFixedIterator()
vc := NewValueComparisonIterator(fixed, kCompareGTE, int64(2), ts) vc := NewComparison(fixed, kCompareGTE, int64(2), ts)
checkIteratorContains(ts, vc, []string{"2", "3", "4"}, t) checkIteratorContains(ts, vc, []string{"2", "3", "4"}, t)
} }
func TestVCICheck(t *testing.T) { func TestVCICheck(t *testing.T) {
ts := SimpleValueTripleStore() ts := SimpleValueTripleStore()
fixed := BuildFixedIterator() fixed := BuildFixedIterator()
vc := NewValueComparisonIterator(fixed, kCompareGTE, int64(2), ts) vc := NewComparison(fixed, kCompareGTE, int64(2), ts)
if vc.Check(1) { if vc.Check(1) {
t.Error("1 is less than 2, should be GTE") t.Error("1 is less than 2, should be GTE")
} }

View file

@ -19,25 +19,26 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/syndtr/goleveldb/leveldb/iterator" ldbit "github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/opt"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
type AllIterator struct { type AllIterator struct {
graph.BaseIterator iterator.Base
prefix []byte prefix []byte
dir graph.Direction dir graph.Direction
open bool open bool
it iterator.Iterator it ldbit.Iterator
ts *TripleStore ts *TripleStore
ro *opt.ReadOptions ro *opt.ReadOptions
} }
func NewAllIterator(prefix string, d graph.Direction, ts *TripleStore) *AllIterator { func NewAllIterator(prefix string, d graph.Direction, ts *TripleStore) *AllIterator {
var it AllIterator var it AllIterator
graph.BaseIteratorInit(&it.BaseIterator) iterator.BaseInit(&it.Base)
it.ro = &opt.ReadOptions{} it.ro = &opt.ReadOptions{}
it.ro.DontFillCache = true it.ro.DontFillCache = true
it.it = ts.db.NewIterator(nil, it.ro) it.it = ts.db.NewIterator(nil, it.ro)

View file

@ -19,19 +19,20 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/syndtr/goleveldb/leveldb/iterator" ldbit "github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/opt"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
type Iterator struct { type Iterator struct {
graph.BaseIterator iterator.Base
nextPrefix []byte nextPrefix []byte
checkId []byte checkId []byte
dir graph.Direction dir graph.Direction
open bool open bool
it iterator.Iterator it ldbit.Iterator
ts *TripleStore ts *TripleStore
ro *opt.ReadOptions ro *opt.ReadOptions
originalPrefix string originalPrefix string
@ -39,7 +40,7 @@ type Iterator struct {
func NewIterator(prefix string, d graph.Direction, value graph.TSVal, ts *TripleStore) *Iterator { func NewIterator(prefix string, d graph.Direction, value graph.TSVal, ts *TripleStore) *Iterator {
var it Iterator var it Iterator
graph.BaseIteratorInit(&it.BaseIterator) iterator.BaseInit(&it.Base)
it.checkId = value.([]byte) it.checkId = value.([]byte)
it.dir = d it.dir = d
it.originalPrefix = prefix it.originalPrefix = prefix

View file

@ -23,21 +23,22 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func makeTripleSet() []*graph.Triple { func makeTripleSet() []*graph.Triple {
tripleSet := []*graph.Triple{ tripleSet := []*graph.Triple{
graph.MakeTriple("A", "follows", "B", ""), {"A", "follows", "B", ""},
graph.MakeTriple("C", "follows", "B", ""), {"C", "follows", "B", ""},
graph.MakeTriple("C", "follows", "D", ""), {"C", "follows", "D", ""},
graph.MakeTriple("D", "follows", "B", ""), {"D", "follows", "B", ""},
graph.MakeTriple("B", "follows", "F", ""), {"B", "follows", "F", ""},
graph.MakeTriple("F", "follows", "G", ""), {"F", "follows", "G", ""},
graph.MakeTriple("D", "follows", "G", ""), {"D", "follows", "G", ""},
graph.MakeTriple("E", "follows", "F", ""), {"E", "follows", "F", ""},
graph.MakeTriple("B", "status", "cool", "status_graph"), {"B", "status", "cool", "status_graph"},
graph.MakeTriple("D", "status", "cool", "status_graph"), {"D", "status", "cool", "status_graph"},
graph.MakeTriple("G", "status", "cool", "status_graph"), {"G", "status", "cool", "status_graph"},
} }
return tripleSet return tripleSet
} }
@ -49,7 +50,7 @@ func extractTripleFromIterator(ts graph.TripleStore, it graph.Iterator) []string
if !ok { if !ok {
break break
} }
output = append(output, ts.GetTriple(val).ToString()) output = append(output, ts.GetTriple(val).String())
} }
return output return output
} }
@ -111,7 +112,7 @@ func TestLoadDatabase(t *testing.T) {
ts = NewTripleStore(tmpDir, nil) ts = NewTripleStore(tmpDir, nil)
Convey("Can load a single triple", func() { Convey("Can load a single triple", func() {
ts.AddTriple(graph.MakeTriple("Something", "points_to", "Something Else", "context")) ts.AddTriple(&graph.Triple{"Something", "points_to", "Something Else", "context"})
So(ts.GetNameFor(ts.GetIdFor("Something")), ShouldEqual, "Something") So(ts.GetNameFor(ts.GetIdFor("Something")), ShouldEqual, "Something")
So(ts.Size(), ShouldEqual, 1) So(ts.Size(), ShouldEqual, 1)
}) })
@ -123,7 +124,7 @@ func TestLoadDatabase(t *testing.T) {
So(ts.GetSizeFor(ts.GetIdFor("B")), ShouldEqual, 5) So(ts.GetSizeFor(ts.GetIdFor("B")), ShouldEqual, 5)
Convey("Can delete triples", func() { Convey("Can delete triples", func() {
ts.RemoveTriple(graph.MakeTriple("A", "follows", "B", "")) ts.RemoveTriple(&graph.Triple{"A", "follows", "B", ""})
So(ts.Size(), ShouldEqual, 10) So(ts.Size(), ShouldEqual, 10)
So(ts.GetSizeFor(ts.GetIdFor("B")), ShouldEqual, 4) So(ts.GetSizeFor(ts.GetIdFor("B")), ShouldEqual, 4)
}) })
@ -220,9 +221,9 @@ func TestIterator(t *testing.T) {
set := makeTripleSet() set := makeTripleSet()
var string_set []string var string_set []string
for _, t := range set { for _, t := range set {
string_set = append(string_set, t.ToString()) string_set = append(string_set, t.String())
} }
So(triple.ToString(), ShouldBeIn, string_set) So(triple.String(), ShouldBeIn, string_set)
}) })
Reset(func() { Reset(func() {
@ -252,8 +253,8 @@ func TestSetIterator(t *testing.T) {
Convey("Containing the right things", func() { Convey("Containing the right things", func() {
expected := []string{ expected := []string{
graph.MakeTriple("C", "follows", "B", "").ToString(), (&graph.Triple{"C", "follows", "B", ""}).String(),
graph.MakeTriple("C", "follows", "D", "").ToString(), (&graph.Triple{"C", "follows", "D", ""}).String(),
} }
actual := extractTripleFromIterator(ts, it) actual := extractTripleFromIterator(ts, it)
sort.Strings(actual) sort.Strings(actual)
@ -262,13 +263,13 @@ func TestSetIterator(t *testing.T) {
}) })
Convey("And checkable", func() { Convey("And checkable", func() {
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(ts.GetTriplesAllIterator()) and.AddSubIterator(ts.GetTriplesAllIterator())
and.AddSubIterator(it) and.AddSubIterator(it)
expected := []string{ expected := []string{
graph.MakeTriple("C", "follows", "B", "").ToString(), (&graph.Triple{"C", "follows", "B", ""}).String(),
graph.MakeTriple("C", "follows", "D", "").ToString(), (&graph.Triple{"C", "follows", "D", ""}).String(),
} }
actual := extractTripleFromIterator(ts, and) actual := extractTripleFromIterator(ts, and)
sort.Strings(actual) sort.Strings(actual)
@ -286,8 +287,8 @@ func TestSetIterator(t *testing.T) {
Convey("Containing the right things", func() { Convey("Containing the right things", func() {
expected := []string{ expected := []string{
graph.MakeTriple("B", "follows", "F", "").ToString(), (&graph.Triple{"B", "follows", "F", ""}).String(),
graph.MakeTriple("E", "follows", "F", "").ToString(), (&graph.Triple{"E", "follows", "F", ""}).String(),
} }
actual := extractTripleFromIterator(ts, it) actual := extractTripleFromIterator(ts, it)
sort.Strings(actual) sort.Strings(actual)
@ -296,12 +297,12 @@ func TestSetIterator(t *testing.T) {
}) })
Convey("Mutually and-checkable", func() { Convey("Mutually and-checkable", func() {
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(ts.GetTripleIterator(graph.Subject, ts.GetIdFor("B"))) and.AddSubIterator(ts.GetTripleIterator(graph.Subject, ts.GetIdFor("B")))
and.AddSubIterator(it) and.AddSubIterator(it)
expected := []string{ expected := []string{
graph.MakeTriple("B", "follows", "F", "").ToString(), (&graph.Triple{"B", "follows", "F", ""}).String(),
} }
actual := extractTripleFromIterator(ts, and) actual := extractTripleFromIterator(ts, and)
sort.Strings(actual) sort.Strings(actual)
@ -316,9 +317,9 @@ func TestSetIterator(t *testing.T) {
Convey("Containing the right things", func() { Convey("Containing the right things", func() {
expected := []string{ expected := []string{
graph.MakeTriple("B", "status", "cool", "status_graph").ToString(), (&graph.Triple{"B", "status", "cool", "status_graph"}).String(),
graph.MakeTriple("D", "status", "cool", "status_graph").ToString(), (&graph.Triple{"D", "status", "cool", "status_graph"}).String(),
graph.MakeTriple("G", "status", "cool", "status_graph").ToString(), (&graph.Triple{"G", "status", "cool", "status_graph"}).String(),
} }
actual := extractTripleFromIterator(ts, it) actual := extractTripleFromIterator(ts, it)
sort.Strings(actual) sort.Strings(actual)
@ -333,9 +334,9 @@ func TestSetIterator(t *testing.T) {
Convey("Containing the right things", func() { Convey("Containing the right things", func() {
expected := []string{ expected := []string{
graph.MakeTriple("B", "status", "cool", "status_graph").ToString(), (&graph.Triple{"B", "status", "cool", "status_graph"}).String(),
graph.MakeTriple("D", "status", "cool", "status_graph").ToString(), (&graph.Triple{"D", "status", "cool", "status_graph"}).String(),
graph.MakeTriple("G", "status", "cool", "status_graph").ToString(), (&graph.Triple{"G", "status", "cool", "status_graph"}).String(),
} }
actual := extractTripleFromIterator(ts, it) actual := extractTripleFromIterator(ts, it)
sort.Strings(actual) sort.Strings(actual)
@ -344,26 +345,26 @@ func TestSetIterator(t *testing.T) {
}) })
Convey("Can be cross-checked", func() { Convey("Can be cross-checked", func() {
and := graph.NewAndIterator() and := iterator.NewAnd()
// Order is important // Order is important
and.AddSubIterator(ts.GetTripleIterator(graph.Subject, ts.GetIdFor("B"))) and.AddSubIterator(ts.GetTripleIterator(graph.Subject, ts.GetIdFor("B")))
and.AddSubIterator(it) and.AddSubIterator(it)
expected := []string{ expected := []string{
graph.MakeTriple("B", "status", "cool", "status_graph").ToString(), (&graph.Triple{"B", "status", "cool", "status_graph"}).String(),
} }
actual := extractTripleFromIterator(ts, and) actual := extractTripleFromIterator(ts, and)
So(actual, ShouldResemble, expected) So(actual, ShouldResemble, expected)
}) })
Convey("Can check against other iterators", func() { Convey("Can check against other iterators", func() {
and := graph.NewAndIterator() and := iterator.NewAnd()
// Order is important // Order is important
and.AddSubIterator(it) and.AddSubIterator(it)
and.AddSubIterator(ts.GetTripleIterator(graph.Subject, ts.GetIdFor("B"))) and.AddSubIterator(ts.GetTripleIterator(graph.Subject, ts.GetIdFor("B")))
expected := []string{ expected := []string{
graph.MakeTriple("B", "status", "cool", "status_graph").ToString(), (&graph.Triple{"B", "status", "cool", "status_graph"}).String(),
} }
actual := extractTripleFromIterator(ts, and) actual := extractTripleFromIterator(ts, and)
So(actual, ShouldResemble, expected) So(actual, ShouldResemble, expected)
@ -397,10 +398,10 @@ func TestOptimize(t *testing.T) {
ts.AddTripleSet(makeTripleSet()) ts.AddTripleSet(makeTripleSet())
Convey("With an linksto-fixed pair", func() { Convey("With an linksto-fixed pair", func() {
fixed := ts.MakeFixed() fixed := ts.FixedIterator()
fixed.AddValue(ts.GetIdFor("F")) fixed.AddValue(ts.GetIdFor("F"))
fixed.AddTag("internal") fixed.AddTag("internal")
lto = graph.NewLinksToIterator(ts, fixed, graph.Object) lto = iterator.NewLinksTo(ts, fixed, graph.Object)
Convey("Creates an appropriate iterator", func() { Convey("Creates an appropriate iterator", func() {
oldIt := lto.Clone() oldIt := lto.Clone()

View file

@ -29,6 +29,7 @@ import (
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
const ( const (
@ -143,7 +144,7 @@ func (ts *TripleStore) AddTriple(t *graph.Triple) {
ts.buildWrite(batch, t) ts.buildWrite(batch, t)
err := ts.db.Write(batch, ts.writeopts) err := ts.db.Write(batch, ts.writeopts)
if err != nil { if err != nil {
glog.Errorf("Couldn't write to DB for triple %s", t.ToString()) glog.Errorf("Couldn't write to DB for triple %s", t)
return return
} }
ts.size++ ts.size++
@ -180,7 +181,7 @@ func (ts *TripleStore) RemoveTriple(t *graph.Triple) {
} }
err = ts.db.Write(batch, nil) err = ts.db.Write(batch, nil)
if err != nil { if err != nil {
glog.Errorf("Couldn't delete triple %s", t.ToString()) glog.Errorf("Couldn't delete triple %s", t)
return return
} }
ts.size-- ts.size--
@ -189,7 +190,7 @@ func (ts *TripleStore) RemoveTriple(t *graph.Triple) {
func (ts *TripleStore) buildTripleWrite(batch *leveldb.Batch, t *graph.Triple) { func (ts *TripleStore) buildTripleWrite(batch *leveldb.Batch, t *graph.Triple) {
bytes, err := json.Marshal(*t) bytes, err := json.Marshal(*t)
if err != nil { if err != nil {
glog.Errorf("Couldn't write to buffer for triple %s\n %s\n", t.ToString(), err) glog.Errorf("Couldn't write to buffer for triple %s\n %s\n", t, err)
return return
} }
batch.Put(ts.createKeyFor(spo, t), bytes) batch.Put(ts.createKeyFor(spo, t), bytes)
@ -439,6 +440,6 @@ func compareBytes(a, b graph.TSVal) bool {
return bytes.Equal(a.([]uint8), b.([]uint8)) return bytes.Equal(a.([]uint8), b.([]uint8))
} }
func (ts *TripleStore) MakeFixed() *graph.FixedIterator { func (ts *TripleStore) FixedIterator() graph.FixedIterator {
return graph.NewFixedIteratorWithCompare(compareBytes) return iterator.NewFixedIteratorWithCompare(compareBytes)
} }

View file

@ -16,18 +16,19 @@ package leveldb
import ( import (
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) { func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) {
switch it.Type() { switch it.Type() {
case "linksto": case "linksto":
return ts.optimizeLinksTo(it.(*graph.LinksToIterator)) return ts.optimizeLinksTo(it.(*iterator.LinksTo))
} }
return it, false return it, false
} }
func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bool) {
subs := it.GetSubIterators() subs := it.GetSubIterators()
if len(subs) != 1 { if len(subs) != 1 {
return it, false return it, false

View file

@ -16,22 +16,23 @@ package memstore
import ( import (
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
type AllIterator struct { type AllIterator struct {
graph.Int64AllIterator iterator.Int64
ts *TripleStore ts *TripleStore
} }
func NewMemstoreAllIterator(ts *TripleStore) *AllIterator { func NewMemstoreAllIterator(ts *TripleStore) *AllIterator {
var out AllIterator var out AllIterator
out.Int64AllIterator = *graph.NewInt64AllIterator(1, ts.idCounter-1) out.Int64 = *iterator.NewInt64(1, ts.idCounter-1)
out.ts = ts out.ts = ts
return &out return &out
} }
func (it *AllIterator) Next() (graph.TSVal, bool) { func (it *AllIterator) Next() (graph.TSVal, bool) {
next, out := it.Int64AllIterator.Next() next, out := it.Int64.Next()
if !out { if !out {
return next, out return next, out
} }

View file

@ -22,10 +22,11 @@ import (
"github.com/petar/GoLLRB/llrb" "github.com/petar/GoLLRB/llrb"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
type Iterator struct { type Iterator struct {
graph.BaseIterator iterator.Base
tree *llrb.LLRB tree *llrb.LLRB
data string data string
isRunning bool isRunning bool
@ -53,7 +54,7 @@ func IterateOne(tree *llrb.LLRB, last Int64) Int64 {
func NewLlrbIterator(tree *llrb.LLRB, data string) *Iterator { func NewLlrbIterator(tree *llrb.LLRB, data string) *Iterator {
var it Iterator var it Iterator
graph.BaseIteratorInit(&it.BaseIterator) iterator.BaseInit(&it.Base)
it.tree = tree it.tree = tree
it.iterLast = Int64(-1) it.iterLast = Int64(-1)
it.data = data it.data = data

View file

@ -30,16 +30,16 @@ import "github.com/google/cayley/graph"
func MakeTestingMemstore() *TripleStore { func MakeTestingMemstore() *TripleStore {
ts := NewTripleStore() ts := NewTripleStore()
ts.AddTriple(graph.MakeTriple("A", "follows", "B", "")) ts.AddTriple(&graph.Triple{"A", "follows", "B", ""})
ts.AddTriple(graph.MakeTriple("C", "follows", "B", "")) ts.AddTriple(&graph.Triple{"C", "follows", "B", ""})
ts.AddTriple(graph.MakeTriple("C", "follows", "D", "")) ts.AddTriple(&graph.Triple{"C", "follows", "D", ""})
ts.AddTriple(graph.MakeTriple("D", "follows", "B", "")) ts.AddTriple(&graph.Triple{"D", "follows", "B", ""})
ts.AddTriple(graph.MakeTriple("B", "follows", "F", "")) ts.AddTriple(&graph.Triple{"B", "follows", "F", ""})
ts.AddTriple(graph.MakeTriple("F", "follows", "G", "")) ts.AddTriple(&graph.Triple{"F", "follows", "G", ""})
ts.AddTriple(graph.MakeTriple("D", "follows", "G", "")) ts.AddTriple(&graph.Triple{"D", "follows", "G", ""})
ts.AddTriple(graph.MakeTriple("E", "follows", "F", "")) ts.AddTriple(&graph.Triple{"E", "follows", "F", ""})
ts.AddTriple(graph.MakeTriple("B", "status", "cool", "status_graph")) ts.AddTriple(&graph.Triple{"B", "status", "cool", "status_graph"})
ts.AddTriple(graph.MakeTriple("D", "status", "cool", "status_graph")) ts.AddTriple(&graph.Triple{"D", "status", "cool", "status_graph"})
ts.AddTriple(graph.MakeTriple("G", "status", "cool", "status_graph")) ts.AddTriple(&graph.Triple{"G", "status", "cool", "status_graph"})
return ts return ts
} }

View file

@ -19,6 +19,7 @@ import (
"github.com/barakmich/glog" "github.com/barakmich/glog"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
"github.com/petar/GoLLRB/llrb" "github.com/petar/GoLLRB/llrb"
) )
@ -226,7 +227,7 @@ func (ts *TripleStore) GetTripleIterator(d graph.Direction, value graph.TSVal) g
if ok { if ok {
return NewLlrbIterator(index, data) return NewLlrbIterator(index, data)
} }
return &graph.NullIterator{} return &iterator.Null{}
} }
func (ts *TripleStore) Size() int64 { func (ts *TripleStore) Size() int64 {
@ -238,7 +239,7 @@ func (ts *TripleStore) DebugPrint() {
if i == 0 { if i == 0 {
continue continue
} }
glog.V(2).Infoln("%d: %s", i, t.ToString()) glog.V(2).Infoln("%d: %s", i, t)
} }
} }
@ -251,11 +252,11 @@ func (ts *TripleStore) GetNameFor(id graph.TSVal) string {
} }
func (ts *TripleStore) GetTriplesAllIterator() graph.Iterator { func (ts *TripleStore) GetTriplesAllIterator() graph.Iterator {
return graph.NewInt64AllIterator(0, ts.Size()) return iterator.NewInt64(0, ts.Size())
} }
func (ts *TripleStore) MakeFixed() *graph.FixedIterator { func (ts *TripleStore) FixedIterator() graph.FixedIterator {
return graph.NewFixedIteratorWithCompare(graph.BasicEquality) return iterator.NewFixedIteratorWithCompare(iterator.BasicEquality)
} }
func (ts *TripleStore) GetTripleDirection(val graph.TSVal, d graph.Direction) graph.TSVal { func (ts *TripleStore) GetTripleDirection(val graph.TSVal, d graph.Direction) graph.TSVal {

View file

@ -16,18 +16,19 @@ package memstore
import ( import (
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) { func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) {
switch it.Type() { switch it.Type() {
case "linksto": case "linksto":
return ts.optimizeLinksTo(it.(*graph.LinksToIterator)) return ts.optimizeLinksTo(it.(*iterator.LinksTo))
} }
return it, false return it, false
} }
func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bool) {
subs := it.GetSubIterators() subs := it.GetSubIterators()
if len(subs) != 1 { if len(subs) != 1 {
return it, false return it, false

View file

@ -21,6 +21,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func TestMemstore(t *testing.T) { func TestMemstore(t *testing.T) {
@ -38,19 +39,19 @@ func TestMemstore(t *testing.T) {
func TestIteratorsAndNextResultOrderA(t *testing.T) { func TestIteratorsAndNextResultOrderA(t *testing.T) {
ts := MakeTestingMemstore() ts := MakeTestingMemstore()
fixed := ts.MakeFixed() fixed := ts.FixedIterator()
fixed.AddValue(ts.GetIdFor("C")) fixed.AddValue(ts.GetIdFor("C"))
all := ts.GetNodesAllIterator() all := ts.GetNodesAllIterator()
lto := graph.NewLinksToIterator(ts, all, graph.Object) lto := iterator.NewLinksTo(ts, all, graph.Object)
innerAnd := graph.NewAndIterator() innerAnd := iterator.NewAnd()
fixed2 := ts.MakeFixed() fixed2 := ts.FixedIterator()
fixed2.AddValue(ts.GetIdFor("follows")) fixed2.AddValue(ts.GetIdFor("follows"))
lto2 := graph.NewLinksToIterator(ts, fixed2, graph.Predicate) lto2 := iterator.NewLinksTo(ts, fixed2, graph.Predicate)
innerAnd.AddSubIterator(lto2) innerAnd.AddSubIterator(lto2)
innerAnd.AddSubIterator(lto) innerAnd.AddSubIterator(lto)
hasa := graph.NewHasaIterator(ts, innerAnd, graph.Subject) hasa := iterator.NewHasA(ts, innerAnd, graph.Subject)
outerAnd := graph.NewAndIterator() outerAnd := iterator.NewAnd()
outerAnd.AddSubIterator(fixed) outerAnd.AddSubIterator(fixed)
outerAnd.AddSubIterator(hasa) outerAnd.AddSubIterator(hasa)
val, ok := outerAnd.Next() val, ok := outerAnd.Next()
@ -96,9 +97,9 @@ func CompareStringSlices(t *testing.T, expected []string, actual []string) {
func TestLinksToOptimization(t *testing.T) { func TestLinksToOptimization(t *testing.T) {
ts := MakeTestingMemstore() ts := MakeTestingMemstore()
fixed := ts.MakeFixed() fixed := ts.FixedIterator()
fixed.AddValue(ts.GetIdFor("cool")) fixed.AddValue(ts.GetIdFor("cool"))
lto := graph.NewLinksToIterator(ts, fixed, graph.Object) lto := iterator.NewLinksTo(ts, fixed, graph.Object)
lto.AddTag("foo") lto.AddTag("foo")
newIt, changed := lto.Optimize() newIt, changed := lto.Optimize()
if !changed { if !changed {
@ -119,17 +120,17 @@ func TestLinksToOptimization(t *testing.T) {
func TestRemoveTriple(t *testing.T) { func TestRemoveTriple(t *testing.T) {
ts := MakeTestingMemstore() ts := MakeTestingMemstore()
ts.RemoveTriple(graph.MakeTriple("E", "follows", "F", "")) ts.RemoveTriple(&graph.Triple{"E", "follows", "F", ""})
fixed := ts.MakeFixed() fixed := ts.FixedIterator()
fixed.AddValue(ts.GetIdFor("E")) fixed.AddValue(ts.GetIdFor("E"))
lto := graph.NewLinksToIterator(ts, fixed, graph.Subject) lto := iterator.NewLinksTo(ts, fixed, graph.Subject)
fixed2 := ts.MakeFixed() fixed2 := ts.FixedIterator()
fixed2.AddValue(ts.GetIdFor("follows")) fixed2.AddValue(ts.GetIdFor("follows"))
lto2 := graph.NewLinksToIterator(ts, fixed2, graph.Predicate) lto2 := iterator.NewLinksTo(ts, fixed2, graph.Predicate)
innerAnd := graph.NewAndIterator() innerAnd := iterator.NewAnd()
innerAnd.AddSubIterator(lto2) innerAnd.AddSubIterator(lto2)
innerAnd.AddSubIterator(lto) innerAnd.AddSubIterator(lto)
hasa := graph.NewHasaIterator(ts, innerAnd, graph.Object) hasa := iterator.NewHasA(ts, innerAnd, graph.Object)
newIt, _ := hasa.Optimize() newIt, _ := hasa.Optimize()
_, ok := newIt.Next() _, ok := newIt.Next()
if ok { if ok {

View file

@ -23,10 +23,11 @@ import (
"labix.org/v2/mgo/bson" "labix.org/v2/mgo/bson"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
type Iterator struct { type Iterator struct {
graph.BaseIterator iterator.Base
ts *TripleStore ts *TripleStore
dir graph.Direction dir graph.Direction
iter *mgo.Iter iter *mgo.Iter
@ -40,7 +41,7 @@ type Iterator struct {
func NewIterator(ts *TripleStore, collection string, d graph.Direction, val graph.TSVal) *Iterator { func NewIterator(ts *TripleStore, collection string, d graph.Direction, val graph.TSVal) *Iterator {
var m Iterator var m Iterator
graph.BaseIteratorInit(&m.BaseIterator) iterator.BaseInit(&m.Base)
m.name = ts.GetNameFor(val) m.name = ts.GetNameFor(val)
m.collection = collection m.collection = collection

View file

@ -25,6 +25,7 @@ import (
"github.com/barakmich/glog" "github.com/barakmich/glog"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
const DefaultDBName = "cayley" const DefaultDBName = "cayley"
@ -214,11 +215,12 @@ func (ts *TripleStore) GetTriple(val graph.TSVal) *graph.Triple {
if err != nil { if err != nil {
log.Println("Error: Couldn't retrieve triple", val.(string), err) log.Println("Error: Couldn't retrieve triple", val.(string), err)
} }
return graph.MakeTriple( return &graph.Triple{
bsonDoc["Sub"].(string), bsonDoc["Subject"].(string),
bsonDoc["Pred"].(string), bsonDoc["Predicate"].(string),
bsonDoc["Obj"].(string), bsonDoc["Object"].(string),
bsonDoc["Provenance"].(string)) bsonDoc["Provenance"].(string),
}
} }
func (ts *TripleStore) GetTripleIterator(d graph.Direction, val graph.TSVal) graph.Iterator { func (ts *TripleStore) GetTripleIterator(d graph.Direction, val graph.TSVal) graph.Iterator {
@ -264,8 +266,8 @@ func compareStrings(a, b graph.TSVal) bool {
return a.(string) == b.(string) return a.(string) == b.(string)
} }
func (ts *TripleStore) MakeFixed() *graph.FixedIterator { func (ts *TripleStore) FixedIterator() graph.FixedIterator {
return graph.NewFixedIteratorWithCompare(compareStrings) return iterator.NewFixedIteratorWithCompare(compareStrings)
} }
func (ts *TripleStore) Close() { func (ts *TripleStore) Close() {
@ -303,9 +305,9 @@ func (ts *TripleStore) BulkLoad(t_chan chan *graph.Triple) {
var p_key = this["_id"].slice(len / 4, 2 * len / 4) var p_key = this["_id"].slice(len / 4, 2 * len / 4)
var o_key = this["_id"].slice(2 * len / 4, 3 * len / 4) var o_key = this["_id"].slice(2 * len / 4, 3 * len / 4)
var c_key = this["_id"].slice(3 * len / 4) var c_key = this["_id"].slice(3 * len / 4)
emit(s_key, {"_id": s_key, "Name" : this.Sub, "Size" : 1}) emit(s_key, {"_id": s_key, "Name" : this.Subject, "Size" : 1})
emit(p_key, {"_id": p_key, "Name" : this.Pred, "Size" : 1}) emit(p_key, {"_id": p_key, "Name" : this.Predicate, "Size" : 1})
emit(o_key, {"_id": o_key, "Name" : this.Obj, "Size" : 1}) emit(o_key, {"_id": o_key, "Name" : this.Object, "Size" : 1})
if (this.Provenance != "") { if (this.Provenance != "") {
emit(c_key, {"_id": c_key, "Name" : this.Provenance, "Size" : 1}) emit(c_key, {"_id": c_key, "Name" : this.Provenance, "Size" : 1})
} }

View file

@ -16,18 +16,19 @@ package mongo
import ( import (
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) { func (ts *TripleStore) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) {
switch it.Type() { switch it.Type() {
case "linksto": case "linksto":
return ts.optimizeLinksTo(it.(*graph.LinksToIterator)) return ts.optimizeLinksTo(it.(*iterator.LinksTo))
} }
return it, false return it, false
} }
func (ts *TripleStore) optimizeLinksTo(it *graph.LinksToIterator) (graph.Iterator, bool) { func (ts *TripleStore) optimizeLinksTo(it *iterator.LinksTo) (graph.Iterator, bool) {
subs := it.GetSubIterators() subs := it.GetSubIterators()
if len(subs) != 1 { if len(subs) != 1 {
return it, false return it, false

View file

@ -25,11 +25,11 @@ func NewResultTree(result TSVal) *ResultTree {
return &ResultTree{result: result} return &ResultTree{result: result}
} }
func (t *ResultTree) ToString() string { func (t *ResultTree) String() string {
base := fmt.Sprintf("(%d", t.result) base := fmt.Sprintf("(%d", t.result)
if len(t.subtrees) != 0 { if len(t.subtrees) != 0 {
for _, sub := range t.subtrees { for _, sub := range t.subtrees {
base += fmt.Sprintf(" %s", sub.ToString()) base += fmt.Sprintf(" %s", sub)
} }
} }
base += ")" base += ")"
@ -48,11 +48,11 @@ func StringResultTreeEvaluator(it Iterator) string {
if !ok { if !ok {
break break
} }
out += it.GetResultTree().ToString() out += it.GetResultTree().String()
out += "\n" out += "\n"
for it.NextResult() == true { for it.NextResult() == true {
out += " " out += " "
out += it.GetResultTree().ToString() out += it.GetResultTree().String()
out += "\n" out += "\n"
} }
} }

View file

@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package graph package graph_test
import ( import (
"testing" "testing"
. "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func TestSingleIterator(t *testing.T) { func TestSingleIterator(t *testing.T) {
all := NewInt64AllIterator(1, 3) all := iterator.NewInt64(1, 3)
result := StringResultTreeEvaluator(all) result := StringResultTreeEvaluator(all)
expected := "(1)\n(2)\n(3)\n" expected := "(1)\n(2)\n(3)\n"
if expected != result { if expected != result {
@ -28,9 +31,9 @@ func TestSingleIterator(t *testing.T) {
} }
func TestAndIterator(t *testing.T) { func TestAndIterator(t *testing.T) {
all1 := NewInt64AllIterator(1, 3) all1 := iterator.NewInt64(1, 3)
all2 := NewInt64AllIterator(3, 5) all2 := iterator.NewInt64(3, 5)
and := NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(all1) and.AddSubIterator(all1)
and.AddSubIterator(all2) and.AddSubIterator(all2)

View file

@ -18,6 +18,7 @@ import (
"github.com/badgerodon/peg" "github.com/badgerodon/peg"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func BuildIteratorTreeForQuery(ts graph.TripleStore, query string) graph.Iterator { func BuildIteratorTreeForQuery(ts graph.TripleStore, query string) graph.Iterator {
@ -195,7 +196,7 @@ func buildIteratorTree(tree *peg.ExpressionTree, ts graph.TripleStore) graph.Ite
if tree.Children[0].Children[0].Name == "ColonIdentifier" { if tree.Children[0].Children[0].Name == "ColonIdentifier" {
n = nodeID[1:] n = nodeID[1:]
} }
fixed := ts.MakeFixed() fixed := ts.FixedIterator()
fixed.AddValue(ts.GetIdFor(n)) fixed.AddValue(ts.GetIdFor(n))
out = fixed out = fixed
} }
@ -207,11 +208,11 @@ func buildIteratorTree(tree *peg.ExpressionTree, ts graph.TripleStore) graph.Ite
i++ i++
} }
it := buildIteratorTree(tree.Children[i], ts) it := buildIteratorTree(tree.Children[i], ts)
lto := graph.NewLinksToIterator(ts, it, graph.Predicate) lto := iterator.NewLinksTo(ts, it, graph.Predicate)
return lto return lto
case "RootConstraint": case "RootConstraint":
constraintCount := 0 constraintCount := 0
and := graph.NewAndIterator() and := iterator.NewAnd()
for _, c := range tree.Children { for _, c := range tree.Children {
switch c.Name { switch c.Name {
case "NodeIdentifier": case "NodeIdentifier":
@ -227,10 +228,10 @@ func buildIteratorTree(tree *peg.ExpressionTree, ts graph.TripleStore) graph.Ite
} }
return and return and
case "Constraint": case "Constraint":
var hasa *graph.HasaIterator var hasa *iterator.HasA
topLevelDir := graph.Subject topLevelDir := graph.Subject
subItDir := graph.Object subItDir := graph.Object
subAnd := graph.NewAndIterator() subAnd := iterator.NewAnd()
isOptional := false isOptional := false
for _, c := range tree.Children { for _, c := range tree.Children {
switch c.Name { switch c.Name {
@ -251,21 +252,21 @@ func buildIteratorTree(tree *peg.ExpressionTree, ts graph.TripleStore) graph.Ite
fallthrough fallthrough
case "RootConstraint": case "RootConstraint":
it := buildIteratorTree(c, ts) it := buildIteratorTree(c, ts)
l := graph.NewLinksToIterator(ts, it, subItDir) l := iterator.NewLinksTo(ts, it, subItDir)
subAnd.AddSubIterator(l) subAnd.AddSubIterator(l)
continue continue
default: default:
continue continue
} }
} }
hasa = graph.NewHasaIterator(ts, subAnd, topLevelDir) hasa = iterator.NewHasA(ts, subAnd, topLevelDir)
if isOptional { if isOptional {
optional := graph.NewOptionalIterator(hasa) optional := iterator.NewOptional(hasa)
return optional return optional
} }
return hasa return hasa
default: default:
return &graph.NullIterator{} return &iterator.Null{}
} }
panic("Not reached") panic("Not reached")
} }

View file

@ -40,7 +40,7 @@ func TestParseSexpWithMemstore(t *testing.T) {
}) })
Convey("It should get a single triple linkage", func() { Convey("It should get a single triple linkage", func() {
ts.AddTriple(graph.MakeTriple("i", "can", "win", "")) ts.AddTriple(&graph.Triple{"i", "can", "win", ""})
query := "($a (:can \"win\"))" query := "($a (:can \"win\"))"
So(len(query), ShouldEqual, 17) So(len(query), ShouldEqual, 17)
it := BuildIteratorTreeForQuery(ts, query) it := BuildIteratorTreeForQuery(ts, query)
@ -51,7 +51,7 @@ func TestParseSexpWithMemstore(t *testing.T) {
}) })
Convey("It can get an internal linkage", func() { Convey("It can get an internal linkage", func() {
ts.AddTriple(graph.MakeTriple("i", "can", "win", "")) ts.AddTriple(&graph.Triple{"i", "can", "win", ""})
query := "(\"i\" (:can $a))" query := "(\"i\" (:can $a))"
it := BuildIteratorTreeForQuery(ts, query) it := BuildIteratorTreeForQuery(ts, query)
So(it.Type(), ShouldEqual, "and") So(it.Type(), ShouldEqual, "and")
@ -65,8 +65,8 @@ func TestParseSexpWithMemstore(t *testing.T) {
func TestTreeConstraintParse(t *testing.T) { func TestTreeConstraintParse(t *testing.T) {
ts := memstore.NewTripleStore() ts := memstore.NewTripleStore()
ts.AddTriple(graph.MakeTriple("i", "like", "food", "")) ts.AddTriple(&graph.Triple{"i", "like", "food", ""})
ts.AddTriple(graph.MakeTriple("food", "is", "good", "")) ts.AddTriple(&graph.Triple{"food", "is", "good", ""})
query := "(\"i\"\n" + query := "(\"i\"\n" +
"(:like\n" + "(:like\n" +
"($a (:is :good))))" "($a (:is :good))))"
@ -85,8 +85,8 @@ func TestTreeConstraintParse(t *testing.T) {
func TestTreeConstraintTagParse(t *testing.T) { func TestTreeConstraintTagParse(t *testing.T) {
ts := memstore.NewTripleStore() ts := memstore.NewTripleStore()
ts.AddTriple(graph.MakeTriple("i", "like", "food", "")) ts.AddTriple(&graph.Triple{"i", "like", "food", ""})
ts.AddTriple(graph.MakeTriple("food", "is", "good", "")) ts.AddTriple(&graph.Triple{"food", "is", "good", ""})
query := "(\"i\"\n" + query := "(\"i\"\n" +
"(:like\n" + "(:like\n" +
"($a (:is :good))))" "($a (:is :good))))"
@ -105,9 +105,9 @@ func TestTreeConstraintTagParse(t *testing.T) {
func TestMultipleConstraintParse(t *testing.T) { func TestMultipleConstraintParse(t *testing.T) {
ts := memstore.NewTripleStore() ts := memstore.NewTripleStore()
ts.AddTriple(graph.MakeTriple("i", "like", "food", "")) ts.AddTriple(&graph.Triple{"i", "like", "food", ""})
ts.AddTriple(graph.MakeTriple("i", "like", "beer", "")) ts.AddTriple(&graph.Triple{"i", "like", "beer", ""})
ts.AddTriple(graph.MakeTriple("you", "like", "beer", "")) ts.AddTriple(&graph.Triple{"you", "like", "beer", ""})
query := "($a \n" + query := "($a \n" +
"(:like :beer)\n" + "(:like :beer)\n" +
"(:like \"food\"))" "(:like \"food\"))"

View file

@ -45,14 +45,6 @@ type Triple struct {
Provenance string `json:"provenance,omitempty"` Provenance string `json:"provenance,omitempty"`
} }
func NewTriple() *Triple {
return &Triple{}
}
func MakeTriple(sub string, pred string, obj string, provenance string) *Triple {
return &Triple{sub, pred, obj, provenance}
}
// Direction specifies an edge's type. // Direction specifies an edge's type.
type Direction byte type Direction byte
@ -103,7 +95,7 @@ func (t *Triple) Equals(o *Triple) bool {
} }
// Pretty-prints a triple. // Pretty-prints a triple.
func (t *Triple) ToString() string { func (t *Triple) String() string {
return fmt.Sprintf("%s -- %s -> %s\n", t.Subject, t.Predicate, t.Object) return fmt.Sprintf("%s -- %s -> %s\n", t.Subject, t.Predicate, t.Object)
} }

View file

@ -69,8 +69,8 @@ type TripleStore interface {
// Returns the number of triples currently stored. // Returns the number of triples currently stored.
Size() int64 Size() int64
// Creates a Fixed iterator which can compare TSVals // Creates a fixed iterator which can compare TSVals
MakeFixed() *FixedIterator FixedIterator() FixedIterator
// Optimize an iterator in the context of the triple store. // Optimize an iterator in the context of the triple store.
// Suppose we have a better index for the passed tree; this // Suppose we have a better index for the passed tree; this

View file

@ -16,7 +16,6 @@ package http
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -37,7 +36,7 @@ func ParseJsonToTripleList(jsonBody []byte) ([]*graph.Triple, error) {
} }
for i, t := range tripleList { for i, t := range tripleList {
if !t.IsValid() { if !t.IsValid() {
return nil, errors.New(fmt.Sprintf("Invalid triple at index %d. %s", i, t.ToString())) return nil, fmt.Errorf("Invalid triple at index %d. %s", i, t)
} }
} }
return tripleList, nil return tripleList, nil

View file

@ -58,7 +58,7 @@ func Parse(str string) *graph.Triple {
} }
str = skipWhitespace(remainder) str = skipWhitespace(remainder)
if str != "" && str[0] == '.' { if str != "" && str[0] == '.' {
return graph.MakeTriple(*sub, *pred, *obj, prov) return &graph.Triple{*sub, *pred, *obj, prov}
} }
return nil return nil
} }

View file

@ -21,6 +21,7 @@ import (
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func getStrings(obj *otto.Object, field string) []string { func getStrings(obj *otto.Object, field string) []string {
@ -40,9 +41,9 @@ func getStringArgs(obj *otto.Object) []string { return getStrings(obj, "string_a
func buildIteratorTree(obj *otto.Object, ts graph.TripleStore) graph.Iterator { func buildIteratorTree(obj *otto.Object, ts graph.TripleStore) graph.Iterator {
if !isVertexChain(obj) { if !isVertexChain(obj) {
return graph.NewNullIterator() return iterator.NewNull()
} }
return buildIteratorTreeHelper(obj, ts, graph.NewNullIterator()) return buildIteratorTreeHelper(obj, ts, iterator.NewNull())
} }
func makeListOfStringsFromArrayValue(obj *otto.Object) []string { func makeListOfStringsFromArrayValue(obj *otto.Object) []string {
@ -73,7 +74,7 @@ func buildIteratorFromValue(val otto.Value, ts graph.TripleStore) graph.Iterator
thing, _ := val.Export() thing, _ := val.Export()
switch v := thing.(type) { switch v := thing.(type) {
case string: case string:
it := ts.MakeFixed() it := ts.FixedIterator()
it.AddValue(ts.GetIdFor(v)) it.AddValue(ts.GetIdFor(v))
return it return it
default: default:
@ -86,7 +87,7 @@ func buildIteratorFromValue(val otto.Value, ts graph.TripleStore) graph.Iterator
case "Array": case "Array":
// Had better be an array of strings // Had better be an array of strings
strings := makeListOfStringsFromArrayValue(val.Object()) strings := makeListOfStringsFromArrayValue(val.Object())
it := ts.MakeFixed() it := ts.FixedIterator()
for _, x := range strings { for _, x := range strings {
it.AddValue(ts.GetIdFor(x)) it.AddValue(ts.GetIdFor(x))
} }
@ -98,13 +99,13 @@ func buildIteratorFromValue(val otto.Value, ts graph.TripleStore) graph.Iterator
case "Date": case "Date":
fallthrough fallthrough
case "String": case "String":
it := ts.MakeFixed() it := ts.FixedIterator()
str, _ := val.ToString() str, _ := val.ToString()
it.AddValue(ts.GetIdFor(str)) it.AddValue(ts.GetIdFor(str))
return it return it
default: default:
glog.Errorln("Trying to handle unsupported Javascript value.") glog.Errorln("Trying to handle unsupported Javascript value.")
return graph.NewNullIterator() return iterator.NewNull()
} }
} }
@ -112,7 +113,7 @@ func buildInOutIterator(obj *otto.Object, ts graph.TripleStore, base graph.Itera
argList, _ := obj.Get("_gremlin_values") argList, _ := obj.Get("_gremlin_values")
if argList.Class() != "GoArray" { if argList.Class() != "GoArray" {
glog.Errorln("How is arglist not an array? Return nothing.", argList.Class()) glog.Errorln("How is arglist not an array? Return nothing.", argList.Class())
return graph.NewNullIterator() return iterator.NewNull()
} }
argArray := argList.Object() argArray := argList.Object()
lengthVal, _ := argArray.Get("length") lengthVal, _ := argArray.Get("length")
@ -142,11 +143,11 @@ func buildInOutIterator(obj *otto.Object, ts graph.TripleStore, base graph.Itera
if isReverse { if isReverse {
in, out = out, in in, out = out, in
} }
lto := graph.NewLinksToIterator(ts, base, in) lto := iterator.NewLinksTo(ts, base, in)
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(graph.NewLinksToIterator(ts, predicateNodeIterator, graph.Predicate)) and.AddSubIterator(iterator.NewLinksTo(ts, predicateNodeIterator, graph.Predicate))
and.AddSubIterator(lto) and.AddSubIterator(lto)
return graph.NewHasaIterator(ts, and, out) return iterator.NewHasA(ts, and, out)
} }
func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.Iterator) graph.Iterator { func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.Iterator) graph.Iterator {
@ -169,7 +170,7 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
if len(stringArgs) == 0 { if len(stringArgs) == 0 {
it = ts.GetNodesAllIterator() it = ts.GetNodesAllIterator()
} else { } else {
fixed := ts.MakeFixed() fixed := ts.FixedIterator()
for _, name := range stringArgs { for _, name := range stringArgs {
fixed.AddValue(ts.GetIdFor(name)) fixed.AddValue(ts.GetIdFor(name))
} }
@ -183,58 +184,58 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
case "save": case "save":
all := ts.GetNodesAllIterator() all := ts.GetNodesAllIterator()
if len(stringArgs) > 2 || len(stringArgs) == 0 { if len(stringArgs) > 2 || len(stringArgs) == 0 {
return graph.NewNullIterator() return iterator.NewNull()
} }
if len(stringArgs) == 2 { if len(stringArgs) == 2 {
all.AddTag(stringArgs[1]) all.AddTag(stringArgs[1])
} else { } else {
all.AddTag(stringArgs[0]) all.AddTag(stringArgs[0])
} }
predFixed := ts.MakeFixed() predFixed := ts.FixedIterator()
predFixed.AddValue(ts.GetIdFor(stringArgs[0])) predFixed.AddValue(ts.GetIdFor(stringArgs[0]))
subAnd := graph.NewAndIterator() subAnd := iterator.NewAnd()
subAnd.AddSubIterator(graph.NewLinksToIterator(ts, predFixed, graph.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, predFixed, graph.Predicate))
subAnd.AddSubIterator(graph.NewLinksToIterator(ts, all, graph.Object)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, all, graph.Object))
hasa := graph.NewHasaIterator(ts, subAnd, graph.Subject) hasa := iterator.NewHasA(ts, subAnd, graph.Subject)
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(hasa) and.AddSubIterator(hasa)
and.AddSubIterator(subIt) and.AddSubIterator(subIt)
it = and it = and
case "saver": case "saver":
all := ts.GetNodesAllIterator() all := ts.GetNodesAllIterator()
if len(stringArgs) > 2 || len(stringArgs) == 0 { if len(stringArgs) > 2 || len(stringArgs) == 0 {
return graph.NewNullIterator() return iterator.NewNull()
} }
if len(stringArgs) == 2 { if len(stringArgs) == 2 {
all.AddTag(stringArgs[1]) all.AddTag(stringArgs[1])
} else { } else {
all.AddTag(stringArgs[0]) all.AddTag(stringArgs[0])
} }
predFixed := ts.MakeFixed() predFixed := ts.FixedIterator()
predFixed.AddValue(ts.GetIdFor(stringArgs[0])) predFixed.AddValue(ts.GetIdFor(stringArgs[0]))
subAnd := graph.NewAndIterator() subAnd := iterator.NewAnd()
subAnd.AddSubIterator(graph.NewLinksToIterator(ts, predFixed, graph.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, predFixed, graph.Predicate))
subAnd.AddSubIterator(graph.NewLinksToIterator(ts, all, graph.Subject)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, all, graph.Subject))
hasa := graph.NewHasaIterator(ts, subAnd, graph.Object) hasa := iterator.NewHasA(ts, subAnd, graph.Object)
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(hasa) and.AddSubIterator(hasa)
and.AddSubIterator(subIt) and.AddSubIterator(subIt)
it = and it = and
case "has": case "has":
fixed := ts.MakeFixed() fixed := ts.FixedIterator()
if len(stringArgs) < 2 { if len(stringArgs) < 2 {
return graph.NewNullIterator() return iterator.NewNull()
} }
for _, name := range stringArgs[1:] { for _, name := range stringArgs[1:] {
fixed.AddValue(ts.GetIdFor(name)) fixed.AddValue(ts.GetIdFor(name))
} }
predFixed := ts.MakeFixed() predFixed := ts.FixedIterator()
predFixed.AddValue(ts.GetIdFor(stringArgs[0])) predFixed.AddValue(ts.GetIdFor(stringArgs[0]))
subAnd := graph.NewAndIterator() subAnd := iterator.NewAnd()
subAnd.AddSubIterator(graph.NewLinksToIterator(ts, predFixed, graph.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, predFixed, graph.Predicate))
subAnd.AddSubIterator(graph.NewLinksToIterator(ts, fixed, graph.Object)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, fixed, graph.Object))
hasa := graph.NewHasaIterator(ts, subAnd, graph.Subject) hasa := iterator.NewHasA(ts, subAnd, graph.Subject)
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(hasa) and.AddSubIterator(hasa)
and.AddSubIterator(subIt) and.AddSubIterator(subIt)
it = and it = and
@ -244,27 +245,27 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
arg, _ := obj.Get("_gremlin_values") arg, _ := obj.Get("_gremlin_values")
firstArg, _ := arg.Object().Get("0") firstArg, _ := arg.Object().Get("0")
if !isVertexChain(firstArg.Object()) { if !isVertexChain(firstArg.Object()) {
return graph.NewNullIterator() return iterator.NewNull()
} }
argIt := buildIteratorTree(firstArg.Object(), ts) argIt := buildIteratorTree(firstArg.Object(), ts)
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(subIt) and.AddSubIterator(subIt)
and.AddSubIterator(argIt) and.AddSubIterator(argIt)
it = and it = and
case "back": case "back":
arg, _ := obj.Get("_gremlin_back_chain") arg, _ := obj.Get("_gremlin_back_chain")
argIt := buildIteratorTree(arg.Object(), ts) argIt := buildIteratorTree(arg.Object(), ts)
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(subIt) and.AddSubIterator(subIt)
and.AddSubIterator(argIt) and.AddSubIterator(argIt)
it = and it = and
case "is": case "is":
fixed := ts.MakeFixed() fixed := ts.FixedIterator()
for _, name := range stringArgs { for _, name := range stringArgs {
fixed.AddValue(ts.GetIdFor(name)) fixed.AddValue(ts.GetIdFor(name))
} }
and := graph.NewAndIterator() and := iterator.NewAnd()
and.AddSubIterator(fixed) and.AddSubIterator(fixed)
and.AddSubIterator(subIt) and.AddSubIterator(subIt)
it = and it = and
@ -272,11 +273,11 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
arg, _ := obj.Get("_gremlin_values") arg, _ := obj.Get("_gremlin_values")
firstArg, _ := arg.Object().Get("0") firstArg, _ := arg.Object().Get("0")
if !isVertexChain(firstArg.Object()) { if !isVertexChain(firstArg.Object()) {
return graph.NewNullIterator() return iterator.NewNull()
} }
argIt := buildIteratorTree(firstArg.Object(), ts) argIt := buildIteratorTree(firstArg.Object(), ts)
or := graph.NewOrIterator() or := iterator.NewOr()
or.AddSubIterator(subIt) or.AddSubIterator(subIt)
or.AddSubIterator(argIt) or.AddSubIterator(argIt)
it = or it = or
@ -287,7 +288,7 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
it1 := buildInOutIterator(obj, ts, subIt, false) it1 := buildInOutIterator(obj, ts, subIt, false)
it2 := buildInOutIterator(obj, ts, clone, true) it2 := buildInOutIterator(obj, ts, clone, true)
or := graph.NewOrIterator() or := iterator.NewOr()
or.AddSubIterator(it1) or.AddSubIterator(it1)
or.AddSubIterator(it2) or.AddSubIterator(it2)
it = or it = or
@ -298,14 +299,14 @@ func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.
arg, _ := obj.Get("_gremlin_values") arg, _ := obj.Get("_gremlin_values")
firstArg, _ := arg.Object().Get("0") firstArg, _ := arg.Object().Get("0")
if isVertexChain(firstArg.Object()) { if isVertexChain(firstArg.Object()) {
return graph.NewNullIterator() return iterator.NewNull()
} }
it = buildIteratorTreeHelper(firstArg.Object(), ts, subIt) it = buildIteratorTreeHelper(firstArg.Object(), ts, subIt)
case "followr": case "followr":
// Follow a morphism // Follow a morphism
arg, _ := obj.Get("_gremlin_followr") arg, _ := obj.Get("_gremlin_followr")
if isVertexChain(arg.Object()) { if isVertexChain(arg.Object()) {
return graph.NewNullIterator() return iterator.NewNull()
} }
it = buildIteratorTreeHelper(arg.Object(), ts, subIt) it = buildIteratorTreeHelper(arg.Object(), ts, subIt)
case "in": case "in":

View file

@ -19,6 +19,7 @@ import (
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
const TopResultTag = "id" const TopResultTag = "id"
@ -238,7 +239,7 @@ func runIteratorWithCallback(it graph.Iterator, ses *Session, callback otto.Valu
func runIteratorOnSession(it graph.Iterator, ses *Session) { func runIteratorOnSession(it graph.Iterator, ses *Session) {
if ses.lookingForQueryShape { if ses.lookingForQueryShape {
graph.OutputQueryShapeForIterator(it, ses.ts, &(ses.queryShape)) iterator.OutputQueryShapeForIterator(it, ses.ts, &(ses.queryShape))
return return
} }
it, _ = it.Optimize() it, _ = it.Optimize()

View file

@ -22,10 +22,11 @@ import (
"strings" "strings"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
func (q *Query) buildFixed(s string) graph.Iterator { func (q *Query) buildFixed(s string) graph.Iterator {
f := q.ses.ts.MakeFixed() f := q.ses.ts.FixedIterator()
f.AddValue(q.ses.ts.GetIdFor(s)) f.AddValue(q.ses.ts.GetIdFor(s))
return f return f
} }
@ -101,7 +102,7 @@ func (q *Query) buildIteratorTreeInternal(query interface{}, path Path) (it grap
} }
func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path Path) (graph.Iterator, error) { func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path Path) (graph.Iterator, error) {
it := graph.NewAndIterator() it := iterator.NewAnd()
it.AddSubIterator(q.ses.ts.GetNodesAllIterator()) it.AddSubIterator(q.ses.ts.GetNodesAllIterator())
var err error var err error
err = nil err = nil
@ -135,24 +136,24 @@ func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path
if err != nil { if err != nil {
return nil, err return nil, err
} }
subAnd := graph.NewAndIterator() subAnd := iterator.NewAnd()
predFixed := q.ses.ts.MakeFixed() predFixed := q.ses.ts.FixedIterator()
predFixed.AddValue(q.ses.ts.GetIdFor(pred)) predFixed.AddValue(q.ses.ts.GetIdFor(pred))
subAnd.AddSubIterator(graph.NewLinksToIterator(q.ses.ts, predFixed, graph.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(q.ses.ts, predFixed, graph.Predicate))
if reverse { if reverse {
lto := graph.NewLinksToIterator(q.ses.ts, builtIt, graph.Subject) lto := iterator.NewLinksTo(q.ses.ts, builtIt, graph.Subject)
subAnd.AddSubIterator(lto) subAnd.AddSubIterator(lto)
hasa := graph.NewHasaIterator(q.ses.ts, subAnd, graph.Object) hasa := iterator.NewHasA(q.ses.ts, subAnd, graph.Object)
subit = hasa subit = hasa
} else { } else {
lto := graph.NewLinksToIterator(q.ses.ts, builtIt, graph.Object) lto := iterator.NewLinksTo(q.ses.ts, builtIt, graph.Object)
subAnd.AddSubIterator(lto) subAnd.AddSubIterator(lto)
hasa := graph.NewHasaIterator(q.ses.ts, subAnd, graph.Subject) hasa := iterator.NewHasA(q.ses.ts, subAnd, graph.Subject)
subit = hasa subit = hasa
} }
} }
if optional { if optional {
it.AddSubIterator(graph.NewOptionalIterator(subit)) it.AddSubIterator(iterator.NewOptional(subit))
} else { } else {
it.AddSubIterator(subit) it.AddSubIterator(subit)
} }

View file

@ -22,6 +22,7 @@ import (
"github.com/barakmich/glog" "github.com/barakmich/glog"
"github.com/google/cayley/graph" "github.com/google/cayley/graph"
"github.com/google/cayley/graph/iterator"
) )
type Session struct { type Session struct {
@ -50,9 +51,9 @@ func (m *Session) GetQuery(input string, output_struct chan map[string]interface
m.currentQuery = NewQuery(m) m.currentQuery = NewQuery(m)
m.currentQuery.BuildIteratorTree(mqlQuery) m.currentQuery.BuildIteratorTree(mqlQuery)
output := make(map[string]interface{}) output := make(map[string]interface{})
graph.OutputQueryShapeForIterator(m.currentQuery.it, m.ts, &output) iterator.OutputQueryShapeForIterator(m.currentQuery.it, m.ts, &output)
nodes := output["nodes"].([]graph.Node) nodes := output["nodes"].([]iterator.Node)
new_nodes := make([]graph.Node, 0) new_nodes := make([]iterator.Node, 0)
for _, n := range nodes { for _, n := range nodes {
n.Tags = nil n.Tags = nil
new_nodes = append(new_nodes, n) new_nodes = append(new_nodes, n)