func TestSQLLinkIteration(t *testing.T) { if *postgres_path == "" { t.SkipNow() } db, err := newQuadStore(*postgres_path, nil) qs := db.(*QuadStore) if err != nil { t.Fatal(err) } it := NewSQLLinkIterator(qs, quad.Object, quad.Raw("Humphrey Bogart")) for graph.Next(it) { fmt.Println(it.Result()) } it = NewSQLLinkIterator(qs, quad.Subject, quad.Raw("/en/casablanca_1942")) s, v := it.sql.buildSQL(true, nil) t.Log(s, v) c := 0 for graph.Next(it) { fmt.Println(it.Result()) c += 1 } if c != 18 { t.Errorf("Not enough results, got %d expected 18", c) } }
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 TestIterators(t *testing.T) { qs, opts, closer := makeGAE(t) defer closer() graphtest.MakeWriter(t, qs, opts, graphtest.MakeQuadSet()...) require.Equal(t, int64(11), qs.Size(), "Incorrect number of quads") var expected = []quad.Quad{ quad.Make("C", "follows", "B", ""), quad.Make("C", "follows", "D", ""), } it := qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw("C"))) graphtest.ExpectIteratedQuads(t, qs, it, expected) // Test contains it = qs.QuadIterator(quad.Label, qs.ValueOf(quad.Raw("status_graph"))) gqs := qs.(*QuadStore) key := gqs.createKeyForQuad(quad.Make("G", "status", "cool", "status_graph")) token := &Token{quadKind, key.StringID()} require.True(t, it.Contains(token), "Contains failed") // Test cloning an iterator var it2 graph.Iterator it2 = it.Clone() x := it2.Describe() y := it.Describe() require.Equal(t, y.Name, x.Name, "Iterator Clone was not successful") }
func TestMultipleConstraintParse(t *testing.T) { qs, _ := graph.NewQuadStore("memstore", "", nil) w, _ := graph.NewQuadWriter("single", qs, nil) for _, tv := range []quad.Quad{ quad.Make("i", "like", "food", ""), quad.Make("i", "like", "beer", ""), quad.Make("you", "like", "beer", ""), } { w.WriteQuad(tv) } query := `( $a (:like :beer) (:like "food") )` it := BuildIteratorTreeForQuery(qs, query) if it.Type() != graph.And { t.Errorf("Odd iterator tree. Got: %#v", it.Describe()) } if !graph.Next(it) { t.Error("Got no results") } out := it.Result() if out != qs.ValueOf(quad.Raw("i")) { t.Errorf("Got %d, expected %d", out, qs.ValueOf(quad.Raw("i"))) } if graph.Next(it) { t.Error("Too many results") } }
// ToNative converts protobuf Quad to quad.Quad. func (m *Quad) ToNative() (q quad.Quad) { if m == nil { return } if m.SubjectValue != nil { q.Subject = m.SubjectValue.ToNative() } else if m.Subject != "" { q.Subject = quad.Raw(m.Subject) } if m.PredicateValue != nil { q.Predicate = m.PredicateValue.ToNative() } else if m.Predicate != "" { q.Predicate = quad.Raw(m.Predicate) } if m.ObjectValue != nil { q.Object = m.ObjectValue.ToNative() } else if m.Object != "" { q.Object = quad.Raw(m.Object) } if m.LabelValue != nil { q.Label = m.LabelValue.ToNative() } else if m.Label != "" { q.Label = quad.Raw(m.Label) } return }
func TestInterestingQuery(t *testing.T) { if *postgres_path == "" { t.SkipNow() } db, err := newQuadStore(*postgres_path, nil) if err != nil { t.Fatal(err) } qs := db.(*QuadStore) a := NewSQLLinkIterator(qs, quad.Object, quad.Raw("Humphrey Bogart")) b := NewSQLLinkIterator(qs, quad.Predicate, quad.Raw("name")) it1, err := intersect(a.sql, b.sql, qs) if err != nil { t.Error(err) } it2, err := hasa(it1.sql, quad.Subject, qs) if err != nil { t.Error(err) } it2.Tagger().Add("hb") it3, err := linksto(it2.sql, quad.Object, qs) if err != nil { t.Error(err) } b = NewSQLLinkIterator(db.(*QuadStore), quad.Predicate, quad.Raw("/film/performance/actor")) it4, err := intersect(it3.sql, b.sql, qs) if err != nil { t.Error(err) } it5, err := hasa(it4.sql, quad.Subject, qs) if err != nil { t.Error(err) } it6, err := linksto(it5.sql, quad.Object, qs) if err != nil { t.Error(err) } b = NewSQLLinkIterator(db.(*QuadStore), quad.Predicate, quad.Raw("/film/film/starring")) it7, err := intersect(it6.sql, b.sql, qs) if err != nil { t.Error(err) } it8, err := hasa(it7.sql, quad.Subject, qs) if err != nil { t.Error(err) } s, v := it8.sql.buildSQL(true, nil) it8.Tagger().Add("id") t.Log(s, v) for graph.Next(it8) { t.Log(it8.Result()) out := make(map[string]graph.Value) it8.TagResults(out) for k, v := range out { t.Log("%s: %v\n", k, v) } } }
func TestBuildIntersect(t *testing.T) { a := NewSQLLinkIterator(nil, quad.Subject, quad.Raw("Foo")) b := NewSQLLinkIterator(nil, quad.Predicate, quad.Raw("is_equivalent_to")) it, err := intersect(a.sql, b.sql, nil) if err != nil { t.Error(err) } s, v := it.sql.buildSQL(true, nil) t.Log(s, v) }
func unEscape(r []rune, isEscaped bool) quad.Value { if !isEscaped { return quad.Raw(string(r)) } buf := bytes.NewBuffer(make([]byte, 0, len(r))) for i := 0; i < len(r); { switch r[i] { case '\\': i++ var c byte switch r[i] { case 't': c = '\t' case 'b': c = '\b' case 'n': c = '\n' case 'r': c = '\r' case 'f': c = '\f' case '"': c = '"' case '\'': c = '\'' case '\\': c = '\\' case 'u': rc, err := strconv.ParseInt(string(r[i+1:i+5]), 16, 32) if err != nil { panic(fmt.Errorf("internal parser error: %v", err)) } buf.WriteRune(rune(rc)) i += 5 continue case 'U': rc, err := strconv.ParseInt(string(r[i+1:i+9]), 16, 32) if err != nil { panic(fmt.Errorf("internal parser error: %v", err)) } buf.WriteRune(rune(rc)) i += 9 continue } buf.WriteByte(c) default: buf.WriteRune(r[i]) } i++ } return quad.Raw(buf.String()) }
func hasaWithTag(qs graph.QuadStore, tag string, target string) *HasA { and := NewAnd(qs) obj := qs.FixedIterator() obj.Add(qs.ValueOf(quad.Raw(target))) obj.Tagger().Add(tag) and.AddSubIterator(NewLinksTo(qs, obj, quad.Object)) pred := qs.FixedIterator() pred.Add(qs.ValueOf(quad.Raw("status"))) and.AddSubIterator(NewLinksTo(qs, pred, quad.Predicate)) return NewHasA(qs, and, quad.Subject) }
func TestBuildHasa(t *testing.T) { a := NewSQLLinkIterator(nil, quad.Subject, quad.Raw("Foo")) a.Tagger().Add("foo") b := NewSQLLinkIterator(nil, quad.Predicate, quad.Raw("is_equivalent_to")) it1, err := intersect(a.sql, b.sql, nil) if err != nil { t.Error(err) } it2, err := hasa(it1.sql, quad.Object, nil) if err != nil { t.Error(err) } s, v := it2.sql.buildSQL(true, nil) t.Log(s, v) }
func TestIteratorsAndNextResultOrderA(t *testing.T) { qs, _, _ := makeTestStore(simpleGraph) 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) if !outerAnd.Next() { t.Error("Expected one matching subtree") } val := outerAnd.Result() if qs.NameOf(val) != quad.Raw("C") { t.Errorf("Matching subtree should be %s, got %s", "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) 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 (r *Reader) ReadQuad() (quad.Quad, error) { if r.err != nil { return quad.Quad{}, r.err } next: if len(r.graphs) == 0 { return quad.Quad{}, io.EOF } if r.name == "" { for gname, _ := range r.graphs { r.name = gname break } } if r.n >= len(r.graphs[r.name]) { r.n = 0 delete(r.graphs, r.name) r.name = "" goto next } cur := r.graphs[r.name][r.n] r.n++ var graph quad.Value if r.name != "" && r.name != "@default" { graph = quad.Raw(r.name) } return quad.Quad{ Subject: toQuadValue(cur.Subject), Predicate: toQuadValue(cur.Predicate), Object: toQuadValue(cur.Object), Label: graph, }, nil }
func toQuadValue(o interface{}) (quad.Value, bool) { var qv quad.Value switch v := o.(type) { case quadValue: qv = v.v case quad.Value: qv = v case string: qv = quad.Raw(v) case bool: qv = quad.Bool(v) case int: qv = quad.Int(v) case int64: qv = quad.Int(v) case float64: if float64(int(v)) == v { qv = quad.Int(int64(v)) } else { qv = quad.Float(v) } case time.Time: qv = quad.Time(v) default: return nil, false } return qv, true }
func TestLinksToOptimization(t *testing.T) { qs, _, _ := makeTestStore(simpleGraph) fixed := qs.FixedIterator() fixed.Add(qs.ValueOf(quad.Raw("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 TestSQLNodeIteration(t *testing.T) { if *postgres_path == "" { t.SkipNow() } db, err := newQuadStore(*postgres_path, nil) if err != nil { t.Fatal(err) } link := NewSQLLinkIterator(db.(*QuadStore), quad.Object, quad.Raw("/en/humphrey_bogart")) it := &SQLIterator{ uid: iterator.NextUID(), qs: db.(*QuadStore), sql: &SQLNodeIterator{ tableName: newTableName(), linkIt: sqlItDir{ it: link.sql, dir: quad.Subject, }, }, } s, v := it.sql.buildSQL(true, nil) t.Log(s, v) c := 0 for graph.Next(it) { t.Log(it.Result()) c += 1 } if c != 56 { t.Errorf("Not enough results, got %d expected 56", c) } }
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 (m *Quad) Upgrade() { if m.SubjectValue == nil { m.SubjectValue = MakeValue(quad.Raw(m.Subject)) m.Subject = "" } if m.PredicateValue == nil { m.PredicateValue = MakeValue(quad.Raw(m.Predicate)) m.Predicate = "" } if m.ObjectValue == nil { m.ObjectValue = MakeValue(quad.Raw(m.Object)) m.Object = "" } if m.LabelValue == nil && m.Label != "" { m.LabelValue = MakeValue(quad.Raw(m.Label)) m.Label = "" } }
func (qs *store) valueAt(i int) quad.Value { if !qs.parse { return quad.Raw(qs.data[i]) } iv, err := strconv.Atoi(qs.data[i]) if err == nil { return quad.Int(iv) } return quad.String(qs.data[i]) }
// GetNativeValue returns the value stored in Node. func (m *NodeData) GetNativeValue() quad.Value { if m == nil { return nil } else if m.Value == nil { if m.Name == "" { return nil } return quad.Raw(m.Name) } return m.Value.ToNative() }
func TestTreeConstraintParse(t *testing.T) { qs, _ := graph.NewQuadStore("memstore", "", nil) w, _ := graph.NewQuadWriter("single", qs, nil) w.WriteQuad(quad.Make("i", "like", "food", "")) w.WriteQuad(quad.Make("food", "is", "good", "")) query := "(\"i\"\n" + "(:like\n" + "($a (:is :good))))" it := BuildIteratorTreeForQuery(qs, query) if it.Type() != graph.And { t.Errorf("Odd iterator tree. Got: %#v", it.Describe()) } if !graph.Next(it) { t.Error("Got no results") } out := it.Result() if out != qs.ValueOf(quad.Raw("i")) { t.Errorf("Got %d, expected %d", out, qs.ValueOf(quad.Raw("i"))) } }
func toQuadValue(v value) quad.Value { if v == nil { return nil } switch d := v.(type) { case string: return quad.Raw(d) // compatibility case int64: return quad.Int(d) case float64: return quad.Float(d) case bool: return quad.Bool(d) case time.Time: return quad.Time(d) case bson.M: // TODO(dennwc): use raw document instead? so, ok := d["val"] if !ok { glog.Errorf("Error: Empty value in map: %v", v) return nil } s := so.(string) if len(d) == 1 { return quad.String(s) } if o, ok := d["iri"]; ok && o.(bool) { return quad.IRI(s) } else if o, ok := d["bnode"]; ok && o.(bool) { return quad.BNode(s) } else if o, ok := d["lang"]; ok && o.(string) != "" { return quad.LangString{ Value: quad.String(s), Lang: o.(string), } } else if o, ok := d["type"]; ok && o.(string) != "" { return quad.TypedString{ Value: quad.String(s), Type: quad.IRI(o.(string)), } } return quad.String(s) case []byte: var p proto.Value if err := p.Unmarshal(d); err != nil { glog.Errorf("Error: Couldn't decode value: %v", err) return nil } return p.ToNative() default: panic(fmt.Errorf("unsupported type: %T", v)) } }
func TestHorizonInt(t testing.TB, gen DatabaseFunc, conf *Config) { qs, opts, closer := gen(t) defer closer() w := MakeWriter(t, qs, opts) horizon := qs.Horizon() require.Equal(t, int64(0), horizon.Int(), "Unexpected horizon value") err := w.AddQuadSet(MakeQuadSet()) require.Nil(t, err) require.Equal(t, int64(11), qs.Size(), "Unexpected quadstore size") if qss, ok := qs.(ValueSizer); ok { s := qss.SizeOf(qs.ValueOf(quad.Raw("B"))) require.Equal(t, int64(5), s, "Unexpected quadstore value size") } horizon = qs.Horizon() require.Equal(t, int64(11), horizon.Int(), "Unexpected horizon value") err = w.RemoveQuad(quad.Make( "A", "follows", "B", "", )) require.Nil(t, err) if !conf.SkipSizeCheckAfterDelete { require.Equal(t, int64(10), qs.Size(), "Unexpected quadstore size after RemoveQuad") } else { require.Equal(t, int64(11), qs.Size(), "Unexpected quadstore size") } if qss, ok := qs.(ValueSizer); ok { s := qss.SizeOf(qs.ValueOf(quad.Raw("B"))) require.Equal(t, int64(4), s, "Unexpected quadstore value size") } }
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] switch p := v.(type) { case nil: return PathFromIterator(qs, qs.NodesAllIterator()) case *Path: if p.qs != qs { newp := &Path{ qs: qs, baseContext: p.baseContext, stack: p.stack[:], } return newp } return p case quad.Value: return StartPath(qs, p) case string: return StartPath(qs, quad.Raw(p)) default: panic(fmt.Sprintln("Invalid type passed to buildViaPath.", reflect.TypeOf(v), p)) } } var nodes []quad.Value for _, s := range via { switch v := s.(type) { case quad.Value: nodes = append(nodes, v) case string: nodes = append(nodes, quad.Raw(v)) default: panic("Non-value type passed to long Via path") } } return StartPath(qs, nodes...) }
func (qs *store) NameOf(v graph.Value) quad.Value { switch v.(type) { case Int64Node: i := int(v.(Int64Node)) if i < 0 || i >= len(qs.data) { return nil } return qs.valueAt(i) case stringNode: if qs.parse { return quad.String(v.(stringNode)) } return quad.Raw(v.(stringNode)) default: return nil } }
func TestMemstore(t *testing.T) { qs, _, index := makeTestStore(simpleGraph) if size := qs.Size(); size != int64(len(simpleGraph)) { t.Errorf("Quad store has unexpected size, got:%d expected %d", size, len(simpleGraph)) } for _, test := range index { v := qs.ValueOf(quad.Raw(test.query)) switch v := v.(type) { default: t.Errorf("ValueOf(%q) returned unexpected type, got:%T expected int64", test.query, v) case iterator.Int64Node: if int64(v) != test.value { t.Errorf("ValueOf(%q) returned unexpected value, got:%d expected:%d", test.query, v, test.value) } } } }
func TestLoadOneQuad(t testing.TB, gen DatabaseFunc) { qs, opts, closer := gen(t) defer closer() w := MakeWriter(t, qs, opts) err := w.AddQuad(quad.Make( "Something", "points_to", "Something Else", "context", )) require.Nil(t, err) for _, pq := range []string{"Something", "points_to", "Something Else", "context"} { got := quad.StringOf(qs.NameOf(qs.ValueOf(quad.Raw(pq)))) require.Equal(t, pq, got, "Failed to roundtrip %q", pq) } require.Equal(t, int64(1), qs.Size(), "Unexpected quadstore size") }
func TestZeroRune(t *testing.T) { qs, opts, closer := makePostgres(t) defer closer() w := graphtest.MakeWriter(t, qs, opts) obj := quad.String("AB\u0000CD") if !utf8.ValidString(string(obj)) { t.Fatal("invalid utf8") } err := w.AddQuad(quad.Quad{ Subject: quad.IRI("bob"), Predicate: quad.IRI("pred"), Object: obj, }) require.Nil(t, err) require.Equal(t, obj, qs.NameOf(qs.ValueOf(quad.Raw(obj.String())))) }
func TestDeletedFromIterator(t testing.TB, gen DatabaseFunc) { qs, opts, closer := gen(t) defer closer() w := MakeWriter(t, qs, opts, MakeQuadSet()...) // Subject iterator. it := qs.QuadIterator(quad.Subject, qs.ValueOf(quad.Raw("E"))) ExpectIteratedQuads(t, qs, it, []quad.Quad{ quad.Make("E", "follows", "F", ""), }) it.Reset() w.RemoveQuad(quad.Make("E", "follows", "F", "")) ExpectIteratedQuads(t, qs, it, nil) }
// ToNative converts protobuf Value to quad.Value. func (m *Value) ToNative() (qv quad.Value) { if m == nil { return nil } switch v := m.Value.(type) { case *Value_Raw: return quad.Raw(v.Raw) case *Value_Str: return quad.String(v.Str) case *Value_Iri: return quad.IRI(v.Iri) case *Value_Bnode: return quad.BNode(v.Bnode) case *Value_TypedStr: return quad.TypedString{ Value: quad.String(v.TypedStr.Value), Type: quad.IRI(v.TypedStr.Type), } case *Value_LangStr: return quad.LangString{ Value: quad.String(v.LangStr.Value), Lang: v.LangStr.Lang, } case *Value_Int: return quad.Int(v.Int) case *Value_Float_: return quad.Float(v.Float_) case *Value_Boolean: return quad.Bool(v.Boolean) case *Value_Time: var t time.Time if v.Time == nil { t = time.Unix(0, 0).UTC() } else { t = time.Unix(v.Time.Seconds, int64(v.Time.Nanos)).UTC() } return quad.Time(t) default: panic(fmt.Errorf("unsupported type: %T", m.Value)) } }
func TestLinksTo(t *testing.T) { qs := &store{ data: []string{1: "cool"}, iter: NewFixed(Identity), } qs.iter.(*Fixed).Add(Int64Quad(2)) fixed := NewFixed(Identity) val := qs.ValueOf(quad.Raw("cool")) if val.(Int64Node) != 1 { t.Fatalf("Failed to return correct value, got:%v expect:1", val) } fixed.Add(val) lto := NewLinksTo(qs, fixed, quad.Object) if !lto.Next() { t.Error("At least one quad matches the fixed object") } val = lto.Result() if val.(Int64Quad) != 2 { t.Errorf("Quad index 2, such as %s, should match %s", qs.Quad(Int64Quad(2)), qs.Quad(val)) } }