graph/formats/rdf: add Repeat and Len methods
MODIFY
graph/formats/rdf/graph_example_test.go
graph/formats/rdf/graph_example_test.go
@@ -154,17 +154,15 @@ func ExampleQuery() {
}
// g.V().has('name', 'heracles').repeat(out('father')).emit().values('name')
- q := heracles
- for i := 0; ; i++ {
+ var i int
+ heracles.Repeat(func(q rdf.Query) (rdf.Query, bool) {
q = q.Out(father)
- results := q.Out(name).Result()
- if len(results) == 0 {
- break
- }
- for _, r := range results {
+ for _, r := range q.Out(name).Result() {
fmt.Printf("Heracles' lineage %d: %s\n", i, r.Value)
}
- }
+ i++
+ return q, true
+ })
// parents and typ are helper filters for queries below.
parents := func(s *rdf.Statement) bool {

MODIFY
graph/formats/rdf/query.go
graph/formats/rdf/query.go
@@ -232,6 +232,29 @@ func (q Query) Not(p Query) Query {
return r
}
+// Repeat repeatedly calls fn on q until the set of results is empty or
+// ok is false, and then returns the result. If the last non-empty result
+// is wanted, fn should return its input and false when the partial
+// traversal returns an empty result.
+//
+// result := start.Repeat(func(q rdf.Query) (rdf.Query, bool) {
+// r := q.Out(condition)
+// if r.Len() == 0 {
+// return q, false
+// }
+// return r, true
+// }).Result()
+//
+func (q Query) Repeat(fn func(Query) (q Query, ok bool)) Query {
+ for {
+ var ok bool
+ q, ok = fn(q)
+ if !ok || len(q.terms) == 0 {
+ return q
+ }
+ }
+}
+
// Unique returns a copy of the receiver that contains only one instance
// of each term.
func (q Query) Unique() Query {
@@ -245,6 +268,11 @@ func (q Query) Unique() Query {
return r
}
+// Len returns the number of terms held by the query.
+func (q Query) Len() int {
+ return len(q.terms)
+}
+
// Result returns the terms held by the query.
func (q Query) Result() []Term {
return q.terms

MODIFY
graph/formats/rdf/query_test.go
graph/formats/rdf/query_test.go
@@ -181,6 +181,72 @@ func TestQueryNot(t *testing.T) {
}
}
+func TestQueryRepeat(t *testing.T) {
+ const filterTestGraph = `
+<ex:a> <p:1> <ex:b> .
+<ex:b> <p:1> <ex:c> .
+<ex:c> <p:1> <ex:d> .
+<ex:d> <p:1> <ex:e> .
+<ex:e> <p:1> <ex:f> .
+<ex:a> <p:2> <ex:_b> .
+<ex:b> <p:2> <ex:_c> .
+<ex:c> <p:2> <ex:_d> .
+<ex:d> <p:2> <ex:_e> .
+<ex:e> <p:2> <ex:_f> .
+`
+
+ want := []string{"<ex:a>", "<ex:b>", "<ex:c>", "<ex:d>", "<ex:e>", "<ex:f>"}
+
+ g, err := graphFromStatements(filterTestGraph)
+ if err != nil {
+ t.Fatalf("unexpected error constructing graph: %v", err)
+ }
+ start, ok := g.TermFor("<ex:a>")
+ if !ok {
+ t.Fatal("could not get start term")
+ }
+ for _, limit := range []int{0, 1, 2, 5, 100} {
+ got := []string{}
+ var i int
+ result := g.Query(start).Repeat(func(q Query) (Query, bool) {
+ if i >= limit {
+ return q, false
+ }
+ i++
+ q = q.Out(func(s *Statement) bool {
+ ok := s.Predicate.Value == "<p:1>"
+ if ok {
+ got = append(got, s.Object.Value)
+ }
+ return ok
+ })
+ return q, true
+ }).Unique().Result()
+
+ n := limit
+ if n >= len(want) {
+ n = len(want) - 1
+ }
+ if !reflect.DeepEqual(got, want[1:n+1]) {
+ t.Errorf("unexpected capture for limit=%d: got:%v want:%v",
+ limit, got, want[:n])
+ }
+
+ switch {
+ case limit < len(want):
+ if len(result) == 0 || result[0].Value != want[i] {
+ t.Errorf("unexpected result for limit=%d: got:%v want:%v",
+ limit, result[0], want[i])
+ }
+ default:
+ if len(result) != 0 {
+ t.Errorf("unexpected result for limit=%d: got: %v want:none",
+ limit, result[0])
+ }
+ }
+ }
+}
+
var uniqueTests = []struct {
name string
in []Term

graph/formats/rdf: add Repeat and Len methods