func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path Path) (graph.Iterator, error) { it := graph.NewAndIterator() it.AddSubIterator(q.ses.ts.GetNodesAllIterator()) var err error err = nil outputStructure := make(map[string]interface{}) for key, subquery := range query { 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, err = q.buildIteratorTreeInternal(subquery, path.Follow(key)) if err != nil { return nil, err } it.AddSubIterator(subit) } else { subit, err = q.buildIteratorTreeInternal(subquery, path.Follow(key)) if err != nil { return nil, err } subAnd := graph.NewAndIterator() predFixed := q.ses.ts.MakeFixed() predFixed.AddValue(q.ses.ts.GetIdFor(pred)) subAnd.AddSubIterator(graph.NewLinksToIterator(q.ses.ts, predFixed, "p")) if reverse { lto := graph.NewLinksToIterator(q.ses.ts, subit, "s") subAnd.AddSubIterator(lto) hasa := graph.NewHasaIterator(q.ses.ts, subAnd, "o") it.AddSubIterator(hasa) } else { lto := graph.NewLinksToIterator(q.ses.ts, subit, "o") subAnd.AddSubIterator(lto) hasa := graph.NewHasaIterator(q.ses.ts, subAnd, "s") it.AddSubIterator(hasa) } } } if err != nil { return nil, err } q.queryStructure[path] = outputStructure return it, nil }
func TestIteratorsAndNextResultOrderA(t *testing.T) { ts := MakeTestingMemstore() fixed := ts.MakeFixed() fixed.AddValue(ts.GetIdFor("C")) all := ts.GetNodesAllIterator() lto := graph.NewLinksToIterator(ts, all, "o") innerAnd := graph.NewAndIterator() fixed2 := ts.MakeFixed() fixed2.AddValue(ts.GetIdFor("follows")) lto2 := graph.NewLinksToIterator(ts, fixed2, "p") innerAnd.AddSubIterator(lto2) innerAnd.AddSubIterator(lto) hasa := graph.NewHasaIterator(ts, innerAnd, "s") outerAnd := graph.NewAndIterator() 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.MakeTriple("E", "follows", "F", "")) fixed := ts.MakeFixed() fixed.AddValue(ts.GetIdFor("E")) lto := graph.NewLinksToIterator(ts, fixed, "s") fixed2 := ts.MakeFixed() fixed2.AddValue(ts.GetIdFor("follows")) lto2 := graph.NewLinksToIterator(ts, fixed2, "p") innerAnd := graph.NewAndIterator() innerAnd.AddSubIterator(lto2) innerAnd.AddSubIterator(lto) hasa := graph.NewHasaIterator(ts, innerAnd, "o") 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 graph.NewNullIterator() } argArray := argList.Object() lengthVal, _ := argArray.Get("length") length, _ := lengthVal.ToInteger() var predicateNodeIterator graph.Iterator if length == 0 { predicateNodeIterator = ts.GetNodesAllIterator() } 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.AddTag(tag) } } in, out := graph.Subject, graph.Object if isReverse { in, out = out, in } lto := graph.NewLinksToIterator(ts, base, in) and := graph.NewAndIterator() and.AddSubIterator(graph.NewLinksToIterator(ts, predicateNodeIterator, graph.Predicate)) and.AddSubIterator(lto) return graph.NewHasaIterator(ts, and, out) }
func TestOptimize(t *testing.T) { var ts *LevelDBTripleStore 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 = NewDefaultLevelDBTripleStore(tmpDir, nil) ts.AddTripleSet(makeTripleSet()) Convey("With an linksto-fixed pair", func() { fixed := ts.MakeFixed() fixed.AddValue(ts.GetIdFor("F")) fixed.AddTag("internal") lto = graph.NewLinksToIterator(ts, fixed, "o") 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 TestLinksToOptimization(t *testing.T) { ts := MakeTestingMemstore() fixed := ts.MakeFixed() fixed.AddValue(ts.GetIdFor("cool")) lto := graph.NewLinksToIterator(ts, fixed, "o") lto.AddTag("foo") newIt, changed := lto.Optimize() if !changed { t.Error("Iterator didn't change") } if newIt.Type() != "llrb" { 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 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.GetNodesAllIterator() } else { fixed := ts.MakeFixed() for _, name := range stringArgs { fixed.AddValue(ts.GetIdFor(name)) } it = fixed } case "tag": it = subIt for _, tag := range stringArgs { it.AddTag(tag) } case "save": all := ts.GetNodesAllIterator() if len(stringArgs) > 2 || len(stringArgs) == 0 { return graph.NewNullIterator() } if len(stringArgs) == 2 { all.AddTag(stringArgs[1]) } else { all.AddTag(stringArgs[0]) } predFixed := ts.MakeFixed() predFixed.AddValue(ts.GetIdFor(stringArgs[0])) subAnd := graph.NewAndIterator() subAnd.AddSubIterator(graph.NewLinksToIterator(ts, predFixed, graph.Predicate)) subAnd.AddSubIterator(graph.NewLinksToIterator(ts, all, graph.Object)) hasa := graph.NewHasaIterator(ts, subAnd, graph.Subject) and := graph.NewAndIterator() and.AddSubIterator(hasa) and.AddSubIterator(subIt) it = and case "saver": all := ts.GetNodesAllIterator() if len(stringArgs) > 2 || len(stringArgs) == 0 { return graph.NewNullIterator() } if len(stringArgs) == 2 { all.AddTag(stringArgs[1]) } else { all.AddTag(stringArgs[0]) } predFixed := ts.MakeFixed() predFixed.AddValue(ts.GetIdFor(stringArgs[0])) subAnd := graph.NewAndIterator() subAnd.AddSubIterator(graph.NewLinksToIterator(ts, predFixed, graph.Predicate)) subAnd.AddSubIterator(graph.NewLinksToIterator(ts, all, graph.Subject)) hasa := graph.NewHasaIterator(ts, subAnd, graph.Object) and := graph.NewAndIterator() and.AddSubIterator(hasa) and.AddSubIterator(subIt) it = and case "has": fixed := ts.MakeFixed() if len(stringArgs) < 2 { return graph.NewNullIterator() } for _, name := range stringArgs[1:] { fixed.AddValue(ts.GetIdFor(name)) } predFixed := ts.MakeFixed() predFixed.AddValue(ts.GetIdFor(stringArgs[0])) subAnd := graph.NewAndIterator() subAnd.AddSubIterator(graph.NewLinksToIterator(ts, predFixed, graph.Predicate)) subAnd.AddSubIterator(graph.NewLinksToIterator(ts, fixed, graph.Object)) hasa := graph.NewHasaIterator(ts, subAnd, graph.Subject) and := graph.NewAndIterator() 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 graph.NewNullIterator() } argIt := buildIteratorTree(firstArg.Object(), ts) and := graph.NewAndIterator() and.AddSubIterator(subIt) and.AddSubIterator(argIt) it = and case "back": arg, _ := obj.Get("_gremlin_back_chain") argIt := buildIteratorTree(arg.Object(), ts) and := graph.NewAndIterator() and.AddSubIterator(subIt) and.AddSubIterator(argIt) it = and case "is": fixed := ts.MakeFixed() for _, name := range stringArgs { fixed.AddValue(ts.GetIdFor(name)) } and := graph.NewAndIterator() 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 graph.NewNullIterator() } argIt := buildIteratorTree(firstArg.Object(), ts) or := graph.NewOrIterator() 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 := graph.NewOrIterator() 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 graph.NewNullIterator() } it = buildIteratorTreeHelper(firstArg.Object(), ts, subIt) case "followr": // Follow a morphism arg, _ := obj.Get("_gremlin_followr") if isVertexChain(arg.Object()) { return graph.NewNullIterator() } it = buildIteratorTreeHelper(arg.Object(), ts, subIt) case "in": it = buildInOutIterator(obj, ts, subIt, true) } return it }
func buildIteratorTree(tree *peg.ExpressionTree, ts graph.TripleStore) graph.Iterator { switch tree.Name { case "Start": return buildIteratorTree(tree.Children[0], ts) case "NodeIdentifier": var out graph.Iterator nodeID := getIdentString(tree) if tree.Children[0].Name == "Variable" { allIt := ts.GetNodesAllIterator() allIt.AddTag(nodeID) out = allIt } else { n := nodeID if tree.Children[0].Children[0].Name == "ColonIdentifier" { n = nodeID[1:] } fixed := ts.MakeFixed() fixed.AddValue(ts.GetIdFor(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], ts) lto := graph.NewLinksToIterator(ts, it, "p") return lto case "RootConstraint": constraintCount := 0 and := graph.NewAndIterator() for _, c := range tree.Children { switch c.Name { case "NodeIdentifier": fallthrough case "Constraint": it := buildIteratorTree(c, ts) and.AddSubIterator(it) constraintCount++ continue default: continue } } return and case "Constraint": var hasa *graph.HasaIterator topLevelDir := "s" subItDir := "o" subAnd := graph.NewAndIterator() isOptional := false for _, c := range tree.Children { switch c.Name { case "PredIdentifier": if c.Children[0].Name == "Reverse" { topLevelDir = "o" subItDir = "s" } it := buildIteratorTree(c, ts) subAnd.AddSubIterator(it) continue case "PredicateKeyword": switch c.Children[0].Name { case "OptionalKeyword": isOptional = true } case "NodeIdentifier": fallthrough case "RootConstraint": it := buildIteratorTree(c, ts) l := graph.NewLinksToIterator(ts, it, subItDir) subAnd.AddSubIterator(l) continue default: continue } } hasa = graph.NewHasaIterator(ts, subAnd, topLevelDir) if isOptional { optional := graph.NewOptionalIterator(hasa) return optional } return hasa default: return &graph.NullIterator{} } panic("Not reached") }