add Is, Follow, and All to the path API

This commit is contained in:
Barak Michener 2015-02-08 18:10:44 -05:00
parent 3dc74329c0
commit 4128133675
2 changed files with 98 additions and 5 deletions

View file

@ -32,6 +32,9 @@ type Path struct {
} }
func StartPath(qs graph.QuadStore, nodes ...string) *Path { func StartPath(qs graph.QuadStore, nodes ...string) *Path {
if len(nodes) == 0 {
return PathFromIterator(qs, qs.NodesAllIterator())
}
return &Path{ return &Path{
stack: []morphism{ stack: []morphism{
isMorphism(qs, nodes...), isMorphism(qs, nodes...),
@ -43,7 +46,7 @@ func StartPath(qs graph.QuadStore, nodes ...string) *Path {
func PathFromIterator(qs graph.QuadStore, it graph.Iterator) *Path { func PathFromIterator(qs graph.QuadStore, it graph.Iterator) *Path {
return &Path{ return &Path{
stack: []morphism{ stack: []morphism{
intersectIteratorMorphism(it), iteratorMorphism(it),
}, },
qs: qs, qs: qs,
} }
@ -63,6 +66,11 @@ func (p *Path) Reverse() *Path {
return newPath return newPath
} }
func (p *Path) Is(nodes ...string) *Path {
p.stack = append(p.stack, isMorphism(p.qs, nodes...))
return p
}
func (p *Path) Tag(tags ...string) *Path { func (p *Path) Tag(tags ...string) *Path {
p.stack = append(p.stack, tagMorphism(tags...)) p.stack = append(p.stack, tagMorphism(tags...))
return p return p
@ -87,6 +95,16 @@ func (p *Path) Or(path *Path) *Path {
return p return p
} }
func (p *Path) Follow(path *Path) *Path {
p.stack = append(p.stack, followMorphism(path))
return p
}
func (p *Path) FollowReverse(path *Path) *Path {
p.stack = append(p.stack, followMorphism(path.Reverse()))
return p
}
func (p *Path) BuildIterator() graph.Iterator { func (p *Path) BuildIterator() graph.Iterator {
f := p.MorphismFunc() f := p.MorphismFunc()
return f(p.qs.NodesAllIterator()) return f(p.qs.NodesAllIterator())
@ -149,10 +167,10 @@ func inMorphism(qs graph.QuadStore, via ...interface{}) morphism {
} }
} }
func intersectIteratorMorphism(it graph.Iterator) morphism { func iteratorMorphism(it graph.Iterator) morphism {
return morphism{ return morphism{
"iterator", "iterator",
func() morphism { return intersectIteratorMorphism(it) }, func() morphism { return iteratorMorphism(it) },
func(subIt graph.Iterator) graph.Iterator { func(subIt graph.Iterator) graph.Iterator {
and := iterator.NewAnd() and := iterator.NewAnd()
and.AddSubIterator(it) and.AddSubIterator(it)
@ -190,6 +208,17 @@ func orMorphism(path *Path) morphism {
} }
} }
func followMorphism(path *Path) morphism {
return morphism{
"follow",
func() morphism { return followMorphism(path.Reverse()) },
func(base graph.Iterator) graph.Iterator {
p := path.MorphismFunc()
return p(base)
},
}
}
func inOutIterator(viaPath *Path, reverse bool) graph.MorphismFunc { func inOutIterator(viaPath *Path, reverse bool) graph.MorphismFunc {
return func(base graph.Iterator) graph.Iterator { return func(base graph.Iterator) graph.Iterator {
in, out := quad.Subject, quad.Object in, out := quad.Subject, quad.Object

View file

@ -26,6 +26,20 @@ import (
_ "github.com/google/cayley/writer" _ "github.com/google/cayley/writer"
) )
// This is a simple test graph.
//
// +---+ +---+
// | A |------- ->| F |<--
// +---+ \------>+---+-/ +---+ \--+---+
// ------>|#B#| | | E |
// +---+-------/ >+---+ | +---+
// | C | / v
// +---+ -/ +---+
// ---- +---+/ |#G#|
// \-->|#D#|------------->+---+
// +---+
//
var simpleGraph = []quad.Quad{ var simpleGraph = []quad.Quad{
{"A", "follows", "B", ""}, {"A", "follows", "B", ""},
{"C", "follows", "B", ""}, {"C", "follows", "B", ""},
@ -62,10 +76,28 @@ func runTopLevel(path *Path) []string {
return out return out
} }
func runTag(path *Path, tag string) []string {
var out []string
it := path.BuildIterator()
it, _ = it.Optimize()
for graph.Next(it) {
tags := make(map[string]graph.Value)
it.TagResults(tags)
out = append(out, path.qs.NameOf(tags[tag]))
for it.NextPath() {
tags := make(map[string]graph.Value)
it.TagResults(tags)
out = append(out, path.qs.NameOf(tags[tag]))
}
}
return out
}
type test struct { type test struct {
message string message string
path *Path path *Path
expect []string expect []string
tag string
} }
func testSet(qs graph.QuadStore) []test { func testSet(qs graph.QuadStore) []test {
@ -86,18 +118,50 @@ func testSet(qs graph.QuadStore) []test {
expect: []string{"F", "cool"}, expect: []string{"F", "cool"},
}, },
{ {
message: "in", message: "use And",
path: StartPath(qs, "D").Out("follows").And( path: StartPath(qs, "D").Out("follows").And(
StartPath(qs, "C").Out("follows")), StartPath(qs, "C").Out("follows")),
expect: []string{"B"}, expect: []string{"B"},
}, },
{
message: "use Or",
path: StartPath(qs, "F").Out("follows").Or(
StartPath(qs, "A").Out("follows")),
expect: []string{"B", "G"},
},
{
message: "implicit All",
path: StartPath(qs),
expect: []string{"A", "B", "C", "D", "E", "F", "G", "follows", "status", "cool", "status_graph", "predicates", "are"},
},
{
message: "follow",
path: StartPath(qs, "C").Follow(StartPath(qs).Out("follows").Out("follows")),
expect: []string{"B", "F", "G"},
},
{
message: "followR",
path: StartPath(qs, "F").FollowReverse(StartPath(qs).Out("follows").Out("follows")),
expect: []string{"A", "C", "D"},
},
{
message: "is, tag, instead of FollowR",
path: StartPath(qs).Tag("first").Follow(StartPath(qs).Out("follows").Out("follows")).Is("F"),
expect: []string{"A", "C", "D"},
tag: "first",
},
} }
} }
func TestMorphisms(t *testing.T) { func TestMorphisms(t *testing.T) {
qs := makeTestStore(simpleGraph) qs := makeTestStore(simpleGraph)
for _, test := range testSet(qs) { for _, test := range testSet(qs) {
got := runTopLevel(test.path) var got []string
if test.tag == "" {
got = runTopLevel(test.path)
} else {
got = runTag(test.path, test.tag)
}
sort.Strings(got) sort.Strings(got)
sort.Strings(test.expect) sort.Strings(test.expect)
if !reflect.DeepEqual(got, test.expect) { if !reflect.DeepEqual(got, test.expect) {