From 3dc74329c029983a9620102f057fe6d242fd3702 Mon Sep 17 00:00:00 2001 From: Barak Michener Date: Thu, 4 Sep 2014 16:34:58 -0400 Subject: [PATCH] rename to path --- graph/api/api.go | 229 ------------------------------------------------ graph/api/api_test.go | 107 ---------------------- graph/path/path.go | 229 ++++++++++++++++++++++++++++++++++++++++++++++++ graph/path/path_test.go | 107 ++++++++++++++++++++++ 4 files changed, 336 insertions(+), 336 deletions(-) delete mode 100644 graph/api/api.go delete mode 100644 graph/api/api_test.go create mode 100644 graph/path/path.go create mode 100644 graph/path/path_test.go diff --git a/graph/api/api.go b/graph/api/api.go deleted file mode 100644 index 928ba61..0000000 --- a/graph/api/api.go +++ /dev/null @@ -1,229 +0,0 @@ -// 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 api - -import ( - "github.com/google/cayley/graph" - "github.com/google/cayley/graph/iterator" - "github.com/google/cayley/quad" -) - -type morphism struct { - Name string - Reversal func() morphism - Apply graph.MorphismFunc -} - -type Path struct { - stack []morphism - qs graph.QuadStore -} - -func StartPath(qs graph.QuadStore, nodes ...string) *Path { - return &Path{ - stack: []morphism{ - isMorphism(qs, nodes...), - }, - qs: qs, - } -} - -func PathFromIterator(qs graph.QuadStore, it graph.Iterator) *Path { - return &Path{ - stack: []morphism{ - intersectIteratorMorphism(it), - }, - qs: qs, - } -} - -func NewPath(qs graph.QuadStore) *Path { - return &Path{ - qs: qs, - } -} - -func (p *Path) Reverse() *Path { - newPath := NewPath(p.qs) - for i := len(p.stack) - 1; i >= 0; i-- { - newPath.stack = append(newPath.stack, p.stack[i].Reversal()) - } - return newPath -} - -func (p *Path) Tag(tags ...string) *Path { - p.stack = append(p.stack, tagMorphism(tags...)) - return p -} - -func (p *Path) Out(via ...interface{}) *Path { - p.stack = append(p.stack, outMorphism(p.qs, via...)) - return p -} -func (p *Path) In(via ...interface{}) *Path { - p.stack = append(p.stack, inMorphism(p.qs, via...)) - return p -} - -func (p *Path) And(path *Path) *Path { - p.stack = append(p.stack, andMorphism(path)) - return p -} - -func (p *Path) Or(path *Path) *Path { - p.stack = append(p.stack, orMorphism(path)) - return p -} - -func (p *Path) BuildIterator() graph.Iterator { - f := p.MorphismFunc() - return f(p.qs.NodesAllIterator()) -} - -func (p *Path) MorphismFunc() graph.MorphismFunc { - return func(it graph.Iterator) graph.Iterator { - i := it.Clone() - for _, m := range p.stack { - i = m.Apply(i) - } - return i - } -} - -func isMorphism(qs graph.QuadStore, nodes ...string) morphism { - return morphism{ - "is", - func() morphism { return isMorphism(qs, nodes...) }, - func(it graph.Iterator) graph.Iterator { - fixed := qs.FixedIterator() - for _, n := range nodes { - fixed.Add(qs.ValueOf(n)) - } - and := iterator.NewAnd() - and.AddSubIterator(fixed) - and.AddSubIterator(it) - return and - }, - } -} - -func tagMorphism(tags ...string) morphism { - return morphism{ - "tag", - func() morphism { return tagMorphism(tags...) }, - func(it graph.Iterator) graph.Iterator { - for _, t := range tags { - it.Tagger().Add(t) - } - return it - }} -} - -func outMorphism(qs graph.QuadStore, via ...interface{}) morphism { - path := buildViaPath(qs, via...) - return morphism{ - "out", - func() morphism { return inMorphism(qs, via...) }, - inOutIterator(path, false), - } -} - -func inMorphism(qs graph.QuadStore, via ...interface{}) morphism { - path := buildViaPath(qs, via...) - return morphism{ - "in", - func() morphism { return outMorphism(qs, via...) }, - inOutIterator(path, true), - } -} - -func intersectIteratorMorphism(it graph.Iterator) morphism { - return morphism{ - "iterator", - func() morphism { return intersectIteratorMorphism(it) }, - func(subIt graph.Iterator) graph.Iterator { - and := iterator.NewAnd() - and.AddSubIterator(it) - and.AddSubIterator(subIt) - return and - }, - } -} - -func andMorphism(path *Path) morphism { - return morphism{ - "and", - func() morphism { return andMorphism(path) }, - func(it graph.Iterator) graph.Iterator { - subIt := path.BuildIterator() - and := iterator.NewAnd() - and.AddSubIterator(it) - and.AddSubIterator(subIt) - return and - }, - } -} - -func orMorphism(path *Path) morphism { - return morphism{ - "or", - func() morphism { return orMorphism(path) }, - func(it graph.Iterator) graph.Iterator { - subIt := path.BuildIterator() - and := iterator.NewOr() - and.AddSubIterator(it) - and.AddSubIterator(subIt) - return and - }, - } -} - -func inOutIterator(viaPath *Path, reverse bool) graph.MorphismFunc { - return func(base graph.Iterator) graph.Iterator { - in, out := quad.Subject, quad.Object - if reverse { - in, out = out, in - } - lto := iterator.NewLinksTo(viaPath.qs, base, in) - and := iterator.NewAnd() - and.AddSubIterator(iterator.NewLinksTo(viaPath.qs, viaPath.BuildIterator(), quad.Predicate)) - and.AddSubIterator(lto) - return iterator.NewHasA(viaPath.qs, and, out) - } -} - -func buildViaPath(qs graph.QuadStore, via ...interface{}) *Path { - if len(via) == 0 { - return PathFromIterator(qs, qs.NodesAllIterator()) - } else if len(via) == 1 { - v := via[0] - if path, ok := v.(*Path); ok { - return path - } else if str, ok := v.(string); ok { - return StartPath(qs, str) - } else { - panic("Invalid type passed to buildViaPath.") - } - } - var strings []string - for _, s := range via { - if str, ok := s.(string); ok { - strings = append(strings, str) - } else { - panic("Non-string type passed to long Via path") - } - } - return StartPath(qs, strings...) -} diff --git a/graph/api/api_test.go b/graph/api/api_test.go deleted file mode 100644 index 09dbe1f..0000000 --- a/graph/api/api_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// 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 api - -import ( - "reflect" - "sort" - "testing" - - "github.com/google/cayley/graph" - "github.com/google/cayley/quad" - - _ "github.com/google/cayley/graph/memstore" - _ "github.com/google/cayley/writer" -) - -var simpleGraph = []quad.Quad{ - {"A", "follows", "B", ""}, - {"C", "follows", "B", ""}, - {"C", "follows", "D", ""}, - {"D", "follows", "B", ""}, - {"B", "follows", "F", ""}, - {"F", "follows", "G", ""}, - {"D", "follows", "G", ""}, - {"E", "follows", "F", ""}, - {"B", "status", "cool", "status_graph"}, - {"D", "status", "cool", "status_graph"}, - {"G", "status", "cool", "status_graph"}, - {"predicates", "are", "follows", ""}, - {"predicates", "are", "status", ""}, -} - -func makeTestStore(data []quad.Quad) graph.QuadStore { - qs, _ := graph.NewQuadStore("memstore", "", nil) - w, _ := graph.NewQuadWriter("single", qs, nil) - for _, t := range data { - w.AddQuad(t) - } - return qs -} - -func runTopLevel(path *Path) []string { - var out []string - it := path.BuildIterator() - it, _ = it.Optimize() - for graph.Next(it) { - v := path.qs.NameOf(it.Result()) - out = append(out, v) - } - return out -} - -type test struct { - message string - path *Path - expect []string -} - -func testSet(qs graph.QuadStore) []test { - return []test{ - { - message: "use out", - path: StartPath(qs, "A").Out("follows"), - expect: []string{"B"}, - }, - { - message: "use in", - path: StartPath(qs, "B").In("follows"), - expect: []string{"A", "C", "D"}, - }, - { - message: "use path Out", - path: StartPath(qs, "B").Out(StartPath(qs, "predicates").Out("are")), - expect: []string{"F", "cool"}, - }, - { - message: "in", - path: StartPath(qs, "D").Out("follows").And( - StartPath(qs, "C").Out("follows")), - expect: []string{"B"}, - }, - } -} - -func TestMorphisms(t *testing.T) { - qs := makeTestStore(simpleGraph) - for _, test := range testSet(qs) { - got := runTopLevel(test.path) - sort.Strings(got) - sort.Strings(test.expect) - if !reflect.DeepEqual(got, test.expect) { - t.Errorf("Failed to %s, got: %v expected: %v", test.message, got, test.expect) - } - } -} diff --git a/graph/path/path.go b/graph/path/path.go new file mode 100644 index 0000000..57b4c6e --- /dev/null +++ b/graph/path/path.go @@ -0,0 +1,229 @@ +// 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 path + +import ( + "github.com/google/cayley/graph" + "github.com/google/cayley/graph/iterator" + "github.com/google/cayley/quad" +) + +type morphism struct { + Name string + Reversal func() morphism + Apply graph.MorphismFunc +} + +type Path struct { + stack []morphism + qs graph.QuadStore +} + +func StartPath(qs graph.QuadStore, nodes ...string) *Path { + return &Path{ + stack: []morphism{ + isMorphism(qs, nodes...), + }, + qs: qs, + } +} + +func PathFromIterator(qs graph.QuadStore, it graph.Iterator) *Path { + return &Path{ + stack: []morphism{ + intersectIteratorMorphism(it), + }, + qs: qs, + } +} + +func NewPath(qs graph.QuadStore) *Path { + return &Path{ + qs: qs, + } +} + +func (p *Path) Reverse() *Path { + newPath := NewPath(p.qs) + for i := len(p.stack) - 1; i >= 0; i-- { + newPath.stack = append(newPath.stack, p.stack[i].Reversal()) + } + return newPath +} + +func (p *Path) Tag(tags ...string) *Path { + p.stack = append(p.stack, tagMorphism(tags...)) + return p +} + +func (p *Path) Out(via ...interface{}) *Path { + p.stack = append(p.stack, outMorphism(p.qs, via...)) + return p +} +func (p *Path) In(via ...interface{}) *Path { + p.stack = append(p.stack, inMorphism(p.qs, via...)) + return p +} + +func (p *Path) And(path *Path) *Path { + p.stack = append(p.stack, andMorphism(path)) + return p +} + +func (p *Path) Or(path *Path) *Path { + p.stack = append(p.stack, orMorphism(path)) + return p +} + +func (p *Path) BuildIterator() graph.Iterator { + f := p.MorphismFunc() + return f(p.qs.NodesAllIterator()) +} + +func (p *Path) MorphismFunc() graph.MorphismFunc { + return func(it graph.Iterator) graph.Iterator { + i := it.Clone() + for _, m := range p.stack { + i = m.Apply(i) + } + return i + } +} + +func isMorphism(qs graph.QuadStore, nodes ...string) morphism { + return morphism{ + "is", + func() morphism { return isMorphism(qs, nodes...) }, + func(it graph.Iterator) graph.Iterator { + fixed := qs.FixedIterator() + for _, n := range nodes { + fixed.Add(qs.ValueOf(n)) + } + and := iterator.NewAnd() + and.AddSubIterator(fixed) + and.AddSubIterator(it) + return and + }, + } +} + +func tagMorphism(tags ...string) morphism { + return morphism{ + "tag", + func() morphism { return tagMorphism(tags...) }, + func(it graph.Iterator) graph.Iterator { + for _, t := range tags { + it.Tagger().Add(t) + } + return it + }} +} + +func outMorphism(qs graph.QuadStore, via ...interface{}) morphism { + path := buildViaPath(qs, via...) + return morphism{ + "out", + func() morphism { return inMorphism(qs, via...) }, + inOutIterator(path, false), + } +} + +func inMorphism(qs graph.QuadStore, via ...interface{}) morphism { + path := buildViaPath(qs, via...) + return morphism{ + "in", + func() morphism { return outMorphism(qs, via...) }, + inOutIterator(path, true), + } +} + +func intersectIteratorMorphism(it graph.Iterator) morphism { + return morphism{ + "iterator", + func() morphism { return intersectIteratorMorphism(it) }, + func(subIt graph.Iterator) graph.Iterator { + and := iterator.NewAnd() + and.AddSubIterator(it) + and.AddSubIterator(subIt) + return and + }, + } +} + +func andMorphism(path *Path) morphism { + return morphism{ + "and", + func() morphism { return andMorphism(path) }, + func(it graph.Iterator) graph.Iterator { + subIt := path.BuildIterator() + and := iterator.NewAnd() + and.AddSubIterator(it) + and.AddSubIterator(subIt) + return and + }, + } +} + +func orMorphism(path *Path) morphism { + return morphism{ + "or", + func() morphism { return orMorphism(path) }, + func(it graph.Iterator) graph.Iterator { + subIt := path.BuildIterator() + and := iterator.NewOr() + and.AddSubIterator(it) + and.AddSubIterator(subIt) + return and + }, + } +} + +func inOutIterator(viaPath *Path, reverse bool) graph.MorphismFunc { + return func(base graph.Iterator) graph.Iterator { + in, out := quad.Subject, quad.Object + if reverse { + in, out = out, in + } + lto := iterator.NewLinksTo(viaPath.qs, base, in) + and := iterator.NewAnd() + and.AddSubIterator(iterator.NewLinksTo(viaPath.qs, viaPath.BuildIterator(), quad.Predicate)) + and.AddSubIterator(lto) + return iterator.NewHasA(viaPath.qs, and, out) + } +} + +func buildViaPath(qs graph.QuadStore, via ...interface{}) *Path { + if len(via) == 0 { + return PathFromIterator(qs, qs.NodesAllIterator()) + } else if len(via) == 1 { + v := via[0] + if path, ok := v.(*Path); ok { + return path + } else if str, ok := v.(string); ok { + return StartPath(qs, str) + } else { + panic("Invalid type passed to buildViaPath.") + } + } + var strings []string + for _, s := range via { + if str, ok := s.(string); ok { + strings = append(strings, str) + } else { + panic("Non-string type passed to long Via path") + } + } + return StartPath(qs, strings...) +} diff --git a/graph/path/path_test.go b/graph/path/path_test.go new file mode 100644 index 0000000..c67ea15 --- /dev/null +++ b/graph/path/path_test.go @@ -0,0 +1,107 @@ +// 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 path + +import ( + "reflect" + "sort" + "testing" + + "github.com/google/cayley/graph" + "github.com/google/cayley/quad" + + _ "github.com/google/cayley/graph/memstore" + _ "github.com/google/cayley/writer" +) + +var simpleGraph = []quad.Quad{ + {"A", "follows", "B", ""}, + {"C", "follows", "B", ""}, + {"C", "follows", "D", ""}, + {"D", "follows", "B", ""}, + {"B", "follows", "F", ""}, + {"F", "follows", "G", ""}, + {"D", "follows", "G", ""}, + {"E", "follows", "F", ""}, + {"B", "status", "cool", "status_graph"}, + {"D", "status", "cool", "status_graph"}, + {"G", "status", "cool", "status_graph"}, + {"predicates", "are", "follows", ""}, + {"predicates", "are", "status", ""}, +} + +func makeTestStore(data []quad.Quad) graph.QuadStore { + qs, _ := graph.NewQuadStore("memstore", "", nil) + w, _ := graph.NewQuadWriter("single", qs, nil) + for _, t := range data { + w.AddQuad(t) + } + return qs +} + +func runTopLevel(path *Path) []string { + var out []string + it := path.BuildIterator() + it, _ = it.Optimize() + for graph.Next(it) { + v := path.qs.NameOf(it.Result()) + out = append(out, v) + } + return out +} + +type test struct { + message string + path *Path + expect []string +} + +func testSet(qs graph.QuadStore) []test { + return []test{ + { + message: "use out", + path: StartPath(qs, "A").Out("follows"), + expect: []string{"B"}, + }, + { + message: "use in", + path: StartPath(qs, "B").In("follows"), + expect: []string{"A", "C", "D"}, + }, + { + message: "use path Out", + path: StartPath(qs, "B").Out(StartPath(qs, "predicates").Out("are")), + expect: []string{"F", "cool"}, + }, + { + message: "in", + path: StartPath(qs, "D").Out("follows").And( + StartPath(qs, "C").Out("follows")), + expect: []string{"B"}, + }, + } +} + +func TestMorphisms(t *testing.T) { + qs := makeTestStore(simpleGraph) + for _, test := range testSet(qs) { + got := runTopLevel(test.path) + sort.Strings(got) + sort.Strings(test.expect) + if !reflect.DeepEqual(got, test.expect) { + t.Errorf("Failed to %s, got: %v expected: %v", test.message, got, test.expect) + } + } +}