Merge branch 'master' of https://github.com/google/cayley into operators
Conflicts: query/gremlin/build_iterator.go
This commit is contained in:
commit
a718130f4a
83 changed files with 2370 additions and 2659 deletions
|
|
@ -17,9 +17,9 @@ package iterator
|
|||
// Defines one of the base iterators, the All iterator. Which, logically
|
||||
// enough, represents all nodes or all links in the graph.
|
||||
//
|
||||
// This particular file is actually vestigial. It's up to the TripleStore to give
|
||||
// This particular file is actually vestigial. It's up to the QuadStore to give
|
||||
// us an All iterator that represents all things in the graph. So this is
|
||||
// really the All iterator for the MemTripleStore. That said, it *is* one of
|
||||
// really the All iterator for the memstore.QuadStore. That said, it *is* one of
|
||||
// the base iterators, and it helps just to see it here.
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Defines the And iterator, one of the base iterators. And requires no
|
||||
// knowledge of the constituent TripleStore; its sole purpose is to act as an
|
||||
// knowledge of the constituent QuadStore; its sole purpose is to act as an
|
||||
// intersection operator across the subiterators it is given. If one iterator
|
||||
// contains [1,3,5] and another [2,3,4] -- then And is an iterator that
|
||||
// 'contains' [3]
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ func closeIteratorList(its []graph.Iterator, except graph.Iterator) {
|
|||
|
||||
// Find if there is a single subiterator which is a valid replacement for this
|
||||
// And.
|
||||
func (_ *And) optimizeReplacement(its []graph.Iterator) graph.Iterator {
|
||||
func (*And) optimizeReplacement(its []graph.Iterator) graph.Iterator {
|
||||
// If we were created with no SubIterators, we're as good as Null.
|
||||
if len(its) == 0 {
|
||||
return &Null{}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
func TestIteratorPromotion(t *testing.T) {
|
||||
all := NewInt64(1, 3)
|
||||
fixed := newFixed()
|
||||
fixed := NewFixed(Identity)
|
||||
fixed.Add(3)
|
||||
a := NewAnd()
|
||||
a.AddSubIterator(all)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
|
||||
// Make sure that tags work on the And.
|
||||
func TestTag(t *testing.T) {
|
||||
fix1 := newFixed()
|
||||
fix1 := NewFixed(Identity)
|
||||
fix1.Add(234)
|
||||
fix1.Tagger().Add("foo")
|
||||
and := NewAnd()
|
||||
|
|
@ -55,12 +55,12 @@ func TestTag(t *testing.T) {
|
|||
|
||||
// Do a simple itersection of fixed values.
|
||||
func TestAndAndFixedIterators(t *testing.T) {
|
||||
fix1 := newFixed()
|
||||
fix1 := NewFixed(Identity)
|
||||
fix1.Add(1)
|
||||
fix1.Add(2)
|
||||
fix1.Add(3)
|
||||
fix1.Add(4)
|
||||
fix2 := newFixed()
|
||||
fix2 := NewFixed(Identity)
|
||||
fix2.Add(3)
|
||||
fix2.Add(4)
|
||||
fix2.Add(5)
|
||||
|
|
@ -93,12 +93,12 @@ func TestAndAndFixedIterators(t *testing.T) {
|
|||
// If there's no intersection, the size should still report the same,
|
||||
// but there should be nothing to Next()
|
||||
func TestNonOverlappingFixedIterators(t *testing.T) {
|
||||
fix1 := newFixed()
|
||||
fix1 := NewFixed(Identity)
|
||||
fix1.Add(1)
|
||||
fix1.Add(2)
|
||||
fix1.Add(3)
|
||||
fix1.Add(4)
|
||||
fix2 := newFixed()
|
||||
fix2 := NewFixed(Identity)
|
||||
fix2.Add(5)
|
||||
fix2.Add(6)
|
||||
fix2.Add(7)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package iterator
|
|||
// contains an explicit fixed array of values.
|
||||
//
|
||||
// A fixed iterator requires an Equality function to be passed to it, by reason that graph.Value, the
|
||||
// opaque Triple store value, may not answer to ==.
|
||||
// opaque Quad store value, may not answer to ==.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -42,24 +42,16 @@ type Fixed struct {
|
|||
type Equality func(a, b graph.Value) bool
|
||||
|
||||
// Define an equality function of purely ==, which works for native types.
|
||||
func BasicEquality(a, b graph.Value) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
func Identity(a, b graph.Value) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
// Creates a new Fixed iterator based around == equality.
|
||||
func newFixed() *Fixed {
|
||||
return NewFixedIteratorWithCompare(BasicEquality)
|
||||
}
|
||||
|
||||
// Creates a new Fixed iterator with a custom comparitor.
|
||||
func NewFixedIteratorWithCompare(compareFn Equality) *Fixed {
|
||||
// Creates a new Fixed iterator with a custom comparator.
|
||||
func NewFixed(cmp Equality) *Fixed {
|
||||
return &Fixed{
|
||||
uid: NextUID(),
|
||||
values: make([]graph.Value, 0, 20),
|
||||
cmp: compareFn,
|
||||
cmp: cmp,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +80,7 @@ func (it *Fixed) TagResults(dst map[string]graph.Value) {
|
|||
}
|
||||
|
||||
func (it *Fixed) Clone() graph.Iterator {
|
||||
out := NewFixedIteratorWithCompare(it.cmp)
|
||||
out := NewFixed(it.cmp)
|
||||
for _, val := range it.values {
|
||||
out.Add(val)
|
||||
}
|
||||
|
|
@ -108,7 +100,7 @@ func (it *Fixed) DebugString(indent int) string {
|
|||
if len(it.values) > 0 {
|
||||
value = fmt.Sprint(it.values[0])
|
||||
}
|
||||
return fmt.Sprintf("%s(%s %d tags: %s Size: %d id0: %d)",
|
||||
return fmt.Sprintf("%s(%s %d tags: %s Size: %d id0: %s)",
|
||||
strings.Repeat(" ", indent),
|
||||
it.Type(),
|
||||
it.UID(),
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ package iterator
|
|||
//
|
||||
// HasA is weird in that it may return the same value twice if on the Next()
|
||||
// path. That's okay -- in reality, it can be viewed as returning the value for
|
||||
// a new triple, but to make logic much simpler, here we have the HasA.
|
||||
// a new quad, but to make logic much simpler, here we have the HasA.
|
||||
//
|
||||
// Likewise, it's important to think about Contains()ing a HasA. When given a
|
||||
// value to check, it means "Check all predicates that have this value for your
|
||||
|
|
@ -43,13 +43,13 @@ import (
|
|||
"github.com/google/cayley/quad"
|
||||
)
|
||||
|
||||
// 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 HasA consists of a reference back to the graph.QuadStore that it references,
|
||||
// a primary subiterator, a direction in which the quads for that subiterator point,
|
||||
// and a temporary holder for the iterator generated on Contains().
|
||||
type HasA struct {
|
||||
uid uint64
|
||||
tags graph.Tagger
|
||||
ts graph.TripleStore
|
||||
qs graph.QuadStore
|
||||
primaryIt graph.Iterator
|
||||
dir quad.Direction
|
||||
resultIt graph.Iterator
|
||||
|
|
@ -57,12 +57,12 @@ type HasA struct {
|
|||
runstats graph.IteratorStats
|
||||
}
|
||||
|
||||
// Construct a new HasA iterator, given the triple subiterator, and the triple
|
||||
// Construct a new HasA iterator, given the quad subiterator, and the quad
|
||||
// direction for which it stands.
|
||||
func NewHasA(ts graph.TripleStore, subIt graph.Iterator, d quad.Direction) *HasA {
|
||||
func NewHasA(qs graph.QuadStore, subIt graph.Iterator, d quad.Direction) *HasA {
|
||||
return &HasA{
|
||||
uid: NextUID(),
|
||||
ts: ts,
|
||||
qs: qs,
|
||||
primaryIt: subIt,
|
||||
dir: d,
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ func (it *HasA) Tagger() *graph.Tagger {
|
|||
}
|
||||
|
||||
func (it *HasA) Clone() graph.Iterator {
|
||||
out := NewHasA(it.ts, it.primaryIt.Clone(), it.dir)
|
||||
out := NewHasA(it.qs, it.primaryIt.Clone(), it.dir)
|
||||
out.tags.CopyFrom(it)
|
||||
return out
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ func (it *HasA) Clone() graph.Iterator {
|
|||
func (it *HasA) Direction() quad.Direction { return it.dir }
|
||||
|
||||
// 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 quads that have any directions).
|
||||
func (it *HasA) Optimize() (graph.Iterator, bool) {
|
||||
newPrimary, changed := it.primaryIt.Optimize()
|
||||
if changed {
|
||||
|
|
@ -140,34 +140,34 @@ func (it *HasA) DebugString(indent int) string {
|
|||
}
|
||||
|
||||
// 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 "quads that have `val` in our direction", given to us by the quad store,
|
||||
// and then Next() values out of that iterator and Contains() them against our subiterator.
|
||||
func (it *HasA) Contains(val graph.Value) bool {
|
||||
graph.ContainsLogIn(it, val)
|
||||
it.runstats.Contains += 1
|
||||
if glog.V(4) {
|
||||
glog.V(4).Infoln("Id is", it.ts.NameOf(val))
|
||||
glog.V(4).Infoln("Id is", it.qs.NameOf(val))
|
||||
}
|
||||
// TODO(barakmich): Optimize this
|
||||
if it.resultIt != nil {
|
||||
it.resultIt.Close()
|
||||
}
|
||||
it.resultIt = it.ts.TripleIterator(it.dir, val)
|
||||
it.resultIt = it.qs.QuadIterator(it.dir, val)
|
||||
return graph.ContainsLogOut(it, val, it.NextContains())
|
||||
}
|
||||
|
||||
// NextContains() is shared code between Contains() and GetNextResult() -- calls next on the
|
||||
// result iterator (a triple iterator based on the last checked value) and returns true if
|
||||
// result iterator (a quad iterator based on the last checked value) and returns true if
|
||||
// another match is made.
|
||||
func (it *HasA) NextContains() bool {
|
||||
for graph.Next(it.resultIt) {
|
||||
it.runstats.ContainsNext += 1
|
||||
link := it.resultIt.Result()
|
||||
if glog.V(4) {
|
||||
glog.V(4).Infoln("Quad is", it.ts.Quad(link))
|
||||
glog.V(4).Infoln("Quad is", it.qs.Quad(link))
|
||||
}
|
||||
if it.primaryIt.Contains(link) {
|
||||
it.result = it.ts.TripleDirection(link, it.dir)
|
||||
it.result = it.qs.QuadDirection(link, it.dir)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -192,7 +192,7 @@ func (it *HasA) NextPath() bool {
|
|||
}
|
||||
|
||||
// Next advances the iterator. This is simpler than Contains. 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 quad,
|
||||
// pull our direction out of it, and return that.
|
||||
func (it *HasA) Next() bool {
|
||||
graph.NextLogIn(it)
|
||||
|
|
@ -206,7 +206,7 @@ func (it *HasA) Next() bool {
|
|||
return graph.NextLogOut(it, 0, false)
|
||||
}
|
||||
tID := it.primaryIt.Result()
|
||||
val := it.ts.TripleDirection(tID, it.dir)
|
||||
val := it.qs.QuadDirection(tID, it.dir)
|
||||
it.result = val
|
||||
return graph.NextLogOut(it, val, true)
|
||||
}
|
||||
|
|
@ -217,20 +217,20 @@ func (it *HasA) Result() graph.Value {
|
|||
|
||||
// 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.
|
||||
// ContainsCost involves going to the graph.TripleStore, iterating out values, and hoping
|
||||
// ContainsCost involves going to the graph.QuadStore, iterating out values, and hoping
|
||||
// one sticks -- potentially expensive, depending on fanout. Size, however, is
|
||||
// potentially smaller. we know at worst it's the size of the subiterator, but
|
||||
// if there are many repeated values, it could be much smaller in totality.
|
||||
func (it *HasA) Stats() graph.IteratorStats {
|
||||
subitStats := it.primaryIt.Stats()
|
||||
// TODO(barakmich): These should really come from the triplestore itself
|
||||
// TODO(barakmich): These should really come from the quadstore itself
|
||||
// and be optimized.
|
||||
faninFactor := int64(1)
|
||||
fanoutFactor := int64(30)
|
||||
nextConstant := int64(2)
|
||||
tripleConstant := int64(1)
|
||||
quadConstant := int64(1)
|
||||
return graph.IteratorStats{
|
||||
NextCost: tripleConstant + subitStats.NextCost,
|
||||
NextCost: quadConstant + subitStats.NextCost,
|
||||
ContainsCost: (fanoutFactor * nextConstant) * subitStats.ContainsCost,
|
||||
Size: faninFactor * subitStats.Size,
|
||||
Next: it.runstats.Next,
|
||||
|
|
|
|||
|
|
@ -37,13 +37,13 @@ import (
|
|||
"github.com/google/cayley/quad"
|
||||
)
|
||||
|
||||
// A LinksTo has a reference back to the graph.TripleStore (to create the iterators
|
||||
// A LinksTo has a reference back to the graph.QuadStore (to create the iterators
|
||||
// for each node) the subiterator, and the direction the iterator comes from.
|
||||
// `next_it` is the tempoarary iterator held per result in `primary_it`.
|
||||
type LinksTo struct {
|
||||
uid uint64
|
||||
tags graph.Tagger
|
||||
ts graph.TripleStore
|
||||
qs graph.QuadStore
|
||||
primaryIt graph.Iterator
|
||||
dir quad.Direction
|
||||
nextIt graph.Iterator
|
||||
|
|
@ -53,10 +53,10 @@ type LinksTo struct {
|
|||
|
||||
// Construct a new LinksTo iterator around a direction and a subiterator of
|
||||
// nodes.
|
||||
func NewLinksTo(ts graph.TripleStore, it graph.Iterator, d quad.Direction) *LinksTo {
|
||||
func NewLinksTo(qs graph.QuadStore, it graph.Iterator, d quad.Direction) *LinksTo {
|
||||
return &LinksTo{
|
||||
uid: NextUID(),
|
||||
ts: ts,
|
||||
qs: qs,
|
||||
primaryIt: it,
|
||||
dir: d,
|
||||
nextIt: &Null{},
|
||||
|
|
@ -80,7 +80,7 @@ func (it *LinksTo) Tagger() *graph.Tagger {
|
|||
}
|
||||
|
||||
func (it *LinksTo) Clone() graph.Iterator {
|
||||
out := NewLinksTo(it.ts, it.primaryIt.Clone(), it.dir)
|
||||
out := NewLinksTo(it.qs, it.primaryIt.Clone(), it.dir)
|
||||
out.tags.CopyFrom(it)
|
||||
return out
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ func (it *LinksTo) DebugString(indent int) string {
|
|||
func (it *LinksTo) Contains(val graph.Value) bool {
|
||||
graph.ContainsLogIn(it, val)
|
||||
it.runstats.Contains += 1
|
||||
node := it.ts.TripleDirection(val, it.dir)
|
||||
node := it.qs.QuadDirection(val, it.dir)
|
||||
if it.primaryIt.Contains(node) {
|
||||
it.result = val
|
||||
return graph.ContainsLogOut(it, val, true)
|
||||
|
|
@ -143,10 +143,10 @@ func (it *LinksTo) Optimize() (graph.Iterator, bool) {
|
|||
return it.primaryIt, true
|
||||
}
|
||||
}
|
||||
// Ask the graph.TripleStore if we can be replaced. Often times, this is a great
|
||||
// Ask the graph.QuadStore if we can be replaced. Often times, this is a great
|
||||
// optimization opportunity (there's a fixed iterator underneath us, for
|
||||
// example).
|
||||
newReplacement, hasOne := it.ts.OptimizeIterator(it)
|
||||
newReplacement, hasOne := it.qs.OptimizeIterator(it)
|
||||
if hasOne {
|
||||
it.Close()
|
||||
return newReplacement, true
|
||||
|
|
@ -170,7 +170,7 @@ func (it *LinksTo) Next() bool {
|
|||
return graph.NextLogOut(it, 0, false)
|
||||
}
|
||||
it.nextIt.Close()
|
||||
it.nextIt = it.ts.TripleIterator(it.dir, it.primaryIt.Result())
|
||||
it.nextIt = it.qs.QuadIterator(it.dir, it.primaryIt.Result())
|
||||
|
||||
// Recurse -- return the first in the next set.
|
||||
return it.Next()
|
||||
|
|
@ -197,7 +197,7 @@ func (it *LinksTo) Type() graph.Type { return graph.LinksTo }
|
|||
// Return a guess as to how big or costly it is to next the iterator.
|
||||
func (it *LinksTo) Stats() graph.IteratorStats {
|
||||
subitStats := it.primaryIt.Stats()
|
||||
// TODO(barakmich): These should really come from the triplestore itself
|
||||
// TODO(barakmich): These should really come from the quadstore itself
|
||||
fanoutFactor := int64(20)
|
||||
checkConstant := int64(1)
|
||||
nextConstant := int64(2)
|
||||
|
|
|
|||
|
|
@ -21,23 +21,23 @@ import (
|
|||
)
|
||||
|
||||
func TestLinksTo(t *testing.T) {
|
||||
ts := &store{
|
||||
qs := &store{
|
||||
data: []string{1: "cool"},
|
||||
iter: newFixed(),
|
||||
iter: NewFixed(Identity),
|
||||
}
|
||||
ts.iter.(*Fixed).Add(2)
|
||||
fixed := newFixed()
|
||||
val := ts.ValueOf("cool")
|
||||
qs.iter.(*Fixed).Add(2)
|
||||
fixed := NewFixed(Identity)
|
||||
val := qs.ValueOf("cool")
|
||||
if val != 1 {
|
||||
t.Fatalf("Failed to return correct value, got:%v expect:1", val)
|
||||
}
|
||||
fixed.Add(val)
|
||||
lto := NewLinksTo(ts, fixed, quad.Object)
|
||||
lto := NewLinksTo(qs, fixed, quad.Object)
|
||||
if !lto.Next() {
|
||||
t.Error("At least one triple matches the fixed object")
|
||||
t.Error("At least one quad matches the fixed object")
|
||||
}
|
||||
val = lto.Result()
|
||||
if val != 2 {
|
||||
t.Errorf("Quad index 2, such as %s, should match %s", ts.Quad(2), ts.Quad(val))
|
||||
t.Errorf("Quad index 2, such as %s, should match %s", qs.Quad(2), qs.Quad(val))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,12 @@
|
|||
|
||||
package iterator
|
||||
|
||||
// A quickly mocked version of the TripleStore interface, for use in tests.
|
||||
// Can better used Mock.Called but will fill in as needed.
|
||||
|
||||
import (
|
||||
"github.com/google/cayley/graph"
|
||||
"github.com/google/cayley/quad"
|
||||
)
|
||||
|
||||
// store is a mocked version of the QuadStore interface, for use in tests.
|
||||
type store struct {
|
||||
data []string
|
||||
iter graph.Iterator
|
||||
|
|
@ -40,13 +38,13 @@ func (qs *store) ApplyDeltas([]graph.Delta) error { return nil }
|
|||
|
||||
func (qs *store) Quad(graph.Value) quad.Quad { return quad.Quad{} }
|
||||
|
||||
func (qs *store) TripleIterator(d quad.Direction, i graph.Value) graph.Iterator {
|
||||
func (qs *store) QuadIterator(d quad.Direction, i graph.Value) graph.Iterator {
|
||||
return qs.iter
|
||||
}
|
||||
|
||||
func (qs *store) NodesAllIterator() graph.Iterator { return &Null{} }
|
||||
|
||||
func (qs *store) TriplesAllIterator() graph.Iterator { return &Null{} }
|
||||
func (qs *store) QuadsAllIterator() graph.Iterator { return &Null{} }
|
||||
|
||||
func (qs *store) NameOf(v graph.Value) string {
|
||||
i := v.(int)
|
||||
|
|
@ -67,11 +65,11 @@ func (qs *store) OptimizeIterator(it graph.Iterator) (graph.Iterator, bool) {
|
|||
}
|
||||
|
||||
func (qs *store) FixedIterator() graph.FixedIterator {
|
||||
return NewFixedIteratorWithCompare(BasicEquality)
|
||||
return NewFixed(Identity)
|
||||
}
|
||||
|
||||
func (qs *store) Close() {}
|
||||
|
||||
func (qs *store) TripleDirection(graph.Value, quad.Direction) graph.Value { return 0 }
|
||||
func (qs *store) QuadDirection(graph.Value, quad.Direction) graph.Value { return 0 }
|
||||
|
||||
func (qs *store) RemoveTriple(t quad.Quad) {}
|
||||
func (qs *store) RemoveQuad(t quad.Quad) {}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ func iterated(it graph.Iterator) []int {
|
|||
|
||||
func TestOrIteratorBasics(t *testing.T) {
|
||||
or := NewOr()
|
||||
f1 := newFixed()
|
||||
f1 := NewFixed(Identity)
|
||||
f1.Add(1)
|
||||
f1.Add(2)
|
||||
f1.Add(3)
|
||||
f2 := newFixed()
|
||||
f2 := NewFixed(Identity)
|
||||
f2.Add(3)
|
||||
f2.Add(9)
|
||||
f2.Add(20)
|
||||
|
|
@ -77,11 +77,11 @@ func TestOrIteratorBasics(t *testing.T) {
|
|||
func TestShortCircuitingOrBasics(t *testing.T) {
|
||||
var or *Or
|
||||
|
||||
f1 := newFixed()
|
||||
f1 := NewFixed(Identity)
|
||||
f1.Add(1)
|
||||
f1.Add(2)
|
||||
f1.Add(3)
|
||||
f2 := newFixed()
|
||||
f2 := NewFixed(Identity)
|
||||
f2.Add(3)
|
||||
f2.Add(9)
|
||||
f2.Add(20)
|
||||
|
|
@ -133,7 +133,7 @@ func TestShortCircuitingOrBasics(t *testing.T) {
|
|||
|
||||
// Check that it pulls the second iterator's numbers if the first is empty.
|
||||
or = NewShortCircuitOr()
|
||||
or.AddSubIterator(newFixed())
|
||||
or.AddSubIterator(NewFixed(Identity))
|
||||
or.AddSubIterator(f2)
|
||||
expect = []int{3, 9, 20, 21}
|
||||
for i := 0; i < 2; i++ {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
)
|
||||
|
||||
type Node struct {
|
||||
Id int `json:"id"`
|
||||
ID int `json:"id"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Values []string `json:"values,omitempty"`
|
||||
IsLinkNode bool `json:"is_link_node"`
|
||||
|
|
@ -37,47 +37,47 @@ type Link struct {
|
|||
type queryShape struct {
|
||||
nodes []Node
|
||||
links []Link
|
||||
ts graph.TripleStore
|
||||
nodeId int
|
||||
hasaIds []int
|
||||
qs graph.QuadStore
|
||||
nodeID int
|
||||
hasaIDs []int
|
||||
hasaDirs []quad.Direction
|
||||
}
|
||||
|
||||
func OutputQueryShapeForIterator(it graph.Iterator, ts graph.TripleStore, outputMap map[string]interface{}) {
|
||||
qs := &queryShape{
|
||||
ts: ts,
|
||||
nodeId: 1,
|
||||
func OutputQueryShapeForIterator(it graph.Iterator, qs graph.QuadStore, outputMap map[string]interface{}) {
|
||||
s := &queryShape{
|
||||
qs: qs,
|
||||
nodeID: 1,
|
||||
}
|
||||
|
||||
node := qs.MakeNode(it.Clone())
|
||||
qs.AddNode(node)
|
||||
outputMap["nodes"] = qs.nodes
|
||||
outputMap["links"] = qs.links
|
||||
node := s.MakeNode(it.Clone())
|
||||
s.AddNode(node)
|
||||
outputMap["nodes"] = s.nodes
|
||||
outputMap["links"] = s.links
|
||||
}
|
||||
|
||||
func (qs *queryShape) AddNode(n *Node) {
|
||||
qs.nodes = append(qs.nodes, *n)
|
||||
func (s *queryShape) AddNode(n *Node) {
|
||||
s.nodes = append(s.nodes, *n)
|
||||
}
|
||||
|
||||
func (qs *queryShape) AddLink(l *Link) {
|
||||
qs.links = append(qs.links, *l)
|
||||
func (s *queryShape) AddLink(l *Link) {
|
||||
s.links = append(s.links, *l)
|
||||
}
|
||||
|
||||
func (qs *queryShape) LastHasa() (int, quad.Direction) {
|
||||
return qs.hasaIds[len(qs.hasaIds)-1], qs.hasaDirs[len(qs.hasaDirs)-1]
|
||||
func (s *queryShape) LastHasa() (int, quad.Direction) {
|
||||
return s.hasaIDs[len(s.hasaIDs)-1], s.hasaDirs[len(s.hasaDirs)-1]
|
||||
}
|
||||
|
||||
func (qs *queryShape) PushHasa(i int, d quad.Direction) {
|
||||
qs.hasaIds = append(qs.hasaIds, i)
|
||||
qs.hasaDirs = append(qs.hasaDirs, d)
|
||||
func (s *queryShape) PushHasa(i int, d quad.Direction) {
|
||||
s.hasaIDs = append(s.hasaIDs, i)
|
||||
s.hasaDirs = append(s.hasaDirs, d)
|
||||
}
|
||||
|
||||
func (qs *queryShape) RemoveHasa() {
|
||||
qs.hasaIds = qs.hasaIds[:len(qs.hasaIds)-1]
|
||||
qs.hasaDirs = qs.hasaDirs[:len(qs.hasaDirs)-1]
|
||||
func (s *queryShape) RemoveHasa() {
|
||||
s.hasaIDs = s.hasaIDs[:len(s.hasaIDs)-1]
|
||||
s.hasaDirs = s.hasaDirs[:len(s.hasaDirs)-1]
|
||||
}
|
||||
|
||||
func (qs *queryShape) StealNode(left *Node, right *Node) {
|
||||
func (s *queryShape) StealNode(left *Node, right *Node) {
|
||||
for _, v := range right.Values {
|
||||
left.Values = append(left.Values, v)
|
||||
}
|
||||
|
|
@ -86,88 +86,88 @@ func (qs *queryShape) StealNode(left *Node, right *Node) {
|
|||
}
|
||||
left.IsLinkNode = left.IsLinkNode || right.IsLinkNode
|
||||
left.IsFixed = left.IsFixed || right.IsFixed
|
||||
for i, link := range qs.links {
|
||||
for i, link := range s.links {
|
||||
rewrite := false
|
||||
if link.LinkNode == right.Id {
|
||||
link.LinkNode = left.Id
|
||||
if link.LinkNode == right.ID {
|
||||
link.LinkNode = left.ID
|
||||
rewrite = true
|
||||
}
|
||||
if link.Source == right.Id {
|
||||
link.Source = left.Id
|
||||
if link.Source == right.ID {
|
||||
link.Source = left.ID
|
||||
rewrite = true
|
||||
}
|
||||
if link.Target == right.Id {
|
||||
link.Target = left.Id
|
||||
if link.Target == right.ID {
|
||||
link.Target = left.ID
|
||||
rewrite = true
|
||||
}
|
||||
if rewrite {
|
||||
qs.links = append(append(qs.links[:i], qs.links[i+1:]...), link)
|
||||
s.links = append(append(s.links[:i], s.links[i+1:]...), link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (qs *queryShape) MakeNode(it graph.Iterator) *Node {
|
||||
n := Node{Id: qs.nodeId}
|
||||
func (s *queryShape) MakeNode(it graph.Iterator) *Node {
|
||||
n := Node{ID: s.nodeID}
|
||||
for _, tag := range it.Tagger().Tags() {
|
||||
n.Tags = append(n.Tags, tag)
|
||||
}
|
||||
for k, _ := range it.Tagger().Fixed() {
|
||||
for k := range it.Tagger().Fixed() {
|
||||
n.Tags = append(n.Tags, k)
|
||||
}
|
||||
|
||||
switch it.Type() {
|
||||
case graph.And:
|
||||
for _, sub := range it.SubIterators() {
|
||||
qs.nodeId++
|
||||
newNode := qs.MakeNode(sub)
|
||||
s.nodeID++
|
||||
newNode := s.MakeNode(sub)
|
||||
if sub.Type() != graph.Or {
|
||||
qs.StealNode(&n, newNode)
|
||||
s.StealNode(&n, newNode)
|
||||
} else {
|
||||
qs.AddNode(newNode)
|
||||
qs.AddLink(&Link{n.Id, newNode.Id, 0, 0})
|
||||
s.AddNode(newNode)
|
||||
s.AddLink(&Link{n.ID, newNode.ID, 0, 0})
|
||||
}
|
||||
}
|
||||
case graph.Fixed:
|
||||
n.IsFixed = true
|
||||
for graph.Next(it) {
|
||||
n.Values = append(n.Values, qs.ts.NameOf(it.Result()))
|
||||
n.Values = append(n.Values, s.qs.NameOf(it.Result()))
|
||||
}
|
||||
case graph.HasA:
|
||||
hasa := it.(*HasA)
|
||||
qs.PushHasa(n.Id, hasa.dir)
|
||||
qs.nodeId++
|
||||
newNode := qs.MakeNode(hasa.primaryIt)
|
||||
qs.AddNode(newNode)
|
||||
qs.RemoveHasa()
|
||||
s.PushHasa(n.ID, hasa.dir)
|
||||
s.nodeID++
|
||||
newNode := s.MakeNode(hasa.primaryIt)
|
||||
s.AddNode(newNode)
|
||||
s.RemoveHasa()
|
||||
case graph.Or:
|
||||
for _, sub := range it.SubIterators() {
|
||||
qs.nodeId++
|
||||
newNode := qs.MakeNode(sub)
|
||||
s.nodeID++
|
||||
newNode := s.MakeNode(sub)
|
||||
if sub.Type() == graph.Or {
|
||||
qs.StealNode(&n, newNode)
|
||||
s.StealNode(&n, newNode)
|
||||
} else {
|
||||
qs.AddNode(newNode)
|
||||
qs.AddLink(&Link{n.Id, newNode.Id, 0, 0})
|
||||
s.AddNode(newNode)
|
||||
s.AddLink(&Link{n.ID, newNode.ID, 0, 0})
|
||||
}
|
||||
}
|
||||
case graph.LinksTo:
|
||||
n.IsLinkNode = true
|
||||
lto := it.(*LinksTo)
|
||||
qs.nodeId++
|
||||
newNode := qs.MakeNode(lto.primaryIt)
|
||||
hasaID, hasaDir := qs.LastHasa()
|
||||
s.nodeID++
|
||||
newNode := s.MakeNode(lto.primaryIt)
|
||||
hasaID, hasaDir := s.LastHasa()
|
||||
if (hasaDir == quad.Subject && lto.dir == quad.Object) ||
|
||||
(hasaDir == quad.Object && lto.dir == quad.Subject) {
|
||||
qs.AddNode(newNode)
|
||||
s.AddNode(newNode)
|
||||
if hasaDir == quad.Subject {
|
||||
qs.AddLink(&Link{hasaID, newNode.Id, 0, n.Id})
|
||||
s.AddLink(&Link{hasaID, newNode.ID, 0, n.ID})
|
||||
} else {
|
||||
qs.AddLink(&Link{newNode.Id, hasaID, 0, n.Id})
|
||||
s.AddLink(&Link{newNode.ID, hasaID, 0, n.ID})
|
||||
}
|
||||
} else if lto.primaryIt.Type() == graph.Fixed {
|
||||
qs.StealNode(&n, newNode)
|
||||
s.StealNode(&n, newNode)
|
||||
} else {
|
||||
qs.AddNode(newNode)
|
||||
s.AddNode(newNode)
|
||||
}
|
||||
case graph.Optional:
|
||||
// Unsupported, for the moment
|
||||
|
|
|
|||
|
|
@ -22,23 +22,23 @@ import (
|
|||
"github.com/google/cayley/quad"
|
||||
)
|
||||
|
||||
func hasaWithTag(ts graph.TripleStore, tag string, target string) *HasA {
|
||||
func hasaWithTag(qs graph.QuadStore, tag string, target string) *HasA {
|
||||
and := NewAnd()
|
||||
|
||||
obj := ts.FixedIterator()
|
||||
obj.Add(ts.ValueOf(target))
|
||||
obj := qs.FixedIterator()
|
||||
obj.Add(qs.ValueOf(target))
|
||||
obj.Tagger().Add(tag)
|
||||
and.AddSubIterator(NewLinksTo(ts, obj, quad.Object))
|
||||
and.AddSubIterator(NewLinksTo(qs, obj, quad.Object))
|
||||
|
||||
pred := ts.FixedIterator()
|
||||
pred.Add(ts.ValueOf("status"))
|
||||
and.AddSubIterator(NewLinksTo(ts, pred, quad.Predicate))
|
||||
pred := qs.FixedIterator()
|
||||
pred.Add(qs.ValueOf("status"))
|
||||
and.AddSubIterator(NewLinksTo(qs, pred, quad.Predicate))
|
||||
|
||||
return NewHasA(ts, and, quad.Subject)
|
||||
return NewHasA(qs, and, quad.Subject)
|
||||
}
|
||||
|
||||
func TestQueryShape(t *testing.T) {
|
||||
ts := &store{
|
||||
qs := &store{
|
||||
data: []string{
|
||||
1: "cool",
|
||||
2: "status",
|
||||
|
|
@ -48,11 +48,11 @@ func TestQueryShape(t *testing.T) {
|
|||
}
|
||||
|
||||
// Given a single linkage iterator's shape.
|
||||
hasa := hasaWithTag(ts, "tag", "cool")
|
||||
hasa := hasaWithTag(qs, "tag", "cool")
|
||||
hasa.Tagger().Add("top")
|
||||
|
||||
shape := make(map[string]interface{})
|
||||
OutputQueryShapeForIterator(hasa, ts, shape)
|
||||
OutputQueryShapeForIterator(hasa, qs, shape)
|
||||
|
||||
nodes := shape["nodes"].([]Node)
|
||||
if len(nodes) != 3 {
|
||||
|
|
@ -77,14 +77,14 @@ func TestQueryShape(t *testing.T) {
|
|||
// Link should be correctly typed.
|
||||
nodes = shape["nodes"].([]Node)
|
||||
link := shape["links"].([]Link)[0]
|
||||
if link.Source != nodes[2].Id {
|
||||
t.Errorf("Failed to get correct link source, got:%v expect:%v", link.Source, nodes[2].Id)
|
||||
if link.Source != nodes[2].ID {
|
||||
t.Errorf("Failed to get correct link source, got:%v expect:%v", link.Source, nodes[2].ID)
|
||||
}
|
||||
if link.Target != nodes[0].Id {
|
||||
t.Errorf("Failed to get correct link target, got:%v expect:%v", link.Target, nodes[0].Id)
|
||||
if link.Target != nodes[0].ID {
|
||||
t.Errorf("Failed to get correct link target, got:%v expect:%v", link.Target, nodes[0].ID)
|
||||
}
|
||||
if link.LinkNode != nodes[1].Id {
|
||||
t.Errorf("Failed to get correct link node, got:%v expect:%v", link.LinkNode, nodes[1].Id)
|
||||
if link.LinkNode != nodes[1].ID {
|
||||
t.Errorf("Failed to get correct link node, got:%v expect:%v", link.LinkNode, nodes[1].ID)
|
||||
}
|
||||
if link.Pred != 0 {
|
||||
t.Errorf("Failed to get correct number of predecessors:%v expect:0", link.Pred)
|
||||
|
|
@ -93,23 +93,23 @@ func TestQueryShape(t *testing.T) {
|
|||
// Given a name-of-an-and-iterator's shape.
|
||||
andInternal := NewAnd()
|
||||
|
||||
hasa1 := hasaWithTag(ts, "tag1", "cool")
|
||||
hasa1 := hasaWithTag(qs, "tag1", "cool")
|
||||
hasa1.Tagger().Add("hasa1")
|
||||
andInternal.AddSubIterator(hasa1)
|
||||
|
||||
hasa2 := hasaWithTag(ts, "tag2", "fun")
|
||||
hasa2 := hasaWithTag(qs, "tag2", "fun")
|
||||
hasa2.Tagger().Add("hasa2")
|
||||
andInternal.AddSubIterator(hasa2)
|
||||
|
||||
pred := ts.FixedIterator()
|
||||
pred.Add(ts.ValueOf("name"))
|
||||
pred := qs.FixedIterator()
|
||||
pred.Add(qs.ValueOf("name"))
|
||||
|
||||
and := NewAnd()
|
||||
and.AddSubIterator(NewLinksTo(ts, andInternal, quad.Subject))
|
||||
and.AddSubIterator(NewLinksTo(ts, pred, quad.Predicate))
|
||||
and.AddSubIterator(NewLinksTo(qs, andInternal, quad.Subject))
|
||||
and.AddSubIterator(NewLinksTo(qs, pred, quad.Predicate))
|
||||
|
||||
shape = make(map[string]interface{})
|
||||
OutputQueryShapeForIterator(NewHasA(ts, and, quad.Object), ts, shape)
|
||||
OutputQueryShapeForIterator(NewHasA(qs, and, quad.Object), qs, shape)
|
||||
|
||||
links = shape["links"].([]Link)
|
||||
if len(links) != 3 {
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ import (
|
|||
type Operator int
|
||||
|
||||
const (
|
||||
kCompareLT Operator = iota
|
||||
kCompareLTE
|
||||
kCompareGT
|
||||
kCompareGTE
|
||||
compareLT Operator = iota
|
||||
compareLTE
|
||||
compareGT
|
||||
compareGTE
|
||||
// Why no Equals? Because that's usually an AndIterator.
|
||||
)
|
||||
|
||||
|
|
@ -51,17 +51,17 @@ type Comparison struct {
|
|||
subIt graph.Iterator
|
||||
op Operator
|
||||
val interface{}
|
||||
ts graph.TripleStore
|
||||
qs graph.QuadStore
|
||||
result graph.Value
|
||||
}
|
||||
|
||||
func NewComparison(sub graph.Iterator, op Operator, val interface{}, ts graph.TripleStore) *Comparison {
|
||||
func NewComparison(sub graph.Iterator, op Operator, val interface{}, qs graph.QuadStore) *Comparison {
|
||||
return &Comparison{
|
||||
uid: NextUID(),
|
||||
subIt: sub,
|
||||
op: op,
|
||||
val: val,
|
||||
ts: ts,
|
||||
qs: qs,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ func (it *Comparison) UID() uint64 {
|
|||
// and our operator, determine whether or not we meet the requirement.
|
||||
func (it *Comparison) doComparison(val graph.Value) bool {
|
||||
//TODO(barakmich): Implement string comparison.
|
||||
nodeStr := it.ts.NameOf(val)
|
||||
nodeStr := it.qs.NameOf(val)
|
||||
switch cVal := it.val.(type) {
|
||||
case int:
|
||||
cInt := int64(cVal)
|
||||
|
|
@ -99,13 +99,13 @@ func (it *Comparison) Close() {
|
|||
|
||||
func RunIntOp(a int64, op Operator, b int64) bool {
|
||||
switch op {
|
||||
case kCompareLT:
|
||||
case compareLT:
|
||||
return a < b
|
||||
case kCompareLTE:
|
||||
case compareLTE:
|
||||
return a <= b
|
||||
case kCompareGT:
|
||||
case compareGT:
|
||||
return a > b
|
||||
case kCompareGTE:
|
||||
case compareGTE:
|
||||
return a >= b
|
||||
default:
|
||||
log.Fatal("Unknown operator type")
|
||||
|
|
@ -122,7 +122,7 @@ func (it *Comparison) Tagger() *graph.Tagger {
|
|||
}
|
||||
|
||||
func (it *Comparison) Clone() graph.Iterator {
|
||||
out := NewComparison(it.subIt.Clone(), it.op, it.val, it.ts)
|
||||
out := NewComparison(it.subIt.Clone(), it.op, it.val, it.qs)
|
||||
out.tags.CopyFrom(it)
|
||||
return out
|
||||
}
|
||||
|
|
@ -154,7 +154,7 @@ func (it *Comparison) NextPath() bool {
|
|||
return false
|
||||
}
|
||||
if it.doComparison(it.subIt.Result()) {
|
||||
return true
|
||||
break
|
||||
}
|
||||
}
|
||||
it.result = it.subIt.Result()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
var simpleStore = &store{data: []string{"0", "1", "2", "3", "4", "5"}}
|
||||
|
||||
func simpleFixedIterator() *Fixed {
|
||||
f := newFixed()
|
||||
f := NewFixed(Identity)
|
||||
for i := 0; i < 5; i++ {
|
||||
f.Add(i)
|
||||
}
|
||||
|
|
@ -40,37 +40,37 @@ var comparisonTests = []struct {
|
|||
{
|
||||
message: "successful int64 less than comparison",
|
||||
operand: int64(3),
|
||||
operator: kCompareLT,
|
||||
operator: compareLT,
|
||||
expect: []string{"0", "1", "2"},
|
||||
},
|
||||
{
|
||||
message: "empty int64 less than comparison",
|
||||
operand: int64(0),
|
||||
operator: kCompareLT,
|
||||
operator: compareLT,
|
||||
expect: nil,
|
||||
},
|
||||
{
|
||||
message: "successful int64 greater than comparison",
|
||||
operand: int64(2),
|
||||
operator: kCompareGT,
|
||||
operator: compareGT,
|
||||
expect: []string{"3", "4"},
|
||||
},
|
||||
{
|
||||
message: "successful int64 greater than or equal comparison",
|
||||
operand: int64(2),
|
||||
operator: kCompareGTE,
|
||||
operator: compareGTE,
|
||||
expect: []string{"2", "3", "4"},
|
||||
},
|
||||
}
|
||||
|
||||
func TestValueComparison(t *testing.T) {
|
||||
for _, test := range comparisonTests {
|
||||
ts := simpleStore
|
||||
vc := NewComparison(simpleFixedIterator(), test.operator, test.operand, ts)
|
||||
qs := simpleStore
|
||||
vc := NewComparison(simpleFixedIterator(), test.operator, test.operand, qs)
|
||||
|
||||
var got []string
|
||||
for vc.Next() {
|
||||
got = append(got, ts.NameOf(vc.Result()))
|
||||
got = append(got, qs.NameOf(vc.Result()))
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.expect) {
|
||||
t.Errorf("Failed to show %s, got:%q expect:%q", test.message, got, test.expect)
|
||||
|
|
@ -86,25 +86,25 @@ var vciContainsTests = []struct {
|
|||
}{
|
||||
{
|
||||
message: "1 is less than 2",
|
||||
operator: kCompareGTE,
|
||||
operator: compareGTE,
|
||||
check: 1,
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
message: "2 is greater than or equal to 2",
|
||||
operator: kCompareGTE,
|
||||
operator: compareGTE,
|
||||
check: 2,
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
message: "3 is greater than or equal to 2",
|
||||
operator: kCompareGTE,
|
||||
operator: compareGTE,
|
||||
check: 3,
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
message: "5 is absent from iterator",
|
||||
operator: kCompareGTE,
|
||||
operator: compareGTE,
|
||||
check: 5,
|
||||
expect: false,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue