From 412813367501257e8ac6193a6eb05ca77c9fb90b Mon Sep 17 00:00:00 2001 From: Barak Michener Date: Sun, 8 Feb 2015 18:10:44 -0500 Subject: [PATCH] add Is, Follow, and All to the path API --- graph/path/path.go | 35 ++++++++++++++++++++++--- graph/path/path_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/graph/path/path.go b/graph/path/path.go index 57b4c6e..3d13740 100644 --- a/graph/path/path.go +++ b/graph/path/path.go @@ -32,6 +32,9 @@ type Path struct { } func StartPath(qs graph.QuadStore, nodes ...string) *Path { + if len(nodes) == 0 { + return PathFromIterator(qs, qs.NodesAllIterator()) + } return &Path{ stack: []morphism{ isMorphism(qs, nodes...), @@ -43,7 +46,7 @@ func StartPath(qs graph.QuadStore, nodes ...string) *Path { func PathFromIterator(qs graph.QuadStore, it graph.Iterator) *Path { return &Path{ stack: []morphism{ - intersectIteratorMorphism(it), + iteratorMorphism(it), }, qs: qs, } @@ -63,6 +66,11 @@ func (p *Path) Reverse() *Path { 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 { p.stack = append(p.stack, tagMorphism(tags...)) return p @@ -87,6 +95,16 @@ func (p *Path) Or(path *Path) *Path { 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 { f := p.MorphismFunc() 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{ "iterator", - func() morphism { return intersectIteratorMorphism(it) }, + func() morphism { return iteratorMorphism(it) }, func(subIt graph.Iterator) graph.Iterator { and := iterator.NewAnd() 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 { return func(base graph.Iterator) graph.Iterator { in, out := quad.Subject, quad.Object diff --git a/graph/path/path_test.go b/graph/path/path_test.go index c67ea15..d9d6c0d 100644 --- a/graph/path/path_test.go +++ b/graph/path/path_test.go @@ -26,6 +26,20 @@ import ( _ "github.com/google/cayley/writer" ) +// This is a simple test graph. +// +// +---+ +---+ +// | A |------- ->| F |<-- +// +---+ \------>+---+-/ +---+ \--+---+ +// ------>|#B#| | | E | +// +---+-------/ >+---+ | +---+ +// | C | / v +// +---+ -/ +---+ +// ---- +---+/ |#G#| +// \-->|#D#|------------->+---+ +// +---+ +// + var simpleGraph = []quad.Quad{ {"A", "follows", "B", ""}, {"C", "follows", "B", ""}, @@ -62,10 +76,28 @@ func runTopLevel(path *Path) []string { 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 { message string path *Path expect []string + tag string } func testSet(qs graph.QuadStore) []test { @@ -86,18 +118,50 @@ func testSet(qs graph.QuadStore) []test { expect: []string{"F", "cool"}, }, { - message: "in", + message: "use And", path: StartPath(qs, "D").Out("follows").And( StartPath(qs, "C").Out("follows")), 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) { qs := makeTestStore(simpleGraph) 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(test.expect) if !reflect.DeepEqual(got, test.expect) {