func TestRemoveQuad(t *testing.T) { qs, w, _ := makeTestStore(simpleGraph) w.RemoveQuad(quad.Quad{ Subject: "E", Predicate: "follows", Object: "F", Label: "", }) fixed := qs.FixedIterator() fixed.Add(qs.ValueOf("E")) fixed2 := qs.FixedIterator() fixed2.Add(qs.ValueOf("follows")) innerAnd := iterator.NewAnd() 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 hasMorphism(via interface{}, nodes ...string) morphism { return morphism{ Name: "has", Reversal: func() morphism { return hasMorphism(via, nodes...) }, Apply: func(qs graph.QuadStore, it graph.Iterator) graph.Iterator { var sub graph.Iterator if len(nodes) == 0 { sub = qs.NodesAllIterator() } else { fixed := qs.FixedIterator() for _, n := range nodes { fixed.Add(qs.ValueOf(n)) } sub = fixed } var viaPath *Path if via != nil { viaPath = buildViaPath(qs, via) } else { viaPath = buildViaPath(qs) } subAnd := iterator.NewAnd(qs) subAnd.AddSubIterator(iterator.NewLinksTo(qs, sub, quad.Object)) subAnd.AddSubIterator(iterator.NewLinksTo(qs, viaPath.BuildIterator(), quad.Predicate)) hasa := iterator.NewHasA(qs, subAnd, quad.Subject) and := iterator.NewAnd(qs) and.AddSubIterator(it) and.AddSubIterator(hasa) return and }, } }
func TestRemoveQuad(t *testing.T) { qs, w, _ := makeTestStore(simpleGraph) err := w.RemoveQuad(quad.Make( "E", "follows", "F", "", )) if err != nil { t.Error("Couldn't remove quad", err) } fixed := qs.FixedIterator() fixed.Add(qs.ValueOf(quad.Raw("E"))) fixed2 := qs.FixedIterator() fixed2.Add(qs.ValueOf(quad.Raw("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 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 TestIteratorsAndNextResultOrderA(t *testing.T) { glog.Info("\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 inOutIterator(viaPath *Path, it graph.Iterator, reverse bool) graph.Iterator { in, out := quad.Subject, quad.Object if reverse { in, out = out, in } lto := iterator.NewLinksTo(viaPath.qs, it, in) and := iterator.NewAnd(viaPath.qs) and.AddSubIterator(iterator.NewLinksTo(viaPath.qs, viaPath.BuildIterator(), quad.Predicate)) and.AddSubIterator(lto) return iterator.NewHasA(viaPath.qs, and, out) }
func TestIteratorsAndNextResultOrderA(t *testing.T) { ts, _ := makeTestStore(simpleGraph) fixed := ts.FixedIterator() fixed.Add(ts.ValueOf("C")) fixed2 := ts.FixedIterator() fixed2.Add(ts.ValueOf("follows")) all := ts.NodesAllIterator() innerAnd := iterator.NewAnd() innerAnd.AddSubIterator(iterator.NewLinksTo(ts, fixed2, graph.Predicate)) innerAnd.AddSubIterator(iterator.NewLinksTo(ts, all, graph.Object)) hasa := iterator.NewHasA(ts, innerAnd, graph.Subject) outerAnd := iterator.NewAnd() outerAnd.AddSubIterator(fixed) outerAnd.AddSubIterator(hasa) val, ok := outerAnd.Next() if !ok { t.Error("Expected one matching subtree") } if ts.NameOf(val) != "C" { t.Errorf("Matching subtree should be %s, got %s", "barak", ts.NameOf(val)) } var ( got []string expect = []string{"B", "D"} ) for { got = append(got, ts.NameOf(all.Result())) if !outerAnd.NextResult() { break } } sort.Strings(got) if !reflect.DeepEqual(got, expect) { t.Errorf("Unexpected result, got:%q expect:%q", got, expect) } val, ok = outerAnd.Next() if ok { t.Error("More than one possible top level output?") } }
func inOutIterator(viaPath *Path, from graph.Iterator, inIterator bool) graph.Iterator { start, goal := quad.Subject, quad.Object if inIterator { start, goal = goal, start } viaIter := viaPath.BuildIterator() source := iterator.NewLinksTo(viaPath.qs, from, start) trail := iterator.NewLinksTo(viaPath.qs, viaIter, quad.Predicate) route := join(viaPath.qs, source, trail) return iterator.NewHasA(viaPath.qs, route, goal) }
func TestLinksToOptimization(t *testing.T) { ts, _ := makeTestStore(simpleGraph) fixed := ts.FixedIterator() fixed.Add(ts.ValueOf("cool")) lto := iterator.NewLinksTo(ts, fixed, graph.Object) lto.AddTag("foo") newIt, changed := lto.Optimize() if !changed { t.Error("Iterator didn't change") } if newIt.Type() != Type() { t.Fatal("Didn't swap out to LLRB") } v := newIt.(*Iterator) v_clone := v.Clone() if v_clone.DebugString(0) != v.DebugString(0) { t.Fatal("Wrong iterator. Got ", v_clone.DebugString(0)) } if len(v_clone.Tags()) < 1 || v_clone.Tags()[0] != "foo" { t.Fatal("Tag on LinksTo did not persist") } }
func TestLinksToOptimization(t *testing.T) { qs, _, _ := makeTestStore(simpleGraph) fixed := qs.FixedIterator() fixed.Add(qs.ValueOf("cool")) lto := iterator.NewLinksTo(qs, fixed, quad.Object) lto.Tagger().Add("foo") newIt, changed := lto.Optimize() if !changed { t.Error("Iterator didn't change") } if newIt.Type() != Type() { t.Fatal("Didn't swap out to LLRB") } v := newIt.(*Iterator) vClone := v.Clone() origDesc := v.Describe() cloneDesc := vClone.Describe() origDesc.UID, cloneDesc.UID = 0, 0 // We are more strict now, so fake UID equality. if !reflect.DeepEqual(cloneDesc, origDesc) { t.Fatalf("Unexpected iterator description.\ngot: %#v\nexpect: %#v", cloneDesc, origDesc) } vt := vClone.Tagger() if len(vt.Tags()) < 1 || vt.Tags()[0] != "foo" { t.Fatal("Tag on LinksTo did not persist") } }
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?") }
func buildInOutPredicateIterator(obj *otto.Object, qs graph.QuadStore, base graph.Iterator, isReverse bool) graph.Iterator { dir := quad.Subject if isReverse { dir = quad.Object } lto := iterator.NewLinksTo(qs, base, dir) hasa := iterator.NewHasA(qs, lto, quad.Predicate) return iterator.NewUnique(hasa) }
func inOutIterator(viaPath *Path, from graph.Iterator, inIterator bool, tags []string) 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) route := join(viaPath.qs, source, trail) return iterator.NewHasA(viaPath.qs, route, goal) }
func TestIteratorsAndNextResultOrderA(t *testing.T) { ts := MakeTestingMemstore() fixed := ts.FixedIterator() fixed.AddValue(ts.GetIdFor("C")) all := ts.GetNodesAllIterator() lto := iterator.NewLinksTo(ts, all, graph.Object) innerAnd := iterator.NewAnd() fixed2 := ts.FixedIterator() fixed2.AddValue(ts.GetIdFor("follows")) lto2 := iterator.NewLinksTo(ts, fixed2, graph.Predicate) innerAnd.AddSubIterator(lto2) innerAnd.AddSubIterator(lto) hasa := iterator.NewHasA(ts, innerAnd, graph.Subject) outerAnd := iterator.NewAnd() outerAnd.AddSubIterator(fixed) outerAnd.AddSubIterator(hasa) val, ok := outerAnd.Next() if !ok { t.Error("Expected one matching subtree") } if ts.GetNameFor(val) != "C" { t.Errorf("Matching subtree should be %s, got %s", "barak", ts.GetNameFor(val)) } expected := make([]string, 2) expected[0] = "B" expected[1] = "D" actualOut := make([]string, 2) actualOut[0] = ts.GetNameFor(all.LastResult()) nresultOk := outerAnd.NextResult() if !nresultOk { t.Error("Expected two results got one") } actualOut[1] = ts.GetNameFor(all.LastResult()) nresultOk = outerAnd.NextResult() if nresultOk { t.Error("Expected two results got three") } CompareStringSlices(t, expected, actualOut) val, ok = outerAnd.Next() if ok { t.Error("More than one possible top level output?") } }
func TestRemoveTriple(t *testing.T) { ts := MakeTestingMemstore() ts.RemoveTriple(&graph.Triple{"E", "follows", "F", ""}) fixed := ts.FixedIterator() fixed.AddValue(ts.GetIdFor("E")) lto := iterator.NewLinksTo(ts, fixed, graph.Subject) fixed2 := ts.FixedIterator() fixed2.AddValue(ts.GetIdFor("follows")) lto2 := iterator.NewLinksTo(ts, fixed2, graph.Predicate) innerAnd := iterator.NewAnd() innerAnd.AddSubIterator(lto2) innerAnd.AddSubIterator(lto) hasa := iterator.NewHasA(ts, innerAnd, graph.Object) newIt, _ := hasa.Optimize() _, ok := newIt.Next() if ok { t.Error("E should not have any followers.") } }
func buildInOutIterator(obj *otto.Object, ts graph.TripleStore, base graph.Iterator, isReverse bool) graph.Iterator { argList, _ := obj.Get("_gremlin_values") if argList.Class() != "GoArray" { glog.Errorln("How is arglist not an array? Return nothing.", argList.Class()) return iterator.NewNull() } argArray := argList.Object() lengthVal, _ := argArray.Get("length") length, _ := lengthVal.ToInteger() var predicateNodeIterator graph.Iterator if length == 0 { predicateNodeIterator = ts.NodesAllIterator() } else { zero, _ := argArray.Get("0") predicateNodeIterator = buildIteratorFromValue(zero, ts) } if length >= 2 { var tags []string one, _ := argArray.Get("1") if one.IsString() { s, _ := one.ToString() tags = append(tags, s) } else if one.Class() == "Array" { tags = makeListOfStringsFromArrayValue(one.Object()) } for _, tag := range tags { predicateNodeIterator.Tagger().Add(tag) } } in, out := quad.Subject, quad.Object if isReverse { in, out = out, in } lto := iterator.NewLinksTo(ts, base, in) and := iterator.NewAnd() and.AddSubIterator(iterator.NewLinksTo(ts, predicateNodeIterator, quad.Predicate)) and.AddSubIterator(lto) return iterator.NewHasA(ts, and, out) }
// hasMorphism is the set of nodes that is reachable via either a *Path, a // single node.(string) or a list of nodes.([]string). func hasMorphism(via interface{}, nodes ...string) morphism { return morphism{ Name: "has", Reversal: func(ctx *context) (morphism, *context) { return hasMorphism(via, nodes...), ctx }, Apply: func(qs graph.QuadStore, in graph.Iterator, ctx *context) (graph.Iterator, *context) { 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 }() trail := iterator.NewLinksTo(qs, viaIter, quad.Predicate) dest := iterator.NewLinksTo(qs, ends, quad.Object) // 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, quad.Subject) return join(qs, in, has), ctx } // This looks backwards. That's OK-- see the note above. route := join(qs, dest, trail) has := iterator.NewHasA(qs, route, quad.Subject) return join(qs, has, in), ctx }, } }
func TestOptimize(t *testing.T) { var ts *TripleStore var lto graph.Iterator var tmpDir string Convey("Given a prepared database", t, func() { tmpDir, _ = ioutil.TempDir(os.TempDir(), "cayley_test") t.Log(tmpDir) defer os.RemoveAll(tmpDir) ok := CreateNewLevelDB(tmpDir) So(ok, ShouldBeTrue) ts = NewTripleStore(tmpDir, nil) ts.AddTripleSet(makeTripleSet()) Convey("With an linksto-fixed pair", func() { fixed := ts.FixedIterator() fixed.AddValue(ts.GetIdFor("F")) fixed.AddTag("internal") lto = iterator.NewLinksTo(ts, fixed, graph.Object) Convey("Creates an appropriate iterator", func() { oldIt := lto.Clone() newIt, ok := lto.Optimize() So(ok, ShouldBeTrue) So(newIt.Type(), ShouldEqual, "leveldb") Convey("Containing the right things", func() { afterOp := extractTripleFromIterator(ts, newIt) beforeOp := extractTripleFromIterator(ts, oldIt) sort.Strings(afterOp) sort.Strings(beforeOp) So(afterOp, ShouldResemble, beforeOp) }) Convey("With the correct tags", func() { oldIt.Next() newIt.Next() oldResults := make(map[string]graph.TSVal) oldIt.TagResults(&oldResults) newResults := make(map[string]graph.TSVal) oldIt.TagResults(&newResults) So(newResults, ShouldResemble, oldResults) }) }) }) }) }
func buildHas(qs graph.QuadStore, via interface{}, in graph.Iterator, reverse bool, nodes []quad.Value) 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 buildSave(qs graph.QuadStore, via interface{}, tag string, it graph.Iterator, reverse bool) graph.Iterator { all := qs.NodesAllIterator() all.Tagger().Add(tag) node, allDir := quad.Subject, quad.Object var viaPath *Path if via != nil { viaPath = buildViaPath(qs, via) } else { viaPath = buildViaPath(qs) } if reverse { node, allDir = allDir, node } lto := iterator.NewLinksTo(qs, all, allDir) subAnd := iterator.NewAnd(qs) subAnd.AddSubIterator(iterator.NewLinksTo(qs, viaPath.BuildIterator(), quad.Predicate)) subAnd.AddSubIterator(lto) hasa := iterator.NewHasA(qs, subAnd, node) and := iterator.NewAnd(qs) and.AddSubIterator(hasa) and.AddSubIterator(it) return and }
func TestRemoveTriple(t *testing.T) { ts, _ := makeTestStore(simpleGraph) ts.RemoveTriple(&graph.Triple{"E", "follows", "F", ""}) fixed := ts.FixedIterator() fixed.Add(ts.ValueOf("E")) fixed2 := ts.FixedIterator() fixed2.Add(ts.ValueOf("follows")) innerAnd := iterator.NewAnd() innerAnd.AddSubIterator(iterator.NewLinksTo(ts, fixed, graph.Subject)) innerAnd.AddSubIterator(iterator.NewLinksTo(ts, fixed2, graph.Predicate)) hasa := iterator.NewHasA(ts, innerAnd, graph.Object) newIt, _ := hasa.Optimize() _, ok := newIt.Next() if ok { t.Error("E should not have any followers.") } }
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 TestOptimize(t *testing.T) { tmpFile, _ := ioutil.TempFile(os.TempDir(), "cayley_test") t.Log(tmpFile.Name()) defer os.RemoveAll(tmpFile.Name()) err := createNewBolt(tmpFile.Name(), nil) if err != nil { t.Fatalf("Failed to create working directory") } qs, err := newQuadStore(tmpFile.Name(), nil) if qs == nil || err != nil { t.Error("Failed to create leveldb QuadStore.") } w, _ := writer.NewSingleReplication(qs, nil) w.AddQuadSet(makeQuadSet()) // With an linksto-fixed pair fixed := qs.FixedIterator() fixed.Add(qs.ValueOf("F")) fixed.Tagger().Add("internal") lto := iterator.NewLinksTo(qs, fixed, quad.Object) oldIt := lto.Clone() newIt, ok := lto.Optimize() if !ok { t.Errorf("Failed to optimize iterator") } if newIt.Type() != Type() { t.Errorf("Optimized iterator type does not match original, got:%v expect:%v", newIt.Type(), Type()) } newQuads := iteratedQuads(qs, newIt) oldQuads := iteratedQuads(qs, oldIt) if !reflect.DeepEqual(newQuads, oldQuads) { t.Errorf("Optimized iteration does not match original") } graph.Next(oldIt) oldResults := make(map[string]graph.Value) oldIt.TagResults(oldResults) graph.Next(newIt) newResults := make(map[string]graph.Value) newIt.TagResults(newResults) if !reflect.DeepEqual(newResults, oldResults) { t.Errorf("Discordant tag results, new:%v old:%v", newResults, oldResults) } }
func TestOptimize(t *testing.T) { tmpDir, _ := ioutil.TempDir(os.TempDir(), "cayley_test") t.Log(tmpDir) defer os.RemoveAll(tmpDir) err := createNewLevelDB(tmpDir, nil) if err != nil { t.Fatalf("Failed to create working directory") } ts, err := newTripleStore(tmpDir, nil) if ts == nil || err != nil { t.Error("Failed to create leveldb TripleStore.") } ts.AddTripleSet(makeTripleSet()) // With an linksto-fixed pair fixed := ts.FixedIterator() fixed.Add(ts.ValueOf("F")) fixed.AddTag("internal") lto := iterator.NewLinksTo(ts, fixed, graph.Object) oldIt := lto.Clone() newIt, ok := lto.Optimize() if !ok { t.Errorf("Failed to optimize iterator") } if newIt.Type() != Type() { t.Errorf("Optimized iterator type does not match original, got:%v expect:%v", newIt.Type(), Type()) } newTriples := iteratedTriples(ts, newIt) oldTriples := iteratedTriples(ts, oldIt) if !reflect.DeepEqual(newTriples, oldTriples) { t.Errorf("Optimized iteration does not match original") } oldIt.Next() oldResults := make(map[string]graph.Value) oldIt.TagResults(oldResults) newIt.Next() newResults := make(map[string]graph.Value) newIt.TagResults(newResults) if !reflect.DeepEqual(newResults, oldResults) { t.Errorf("Discordant tag results, new:%v old:%v", newResults, oldResults) } }
// 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 TestOptimize(t *testing.T) { qs, opts, closer := makeBolt(t) defer closer() graphtest.MakeWriter(t, qs, opts, graphtest.MakeQuadSet()...) // With an linksto-fixed pair fixed := qs.FixedIterator() fixed.Add(qs.ValueOf(quad.Raw("F"))) fixed.Tagger().Add("internal") lto := iterator.NewLinksTo(qs, fixed, quad.Object) oldIt := lto.Clone() newIt, ok := lto.Optimize() if !ok { t.Errorf("Failed to optimize iterator") } if newIt.Type() != Type() { t.Errorf("Optimized iterator type does not match original, got:%v expect:%v", newIt.Type(), Type()) } newQuads := graphtest.IteratedQuads(t, qs, newIt) oldQuads := graphtest.IteratedQuads(t, qs, oldIt) if !reflect.DeepEqual(newQuads, oldQuads) { t.Errorf("Optimized iteration does not match original") } graph.Next(oldIt) oldResults := make(map[string]graph.Value) oldIt.TagResults(oldResults) graph.Next(newIt) newResults := make(map[string]graph.Value) newIt.TagResults(newResults) if !reflect.DeepEqual(newResults, oldResults) { t.Errorf("Discordant tag results, new:%v old:%v", newResults, oldResults) } }
func buildIteratorTreeHelper(obj *otto.Object, ts graph.TripleStore, base graph.Iterator) graph.Iterator { var it graph.Iterator it = base // TODO: Better error handling kindVal, _ := obj.Get("_gremlin_type") stringArgs := getStringArgs(obj) var subIt graph.Iterator prevVal, _ := obj.Get("_gremlin_prev") if !prevVal.IsObject() { subIt = base } else { subIt = buildIteratorTreeHelper(prevVal.Object(), ts, base) } kind, _ := kindVal.ToString() switch kind { case "vertex": if len(stringArgs) == 0 { it = ts.NodesAllIterator() } else { fixed := ts.FixedIterator() for _, name := range stringArgs { fixed.Add(ts.ValueOf(name)) } it = fixed } case "tag": it = subIt for _, tag := range stringArgs { it.Tagger().Add(tag) } case "save": all := ts.NodesAllIterator() if len(stringArgs) > 2 || len(stringArgs) == 0 { return iterator.NewNull() } if len(stringArgs) == 2 { all.Tagger().Add(stringArgs[1]) } else { all.Tagger().Add(stringArgs[0]) } predFixed := ts.FixedIterator() predFixed.Add(ts.ValueOf(stringArgs[0])) subAnd := iterator.NewAnd() subAnd.AddSubIterator(iterator.NewLinksTo(ts, predFixed, quad.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, all, quad.Object)) hasa := iterator.NewHasA(ts, subAnd, quad.Subject) and := iterator.NewAnd() and.AddSubIterator(hasa) and.AddSubIterator(subIt) it = and case "saver": all := ts.NodesAllIterator() if len(stringArgs) > 2 || len(stringArgs) == 0 { return iterator.NewNull() } if len(stringArgs) == 2 { all.Tagger().Add(stringArgs[1]) } else { all.Tagger().Add(stringArgs[0]) } predFixed := ts.FixedIterator() predFixed.Add(ts.ValueOf(stringArgs[0])) subAnd := iterator.NewAnd() subAnd.AddSubIterator(iterator.NewLinksTo(ts, predFixed, quad.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, all, quad.Subject)) hasa := iterator.NewHasA(ts, subAnd, quad.Object) and := iterator.NewAnd() and.AddSubIterator(hasa) and.AddSubIterator(subIt) it = and case "has": fixed := ts.FixedIterator() if len(stringArgs) < 2 { return iterator.NewNull() } for _, name := range stringArgs[1:] { fixed.Add(ts.ValueOf(name)) } predFixed := ts.FixedIterator() predFixed.Add(ts.ValueOf(stringArgs[0])) subAnd := iterator.NewAnd() subAnd.AddSubIterator(iterator.NewLinksTo(ts, predFixed, quad.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(ts, fixed, quad.Object)) hasa := iterator.NewHasA(ts, subAnd, quad.Subject) and := iterator.NewAnd() and.AddSubIterator(hasa) and.AddSubIterator(subIt) it = and case "morphism": it = base case "and": arg, _ := obj.Get("_gremlin_values") firstArg, _ := arg.Object().Get("0") if !isVertexChain(firstArg.Object()) { return iterator.NewNull() } argIt := buildIteratorTree(firstArg.Object(), ts) and := iterator.NewAnd() and.AddSubIterator(subIt) and.AddSubIterator(argIt) it = and case "back": arg, _ := obj.Get("_gremlin_back_chain") argIt := buildIteratorTree(arg.Object(), ts) and := iterator.NewAnd() and.AddSubIterator(subIt) and.AddSubIterator(argIt) it = and case "is": fixed := ts.FixedIterator() for _, name := range stringArgs { fixed.Add(ts.ValueOf(name)) } and := iterator.NewAnd() and.AddSubIterator(fixed) and.AddSubIterator(subIt) it = and case "or": arg, _ := obj.Get("_gremlin_values") firstArg, _ := arg.Object().Get("0") if !isVertexChain(firstArg.Object()) { return iterator.NewNull() } argIt := buildIteratorTree(firstArg.Object(), ts) or := iterator.NewOr() or.AddSubIterator(subIt) or.AddSubIterator(argIt) it = or case "both": // Hardly the most efficient pattern, but the most general. // Worth looking into an Optimize() optimization here. clone := subIt.Clone() it1 := buildInOutIterator(obj, ts, subIt, false) it2 := buildInOutIterator(obj, ts, clone, true) or := iterator.NewOr() or.AddSubIterator(it1) or.AddSubIterator(it2) it = or case "out": it = buildInOutIterator(obj, ts, subIt, false) case "follow": // Follow a morphism arg, _ := obj.Get("_gremlin_values") firstArg, _ := arg.Object().Get("0") if isVertexChain(firstArg.Object()) { return iterator.NewNull() } it = buildIteratorTreeHelper(firstArg.Object(), ts, subIt) case "followr": // Follow a morphism arg, _ := obj.Get("_gremlin_followr") if isVertexChain(arg.Object()) { return iterator.NewNull() } it = buildIteratorTreeHelper(arg.Object(), ts, subIt) case "in": it = buildInOutIterator(obj, ts, subIt, true) } return it }
func buildIteratorTreeHelper(obj *otto.Object, qs graph.QuadStore, base graph.Iterator) graph.Iterator { // TODO: Better error handling var ( it graph.Iterator subIt graph.Iterator ) if prev, _ := obj.Get("_gremlin_prev"); !prev.IsObject() { subIt = base } else { subIt = buildIteratorTreeHelper(prev.Object(), qs, base) } stringArgs := propertiesOf(obj, "string_args") val, _ := obj.Get("_gremlin_type") switch val.String() { case "vertex": if len(stringArgs) == 0 { it = qs.NodesAllIterator() } else { fixed := qs.FixedIterator() for _, name := range stringArgs { fixed.Add(qs.ValueOf(name)) } it = fixed } case "tag": it = subIt for _, tag := range stringArgs { it.Tagger().Add(tag) } case "save": all := qs.NodesAllIterator() if len(stringArgs) > 2 || len(stringArgs) == 0 { return iterator.NewNull() } if len(stringArgs) == 2 { all.Tagger().Add(stringArgs[1]) } else { all.Tagger().Add(stringArgs[0]) } predFixed := qs.FixedIterator() predFixed.Add(qs.ValueOf(stringArgs[0])) subAnd := iterator.NewAnd(qs) subAnd.AddSubIterator(iterator.NewLinksTo(qs, predFixed, quad.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(qs, all, quad.Object)) hasa := iterator.NewHasA(qs, subAnd, quad.Subject) and := iterator.NewAnd(qs) and.AddSubIterator(hasa) and.AddSubIterator(subIt) it = and case "saver": all := qs.NodesAllIterator() if len(stringArgs) > 2 || len(stringArgs) == 0 { return iterator.NewNull() } if len(stringArgs) == 2 { all.Tagger().Add(stringArgs[1]) } else { all.Tagger().Add(stringArgs[0]) } predFixed := qs.FixedIterator() predFixed.Add(qs.ValueOf(stringArgs[0])) subAnd := iterator.NewAnd(qs) subAnd.AddSubIterator(iterator.NewLinksTo(qs, predFixed, quad.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(qs, all, quad.Subject)) hasa := iterator.NewHasA(qs, subAnd, quad.Object) and := iterator.NewAnd(qs) and.AddSubIterator(hasa) and.AddSubIterator(subIt) it = and case "has": fixed := qs.FixedIterator() if len(stringArgs) < 2 { return iterator.NewNull() } for _, name := range stringArgs[1:] { fixed.Add(qs.ValueOf(name)) } predFixed := qs.FixedIterator() predFixed.Add(qs.ValueOf(stringArgs[0])) subAnd := iterator.NewAnd(qs) subAnd.AddSubIterator(iterator.NewLinksTo(qs, predFixed, quad.Predicate)) subAnd.AddSubIterator(iterator.NewLinksTo(qs, fixed, quad.Object)) hasa := iterator.NewHasA(qs, subAnd, quad.Subject) and := iterator.NewAnd(qs) and.AddSubIterator(hasa) and.AddSubIterator(subIt) it = and case "morphism": it = base case "and": arg, _ := obj.Get("_gremlin_values") firstArg, _ := arg.Object().Get("0") if !isVertexChain(firstArg.Object()) { return iterator.NewNull() } argIt := buildIteratorTree(firstArg.Object(), qs) and := iterator.NewAnd(qs) and.AddSubIterator(subIt) and.AddSubIterator(argIt) it = and case "back": arg, _ := obj.Get("_gremlin_back_chain") argIt := buildIteratorTree(arg.Object(), qs) and := iterator.NewAnd(qs) and.AddSubIterator(subIt) and.AddSubIterator(argIt) it = and case "is": fixed := qs.FixedIterator() for _, name := range stringArgs { fixed.Add(qs.ValueOf(name)) } and := iterator.NewAnd(qs) and.AddSubIterator(fixed) and.AddSubIterator(subIt) it = and case "or": arg, _ := obj.Get("_gremlin_values") firstArg, _ := arg.Object().Get("0") if !isVertexChain(firstArg.Object()) { return iterator.NewNull() } argIt := buildIteratorTree(firstArg.Object(), qs) or := iterator.NewOr() or.AddSubIterator(subIt) or.AddSubIterator(argIt) it = or case "both": // Hardly the most efficient pattern, but the most general. // Worth looking into an Optimize() optimization here. clone := subIt.Clone() it1 := buildInOutIterator(obj, qs, subIt, false) it2 := buildInOutIterator(obj, qs, clone, true) or := iterator.NewOr() or.AddSubIterator(it1) or.AddSubIterator(it2) it = or case "out": it = buildInOutIterator(obj, qs, subIt, false) case "follow": // Follow a morphism arg, _ := obj.Get("_gremlin_values") firstArg, _ := arg.Object().Get("0") if isVertexChain(firstArg.Object()) { return iterator.NewNull() } it = buildIteratorTreeHelper(firstArg.Object(), qs, subIt) case "followr": // Follow a morphism arg, _ := obj.Get("_gremlin_followr") if isVertexChain(arg.Object()) { return iterator.NewNull() } it = buildIteratorTreeHelper(arg.Object(), qs, subIt) case "in": it = buildInOutIterator(obj, qs, subIt, true) case "except": arg, _ := obj.Get("_gremlin_values") firstArg, _ := arg.Object().Get("0") if !isVertexChain(firstArg.Object()) { return iterator.NewNull() } allIt := qs.NodesAllIterator() toComplementIt := buildIteratorTree(firstArg.Object(), qs) notIt := iterator.NewNot(toComplementIt, allIt) and := iterator.NewAnd(qs) and.AddSubIterator(subIt) and.AddSubIterator(notIt) it = and case "in_predicates": it = buildInOutPredicateIterator(obj, qs, subIt, true) case "out_predicates": it = buildInOutPredicateIterator(obj, qs, subIt, false) } if it == nil { panic("Iterator building does not catch the output iterator in some case.") } return it }
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(quad.Raw(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() 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() 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{} } panic("Not reached") }