func buildSave( qs graph.QuadStore, via interface{}, tag string, from graph.Iterator, reverse bool, optional bool, ) graph.Iterator { allNodes := qs.NodesAllIterator() allNodes.Tagger().Add(tag) start, goal := quad.Subject, quad.Object if reverse { start, goal = goal, start } viaIter := buildViaPath(qs, via). BuildIterator() dest := iterator.NewLinksTo(qs, allNodes, goal) trail := iterator.NewLinksTo(qs, viaIter, quad.Predicate) route := join(qs, trail, dest) save := graph.Iterator(iterator.NewHasA(qs, route, start)) if optional { save = iterator.NewOptional(save) } return join(qs, from, save) }
func TestRemoveQuad(t *testing.T) { qs, w, _ := makeTestStore(simpleGraph) err := w.RemoveQuad(quad.Quad{ Subject: "E", Predicate: "follows", Object: "F", Label: "", }) if err != nil { t.Error("Couldn't remove quad", err) } fixed := qs.FixedIterator() fixed.Add(qs.ValueOf("E")) fixed2 := qs.FixedIterator() fixed2.Add(qs.ValueOf("follows")) innerAnd := iterator.NewAnd(qs) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, fixed, quad.Subject)) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, fixed2, quad.Predicate)) hasa := iterator.NewHasA(qs, innerAnd, quad.Object) newIt, _ := hasa.Optimize() if graph.Next(newIt) { t.Error("E should not have any followers.") } }
func TestIteratorsAndNextResultOrderA(t *testing.T) { clog.Infof("\n-----------\n") inst, opts, err := createInstance() defer inst.Close() if err != nil { t.Fatalf("failed to create instance: %v", err) } qs, _, _ := makeTestStore(simpleGraph, opts) if qs.Size() != 11 { t.Fatal("Incorrect number of quads") } fixed := qs.FixedIterator() fixed.Add(qs.ValueOf("C")) fixed2 := qs.FixedIterator() fixed2.Add(qs.ValueOf("follows")) all := qs.NodesAllIterator() innerAnd := iterator.NewAnd(qs) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, fixed2, quad.Predicate)) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, all, quad.Object)) hasa := iterator.NewHasA(qs, innerAnd, quad.Subject) outerAnd := iterator.NewAnd(qs) outerAnd.AddSubIterator(fixed) outerAnd.AddSubIterator(hasa) if !outerAnd.Next() { t.Error("Expected one matching subtree") } val := outerAnd.Result() if qs.NameOf(val) != "C" { t.Errorf("Matching subtree should be %s, got %s", "barak", qs.NameOf(val)) } var ( got []string expect = []string{"B", "D"} ) for { got = append(got, qs.NameOf(all.Result())) if !outerAnd.NextPath() { break } } sort.Strings(got) if !reflect.DeepEqual(got, expect) { t.Errorf("Unexpected result, got:%q expect:%q", got, expect) } if outerAnd.Next() { t.Error("More than one possible top level output?") } }
func buildHas(qs graph.QuadStore, via interface{}, in graph.Iterator, reverse bool, nodes []string) graph.Iterator { viaIter := buildViaPath(qs, via). BuildIterator() ends := func() graph.Iterator { if len(nodes) == 0 { return qs.NodesAllIterator() } fixed := qs.FixedIterator() for _, n := range nodes { fixed.Add(qs.ValueOf(n)) } return fixed }() start, goal := quad.Subject, quad.Object if reverse { start, goal = goal, start } trail := iterator.NewLinksTo(qs, viaIter, quad.Predicate) dest := iterator.NewLinksTo(qs, ends, goal) // If we were given nodes, intersecting with them first will // be extremely cheap-- otherwise, it will be the most expensive // (requiring iteration over all nodes). We have enough info to // make this optimization now since intersections are commutative if len(nodes) == 0 { // Where dest involves an All iterator. route := join(qs, trail, dest) has := iterator.NewHasA(qs, route, start) return join(qs, in, has) } // This looks backwards. That's OK-- see the note above. route := join(qs, dest, trail) has := iterator.NewHasA(qs, route, start) return join(qs, has, in) }
func TestIteratorsAndNextResultOrderA(t *testing.T) { qs, _, _ := makeTestStore(simpleGraph) fixed := qs.FixedIterator() fixed.Add(qs.ValueOf("C")) fixed2 := qs.FixedIterator() fixed2.Add(qs.ValueOf("follows")) all := qs.NodesAllIterator() innerAnd := iterator.NewAnd(qs) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, fixed2, quad.Predicate)) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, all, quad.Object)) hasa := iterator.NewHasA(qs, innerAnd, quad.Subject) outerAnd := iterator.NewAnd(qs) outerAnd.AddSubIterator(fixed) outerAnd.AddSubIterator(hasa) if !outerAnd.Next() { t.Error("Expected one matching subtree") } val := outerAnd.Result() if qs.NameOf(val) != "C" { t.Errorf("Matching subtree should be %s, got %s", "barak", qs.NameOf(val)) } var ( got []string expect = []string{"B", "D"} ) for { got = append(got, qs.NameOf(all.Result())) if !outerAnd.NextPath() { break } } sort.Strings(got) if !reflect.DeepEqual(got, expect) { t.Errorf("Unexpected result, got:%q expect:%q", got, expect) } if outerAnd.Next() { t.Error("More than one possible top level output?") } }
func TestIteratorsAndNextResultOrderA(t testing.TB, gen DatabaseFunc) { qs, opts, closer := gen(t) defer closer() MakeWriter(t, qs, opts, MakeQuadSet()...) require.Equal(t, int64(11), qs.Size(), "Incorrect number of quads") fixed := qs.FixedIterator() fixed.Add(qs.ValueOf(quad.Raw("C"))) fixed2 := qs.FixedIterator() fixed2.Add(qs.ValueOf(quad.Raw("follows"))) all := qs.NodesAllIterator() innerAnd := iterator.NewAnd(qs) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, fixed2, quad.Predicate)) innerAnd.AddSubIterator(iterator.NewLinksTo(qs, all, quad.Object)) hasa := iterator.NewHasA(qs, innerAnd, quad.Subject) outerAnd := iterator.NewAnd(qs) outerAnd.AddSubIterator(fixed) outerAnd.AddSubIterator(hasa) require.True(t, outerAnd.Next(), "Expected one matching subtree") val := outerAnd.Result() require.Equal(t, quad.Raw("C"), qs.NameOf(val)) var ( got []string expect = []string{"B", "D"} ) for { got = append(got, qs.NameOf(all.Result()).String()) if !outerAnd.NextPath() { break } } sort.Strings(got) require.Equal(t, expect, got) require.True(t, !outerAnd.Next(), "More than one possible top level output?") }
// predicatesMorphism iterates to the uniqified set of predicates from // the given set of nodes in the path. func predicatesMorphism(isIn bool) morphism { m := morphism{ Name: "out_predicates", Reversal: func(ctx *context) (morphism, *context) { panic("not implemented: need a function from predicates to their associated edges") }, Apply: func(qs graph.QuadStore, in graph.Iterator, ctx *context) (graph.Iterator, *context) { dir := quad.Subject if isIn { dir = quad.Object } lto := iterator.NewLinksTo(qs, in, dir) hasa := iterator.NewHasA(qs, lto, quad.Predicate) return iterator.NewUnique(hasa), ctx }, } if isIn { m.Name = "in_predicates" } return m }
func inOutIterator(viaPath *Path, from graph.Iterator, inIterator bool, tags []string, ctx *context) graph.Iterator { start, goal := quad.Subject, quad.Object if inIterator { start, goal = goal, start } viaIter := viaPath.BuildIterator() for _, tag := range tags { viaIter.Tagger().Add(tag) } source := iterator.NewLinksTo(viaPath.qs, from, start) trail := iterator.NewLinksTo(viaPath.qs, viaIter, quad.Predicate) var label graph.Iterator if ctx != nil { if ctx.labelSet != nil { labeliter := ctx.labelSet.BuildIteratorOn(viaPath.qs) label = iterator.NewLinksTo(viaPath.qs, labeliter, quad.Label) } } route := join(viaPath.qs, source, trail, label) return iterator.NewHasA(viaPath.qs, route, goal) }
func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path Path) (graph.Iterator, error) { it := iterator.NewAnd(q.ses.qs) it.AddSubIterator(q.ses.qs.NodesAllIterator()) var err error err = nil outputStructure := make(map[string]interface{}) for key, subquery := range query { optional := false outputStructure[key] = nil reverse := false pred := key if strings.HasPrefix(pred, "@") { i := strings.Index(pred, ":") if i != -1 { pred = pred[(i + 1):] } } if strings.HasPrefix(pred, "!") { reverse = true pred = strings.TrimPrefix(pred, "!") } // Other special constructs here var subit graph.Iterator if key == "id" { subit, optional, err = q.buildIteratorTreeInternal(subquery, path.Follow(key)) if err != nil { return nil, err } } else { var builtIt graph.Iterator builtIt, optional, err = q.buildIteratorTreeInternal(subquery, path.Follow(key)) if err != nil { return nil, err } subAnd := iterator.NewAnd(q.ses.qs) predFixed := q.ses.qs.FixedIterator() predFixed.Add(q.ses.qs.ValueOf(pred)) subAnd.AddSubIterator(iterator.NewLinksTo(q.ses.qs, predFixed, quad.Predicate)) if reverse { lto := iterator.NewLinksTo(q.ses.qs, builtIt, quad.Subject) subAnd.AddSubIterator(lto) hasa := iterator.NewHasA(q.ses.qs, subAnd, quad.Object) subit = hasa } else { lto := iterator.NewLinksTo(q.ses.qs, builtIt, quad.Object) subAnd.AddSubIterator(lto) hasa := iterator.NewHasA(q.ses.qs, subAnd, quad.Subject) subit = hasa } } if optional { it.AddSubIterator(iterator.NewOptional(subit)) } else { it.AddSubIterator(subit) } } if err != nil { return nil, err } q.queryStructure[path] = outputStructure return it, nil }
func buildIteratorTree(tree *peg.ExpressionTree, qs graph.QuadStore) graph.Iterator { switch tree.Name { case "Start": return buildIteratorTree(tree.Children[0], qs) case "NodeIdentifier": var out graph.Iterator nodeID := getIdentString(tree) if tree.Children[0].Name == "Variable" { allIt := qs.NodesAllIterator() allIt.Tagger().Add(nodeID) out = allIt } else { n := nodeID if tree.Children[0].Children[0].Name == "ColonIdentifier" { n = nodeID[1:] } fixed := qs.FixedIterator() fixed.Add(qs.ValueOf(n)) out = fixed } return out case "PredIdentifier": i := 0 if tree.Children[0].Name == "Reverse" { //Taken care of below i++ } it := buildIteratorTree(tree.Children[i], qs) lto := iterator.NewLinksTo(qs, it, quad.Predicate) return lto case "RootConstraint": constraintCount := 0 and := iterator.NewAnd(qs) for _, c := range tree.Children { switch c.Name { case "NodeIdentifier": fallthrough case "Constraint": it := buildIteratorTree(c, qs) and.AddSubIterator(it) constraintCount++ continue default: continue } } return and case "Constraint": var hasa *iterator.HasA topLevelDir := quad.Subject subItDir := quad.Object subAnd := iterator.NewAnd(qs) isOptional := false for _, c := range tree.Children { switch c.Name { case "PredIdentifier": if c.Children[0].Name == "Reverse" { topLevelDir = quad.Object subItDir = quad.Subject } it := buildIteratorTree(c, qs) subAnd.AddSubIterator(it) continue case "PredicateKeyword": switch c.Children[0].Name { case "OptionalKeyword": isOptional = true } case "NodeIdentifier": fallthrough case "RootConstraint": it := buildIteratorTree(c, qs) l := iterator.NewLinksTo(qs, it, subItDir) subAnd.AddSubIterator(l) continue default: continue } } hasa = iterator.NewHasA(qs, subAnd, topLevelDir) if isOptional { optional := iterator.NewOptional(hasa) return optional } return hasa default: return &iterator.Null{} } }