func main() { // To see how most of this works, see hello_world -- this just add in a transaction store, err := cayley.NewMemoryGraph() if err != nil { log.Fatalln(err) } // Create a transaction of work to do // NOTE: the transaction is independent of the storage type, so comes from cayley rather than store t := cayley.NewTransaction() t.AddQuad(quad.Make("food", "is", "good", nil)) t.AddQuad(quad.Make("phrase of the day", "is of course", "Hello World!", nil)) t.AddQuad(quad.Make("cats", "are", "awesome", nil)) t.AddQuad(quad.Make("cats", "are", "scary", nil)) t.AddQuad(quad.Make("cats", "want to", "kill you", nil)) // Apply the transaction err = store.ApplyTransaction(t) if err != nil { log.Fatalln(err) } p := cayley.StartPath(store, quad.String("cats")).Out(quad.String("are")) err = p.Iterate(nil).EachValue(nil, func(v quad.Value) { fmt.Println("cats are", v.Native()) }) if err != nil { log.Fatalln(err) } }
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 { clog.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 { clog.Errorf("Error: Couldn't decode value: %v", err) return nil } return p.ToNative() default: panic(fmt.Errorf("unsupported type: %T", v)) } }
func main() { // File for your new BoltDB. Use path to regular file and not temporary in the real world tmpfile, err := ioutil.TempFile("", "example") if err != nil { log.Fatal(err) } defer os.Remove(tmpfile.Name()) // clean up // Initialize the database graph.InitQuadStore("bolt", tmpfile.Name(), nil) // Open and use the database store, err := cayley.NewGraph("bolt", tmpfile.Name(), nil) if err != nil { log.Fatalln(err) } store.AddQuad(quad.Make("phrase of the day", "is of course", "Hello BoltDB!", "demo graph")) // Now we create the path, to get to our data p := cayley.StartPath(store, quad.String("phrase of the day")).Out(quad.String("is of course")) // This is more advanced example of the query. // Simpler equivalent can be found in hello_world example. // Now we get an iterator for the path and optimize it. // The second return is if it was optimized, but we don't care for now. it, _ := p.BuildIterator().Optimize() // Optimize iterator on quad store level. // After this step iterators will be replaced with backend-specific ones. it, _ = store.OptimizeIterator(it) // remember to cleanup after yourself defer it.Close() // While we have items for it.Next() { token := it.Result() // get a ref to a node (backend-specific) value := store.NameOf(token) // get the value in the node (RDF) nativeValue := quad.NativeOf(value) // convert value to normal Go type fmt.Println(nativeValue) // print it! } if err := it.Err(); err != nil { log.Fatalln(err) } }
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]) }
// 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 main() { // Create a brand new graph store, err := cayley.NewMemoryGraph() if err != nil { log.Fatalln(err) } store.AddQuad(quad.Make("phrase of the day", "is of course", "Hello World!", nil)) // Now we create the path, to get to our data p := cayley.StartPath(store, quad.String("phrase of the day")).Out(quad.String("is of course")) // Now we iterate over results. Arguments: // 1. Optional context used for cancellation. // 2. Quad store, but we can omit it because we have already built path with it. err = p.Iterate(nil).EachValue(nil, func(value quad.Value) { nativeValue := quad.NativeOf(value) // this converts RDF values to normal Go types fmt.Println(nativeValue) }) if err != nil { log.Fatalln(err) } }
func TestCompareTypedValues(t testing.TB, gen DatabaseFunc, conf *Config) { qs, opts, closer := gen(t) defer closer() w := MakeWriter(t, qs, opts) t1 := tzero t2 := t1.Add(time.Hour) t3 := t2.Add(time.Hour * 48) t4 := t1.Add(time.Hour * 24 * 365) err := w.AddQuadSet([]quad.Quad{ {quad.BNode("alice"), quad.BNode("bob"), quad.BNode("charlie"), quad.BNode("dani")}, {quad.IRI("alice"), quad.IRI("bob"), quad.IRI("charlie"), quad.IRI("dani")}, {quad.String("alice"), quad.String("bob"), quad.String("charlie"), quad.String("dani")}, {quad.Int(100), quad.Int(112), quad.Int(110), quad.Int(20)}, {quad.Time(t1), quad.Time(t2), quad.Time(t3), quad.Time(t4)}, }) require.Nil(t, err) for _, c := range casesCompare { it := iterator.NewComparison(qs.NodesAllIterator(), c.op, c.val, qs) ExpectIteratedValues(t, qs, it, c.expect) } for _, c := range casesCompare { it := iterator.NewComparison(qs.NodesAllIterator(), c.op, c.val, qs) nit, ok := qs.OptimizeIterator(it) require.Equal(t, conf.OptimizesComparison, ok) if conf.OptimizesComparison { require.NotEqual(t, it, nit) } else { require.Equal(t, it, nit) } ExpectIteratedValues(t, qs, nit, c.expect) } }
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 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())))) }
// TestUpsertItem tests the insert and update of an item. func TestUpsertItem(t *testing.T) { store := setup(t) defer teardown(t, store) t.Log("Given the need to insert and then update an item.") { //---------------------------------------------------------------------- // Get the fixture. items, err := itemfix.Get() if err != nil { t.Fatalf("\t%s\tShould be able to retrieve the fixture : %v", tests.Failed, err) } t.Logf("\t%s\tShould be able to retrieve the fixture.", tests.Success) itemStrData, err := json.Marshal(&items[0]) if err != nil { t.Fatalf("\t%s\tShould be able to marshal the fixture : %v", tests.Failed, err) } t.Logf("\t%s\tShould be able to marshal the fixture.", tests.Success) //---------------------------------------------------------------------- // Insert the Item. url := "/v1/item" r := httptest.NewRequest("PUT", url, bytes.NewBuffer(itemStrData)) w := httptest.NewRecorder() a.ServeHTTP(w, r) t.Logf("\tWhen calling url to insert : %s", url) { if w.Code != http.StatusOK { t.Fatalf("\t%s\tShould be able to insert the item : %v", tests.Failed, w.Code) } t.Logf("\t%s\tShould be able to insert the item.", tests.Success) } //---------------------------------------------------------------------- // Check the inferred relationship. p := cayley.StartPath(store, quad.String("ITEST_80aa936a-f618-4234-a7be-df59a14cf8de")).Out(quad.String("authored")) it, _ := p.BuildIterator().Optimize() defer it.Close() for it.Next() { token := it.Result() value := store.NameOf(token) if quad.NativeOf(value) != "ITEST_d1dfa366-d2f7-4a4a-a64f-af89d4c97d82" { t.Fatalf("\t%s\tShould be able to get the inferred relationships from the graph", tests.Failed) } } if err := it.Err(); err != nil { t.Fatalf("\t%s\tShould be able to get the inferred relationships from the graph : %s", tests.Failed, err) } it.Close() t.Logf("\t%s\tShould be able to get the inferred relationships from the graph.", tests.Success) //---------------------------------------------------------------------- // Retrieve the item. url = "/v1/item/" + items[0].ID r = httptest.NewRequest("GET", url, nil) w = httptest.NewRecorder() a.ServeHTTP(w, r) t.Logf("\tWhen calling url to get : %s", url) { if w.Code != http.StatusOK { t.Fatalf("\t%s\tShould be able to retrieve the item : %v", tests.Failed, w.Code) } t.Logf("\t%s\tShould be able to retrieve the item.", tests.Success) var itemsBack []item.Item if err := json.Unmarshal(w.Body.Bytes(), &itemsBack); err != nil { t.Fatalf("\t%s\tShould be able to unmarshal the results : %v", tests.Failed, err) } t.Logf("\t%s\tShould be able to unmarshal the results.", tests.Success) if itemsBack[0].ID != items[0].ID || itemsBack[0].Type != items[0].Type { t.Logf("\t%+v", items[0]) t.Logf("\t%+v", itemsBack[0]) t.Fatalf("\t%s\tShould be able to get back the same item.", tests.Failed) } t.Logf("\t%s\tShould be able to get back the same item.", tests.Success) } //---------------------------------------------------------------------- // Update the Item. items[0].Version = 2 itemStrData, err = json.Marshal(items[0]) if err != nil { t.Fatalf("\t%s\tShould be able to marshal the changed fixture : %v", tests.Failed, err) } t.Logf("\t%s\tShould be able to marshal the changed fixture.", tests.Success) url = "/v1/item" r = httptest.NewRequest("PUT", url, bytes.NewBuffer(itemStrData)) w = httptest.NewRecorder() a.ServeHTTP(w, r) t.Logf("\tWhen calling url to update : %s", url) { if w.Code != http.StatusOK { t.Fatalf("\t%s\tShould be able to update the item : %v", tests.Failed, w.Code) } t.Logf("\t%s\tShould be able to update the item.", tests.Success) } //---------------------------------------------------------------------- // Retrieve the Item. url = "/v1/item/" + items[0].ID r = httptest.NewRequest("GET", url, nil) w = httptest.NewRecorder() a.ServeHTTP(w, r) t.Logf("\tWhen calling url to get : %s", url) { if w.Code != http.StatusOK { t.Fatalf("\t%s\tShould be able to retrieve the item : %v", tests.Failed, w.Code) } t.Logf("\t%s\tShould be able to retrieve the item.", tests.Success) var itUpdated []item.Item if err := json.Unmarshal(w.Body.Bytes(), &itUpdated); err != nil { t.Fatalf("\t%s\tShould be able to unmarshal the results : %v", tests.Failed, err) } t.Logf("\t%s\tShould be able to unmarshal the results.", tests.Success) if itUpdated[0].Version != 2 { t.Log(w.Body.String()) t.Fatalf("\t%s\tShould get the expected result.", tests.Failed) } t.Logf("\t%s\tShould get the expected result.", tests.Success) } } }
// TestDeleteItem tests the insert and deletion of a item. func TestDeleteItem(t *testing.T) { store := setup(t) defer teardown(t, store) t.Log("Given the need to delete an item.") { //---------------------------------------------------------------------- // Delete the Item. url := "/v1/item/ITEST_d1dfa366-d2f7-4a4a-a64f-af89d4c97d82" r := httptest.NewRequest("DELETE", url, nil) w := httptest.NewRecorder() a.ServeHTTP(w, r) t.Logf("\tWhen calling url to delete : %s", url) { if w.Code != http.StatusNoContent { t.Fatalf("\t%s\tShould be able to delete the item : %v", tests.Failed, w.Code) } t.Logf("\t%s\tShould be able to delete the item.", tests.Success) } //---------------------------------------------------------------------- // Retrieve the Item. url = "/v1/view/ITEST_d1dfa366-d2f7-4a4a-a64f-af89d4c97d82" r = httptest.NewRequest("GET", url, nil) w = httptest.NewRecorder() a.ServeHTTP(w, r) t.Logf("\tWhen calling url to get : %s", url) { if w.Code != 404 { t.Fatalf("\t%s\tShould not be able to retrieve the item : %v", tests.Failed, w.Code) } t.Logf("\t%s\tShould not be able to retrieve the item.", tests.Success) } //---------------------------------------------------------------------- // Check the inferred relationships. p := cayley.StartPath(store, quad.String("ITEST_80aa936a-f618-4234-a7be-df59a14cf8de")).Out(quad.String("authored")) it, _ := p.BuildIterator().Optimize() defer it.Close() var count int for it.Next() { count++ } if err := it.Err(); err != nil { t.Fatalf("\t%s\tShould be able to confirm removed relationships : %s", tests.Failed, err) } if count > 0 { t.Fatalf("\t%s\tShould be able to confirm removed relationships.", tests.Failed) } t.Logf("\t%s\tShould be able to confirm removed relationships.", tests.Success) } }
// Decorate decorates the given graph node with a predicate with the given string value. func (gn ModifiableGraphNode) Decorate(predicate Predicate, value string) { gn.DecorateWithValue(predicate, GraphValue{quad.String(value)}) }
func unEscape(r []rune, spec int, isQuoted, isEscaped bool) quad.Value { raw := r var sp []rune if spec > 0 { r, sp = r[:spec], r[spec:] isQuoted = true } if isQuoted { r = r[1 : len(r)-1] } else { if len(r) >= 2 && r[0] == '<' && r[len(r)-1] == '>' { return quad.IRI(r[1 : len(r)-1]) } if len(r) >= 2 && r[0] == '_' && r[1] == ':' { return quad.BNode(string(r[2:])) } } var val string if isEscaped { 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++ } val = buf.String() } else { val = string(r) } if len(sp) == 0 { if isQuoted { return quad.String(val) } return quad.Raw(val) } if sp[0] == '@' { return quad.LangString{ Value: quad.String(val), Lang: string(sp[1:]), } } else if len(sp) >= 4 && sp[0] == '^' && sp[1] == '^' && sp[2] == '<' && sp[len(sp)-1] == '>' { v := quad.TypedString{ Value: quad.String(val), Type: quad.IRI(sp[3 : len(sp)-1]), } if AutoConvertTypedString { nv, err := v.ParseValue() if err == nil { return nv } } return v } return quad.Raw(raw) }
return otto.NullValue() } return outObj(call, cmpOperator{op: op, val: qv}) } } type cmpOperator struct { op iterator.Operator val quad.Value } var defaultEnv = map[string]func(call otto.FunctionCall) otto.Value{ "iri": oneStringType(func(s string) quad.Value { return quad.IRI(s) }), "bnode": oneStringType(func(s string) quad.Value { return quad.BNode(s) }), "raw": oneStringType(func(s string) quad.Value { return quad.Raw(s) }), "str": oneStringType(func(s string) quad.Value { return quad.String(s) }), "lang": twoStringType(func(s, lang string) quad.Value { return quad.LangString{Value: quad.String(s), Lang: lang} }), "typed": twoStringType(func(s, typ string) quad.Value { return quad.TypedString{Value: quad.String(s), Type: quad.IRI(typ)} }), "lt": cmpOpType(iterator.CompareLT), "lte": cmpOpType(iterator.CompareLTE), "gt": cmpOpType(iterator.CompareGT), "gte": cmpOpType(iterator.CompareGTE), } func newWorker(qs graph.QuadStore) *worker {
// TestAddRemoveGraph tests if we can add/remove relationship quads to/from cayley. func TestAddRemoveGraph(t *testing.T) { db, store, items := setupGraph(t) defer tests.DisplayLog() t.Log("Given the need to add/remove relationship quads from the Cayley graph.") { t.Log("\tWhen starting from an empty graph") { //---------------------------------------------------------------------- // Infer and add the relationships to the graph. if err := wire.AddToGraph(tests.Context, db, store, items[0]); err != nil { t.Fatalf("\t%s\tShould be able to add relationships to the graph : %s", tests.Failed, err) } t.Logf("\t%s\tShould be able to add relationships to the graph.", tests.Success) //---------------------------------------------------------------------- // Get the relationship quads from the graph. p := cayley.StartPath(store, quad.String("WTEST_80aa936a-f618-4234-a7be-df59a14cf8de")).Out(quad.String("WTEST_flagged")) it, _ := p.BuildIterator().Optimize() defer it.Close() var count int for it.Next() { count++ token := it.Result() value := store.NameOf(token) if quad.NativeOf(value) != "WTEST_d1dfa366-d2f7-4a4a-a64f-af89d4c97d82" { t.Fatalf("\t%s\tShould be able to get the relationships from the graph", tests.Failed) } } if err := it.Err(); err != nil { t.Fatalf("\t%s\tShould be able to get the relationships from the graph : %s", tests.Failed, err) } it.Close() p = cayley.StartPath(store, quad.String("WTEST_d1dfa366-d2f7-4a4a-a64f-af89d4c97d82")).Out(quad.String("WTEST_on")) it, _ = p.BuildIterator().Optimize() defer it.Close() for it.Next() { count++ token := it.Result() value := store.NameOf(token) if quad.NativeOf(value) != "WTEST_c1b2bbfe-af9f-4903-8777-bd47c4d5b20a" { t.Fatalf("\t%s\tShould be able to get the relationships from the graph", tests.Failed) } } if err := it.Err(); err != nil { t.Fatalf("\t%s\tShould be able to get the relationships from the graph : %s", tests.Failed, err) } it.Close() if count != 2 { t.Fatalf("\t%s\tShould be able to get relationships from the graph", tests.Failed) } t.Logf("\t%s\tShould be able to get relationships from the graph.", tests.Success) //---------------------------------------------------------------------- // Try to infer and add the relationships again. if err := wire.AddToGraph(tests.Context, db, store, items[0]); err != nil { t.Fatalf("\t%s\tShould be able to add an item again and maintain relationships : %s", tests.Failed, err) } t.Logf("\t%s\tShould be able to add an item again and maintain relationships.", tests.Success) //---------------------------------------------------------------------- // Remove the relationships from the graph. if err := wire.RemoveFromGraph(tests.Context, db, store, items[0]); err != nil { t.Fatalf("\t%s\tShould be able to remove relationships from the graph : %s", tests.Failed, err) } t.Logf("\t%s\tShould be able to remove relationships from the graph.", tests.Success) //---------------------------------------------------------------------- // Try to get the relationships. count = 0 p = cayley.StartPath(store, quad.String("WTEST_80aa936a-f618-4234-a7be-df59a14cf8de")).Out(quad.String("WTEST_authored")) it, _ = p.BuildIterator().Optimize() defer it.Close() for it.Next() { count++ } if err := it.Err(); err != nil { t.Fatalf("\t%s\tShould be able to verify the empty graph : %s", tests.Failed, err) } it.Close() p = cayley.StartPath(store, quad.String("WTEST_d1dfa366-d2f7-4a4a-a64f-af89d4c97d82")).Out(quad.String("WTEST_on")) it, _ = p.BuildIterator().Optimize() defer it.Close() for it.Next() { count++ } if err := it.Err(); err != nil { t.Fatalf("\t%s\tShould be able to verify the empty graph : %s", tests.Failed, err) } it.Close() if count != 0 { t.Fatalf("\t%s\tShould be able to verify the empty graph", tests.Failed) } t.Logf("\t%s\tShould be able to verify the empty graph.", tests.Success) } } }
const lt, lte, gt, gte = iterator.CompareLT, iterator.CompareLTE, iterator.CompareGT, iterator.CompareGTE var tzero = time.Unix(time.Now().Unix(), 0) var casesCompare = []struct { op iterator.Operator val quad.Value expect []quad.Value }{ {lt, quad.BNode("b"), []quad.Value{ quad.BNode("alice"), }}, {lte, quad.BNode("bob"), []quad.Value{ quad.BNode("alice"), quad.BNode("bob"), }}, {lt, quad.String("b"), []quad.Value{ quad.String("alice"), }}, {lte, quad.String("bob"), []quad.Value{ quad.String("alice"), quad.String("bob"), }}, {gte, quad.String("b"), []quad.Value{ quad.String("bob"), quad.String("charlie"), quad.String("dani"), }}, {lt, quad.IRI("b"), []quad.Value{ quad.IRI("alice"), }}, {lte, quad.IRI("bob"), []quad.Value{ quad.IRI("alice"), quad.IRI("bob"), }}, {lte, quad.IRI("bob"), []quad.Value{
func TestLoadTypedQuads(t testing.TB, gen DatabaseFunc, conf *Config) { qs, opts, closer := gen(t) defer closer() w := MakeWriter(t, qs, opts) values := []quad.Value{ quad.BNode("A"), quad.IRI("name"), quad.String("B"), quad.IRI("graph"), quad.IRI("B"), quad.Raw("<type>"), quad.TypedString{Value: "10", Type: "int"}, quad.LangString{Value: "value", Lang: "en"}, quad.Int(-123456789), quad.Float(-12345e-6), quad.Bool(true), quad.Time(time.Now()), } err := w.AddQuadSet([]quad.Quad{ {values[0], values[1], values[2], values[3]}, {values[4], values[5], values[6], nil}, {values[4], values[5], values[7], nil}, {values[0], values[1], values[8], nil}, {values[0], values[1], values[9], nil}, {values[0], values[1], values[10], nil}, {values[0], values[1], values[11], nil}, }) require.Nil(t, err) for _, pq := range values { got := qs.NameOf(qs.ValueOf(pq)) if !conf.UnTyped { if pt, ok := pq.(quad.Time); ok { var trim int64 if conf.TimeInMcs { trim = 1000 } else if conf.TimeInMs { trim = 1000000 } if trim > 0 { tm := time.Time(pt) seconds := tm.Unix() nanos := int64(tm.Sub(time.Unix(seconds, 0))) if conf.TimeRound { nanos = (nanos/trim + ((nanos/(trim/10))%10)/5) * trim } else { nanos = (nanos / trim) * trim } pq = quad.Time(time.Unix(seconds, nanos).UTC()) } } if eq, ok := pq.(quad.Equaler); ok { assert.True(t, eq.Equal(got), "Failed to roundtrip %q (%T), got %q (%T)", pq, pq, got, got) } else { assert.Equal(t, pq, got, "Failed to roundtrip %q (%T)", pq, pq) if !conf.NoHashes { assert.Equal(t, pq, qs.NameOf(qs.ValueOf(quad.Raw(pq.String()))), "Failed to exchange raw value %q (%T)", pq, pq) } } } else { assert.Equal(t, quad.StringOf(pq), quad.StringOf(got), "Failed to roundtrip raw %q (%T)", pq, pq) } } require.Equal(t, int64(7), qs.Size(), "Unexpected quadstore size") }
func (qs *QuadStore) NameOf(v graph.Value) quad.Value { if v == nil { if clog.V(2) { clog.Infof("NameOf was nil") } return nil } hash := v.(NodeHash) if !hash.Valid() { if clog.V(2) { clog.Infof("NameOf was nil") } return nil } if val, ok := qs.ids.Get(hash.String()); ok { return val.(quad.Value) } query := `SELECT value, value_string, datatype, language, iri, bnode, value_int, value_bool, value_float, value_time FROM nodes WHERE hash = $1 LIMIT 1;` c := qs.db.QueryRow(query, hash.toSQL()) var ( data []byte str sql.NullString typ sql.NullString lang sql.NullString iri sql.NullBool bnode sql.NullBool vint sql.NullInt64 vbool sql.NullBool vfloat sql.NullFloat64 vtime pq.NullTime ) if err := c.Scan( &data, &str, &typ, &lang, &iri, &bnode, &vint, &vbool, &vfloat, &vtime, ); err != nil { clog.Errorf("Couldn't execute value lookup: %v", err) return nil } var val quad.Value if str.Valid { if iri.Bool { val = quad.IRI(str.String) } else if bnode.Bool { val = quad.BNode(str.String) } else if lang.Valid { val = quad.LangString{ Value: quad.String(unescapeNullByte(str.String)), Lang: lang.String, } } else if typ.Valid { val = quad.TypedString{ Value: quad.String(unescapeNullByte(str.String)), Type: quad.IRI(typ.String), } } else { val = quad.String(unescapeNullByte(str.String)) } } else if vint.Valid { val = quad.Int(vint.Int64) } else if vbool.Valid { val = quad.Bool(vbool.Bool) } else if vfloat.Valid { val = quad.Float(vfloat.Float64) } else if vtime.Valid { val = quad.Time(vtime.Time) } else { qv, err := proto.UnmarshalValue(data) if err != nil { clog.Errorf("Couldn't unmarshal value: %v", err) return nil } val = qv } if val != nil { qs.ids.Put(hash.String(), val) } return val }
operator: CompareGTE, expect: []quad.Value{quad.Int(2), quad.Int(3), quad.Int(4)}, qs: simpleStore, iterator: simpleFixedIterator, }, { message: "successful int64 greater than or equal comparison (mixed)", operand: quad.Int(2), operator: CompareGTE, expect: []quad.Value{quad.Int(2), quad.Int(3), quad.Int(4), quad.Int(5)}, qs: mixedStore, iterator: mixedFixedIterator, }, { message: "successful string less than comparison", operand: quad.String("echo"), operator: CompareLT, expect: []quad.Value{quad.String("bar"), quad.String("baz")}, qs: stringStore, iterator: stringFixedIterator, }, { message: "empty string less than comparison", operand: quad.String(""), operator: CompareLT, expect: nil, qs: stringStore, iterator: stringFixedIterator, }, { message: "successful string greater than comparison",
message: "parse simple triples", input: "this is valid .", expect: quad.Quad{ Subject: quad.Raw("this"), Predicate: quad.Raw("is"), Object: quad.Raw("valid"), Label: nil, }, }, { message: "parse quoted triples", input: `this is "valid too" .`, expect: quad.Quad{ Subject: quad.Raw("this"), Predicate: quad.Raw("is"), Object: quad.String("valid too"), Label: nil, }, }, { message: "parse escaped quoted triples", input: `he said "\"That's all folks\"" .`, expect: quad.Quad{ Subject: quad.Raw("he"), Predicate: quad.Raw("said"), Object: quad.String(`"That's all folks"`), Label: nil, }, }, { message: "parse an example real triple",
type test struct { message string path *Path expect []quad.Value tag string } // Define morphisms without a QuadStore const ( vFollows = quad.IRI("follows") vAre = quad.IRI("are") vStatus = quad.IRI("status") vPredicate = quad.IRI("predicates") vCool = quad.String("cool_person") vSmart = quad.String("smart_person") vSmartGraph = quad.IRI("smart_graph") vAlice = quad.IRI("alice") vBob = quad.IRI("bob") vCharlie = quad.IRI("charlie") vDani = quad.IRI("dani") vFred = quad.IRI("fred") vGreg = quad.IRI("greg") vEmily = quad.IRI("emily") ) var ( grandfollows = StartMorphism().Out(vFollows).Out(vFollows) )
// viewPathToGraphPath translates the path in a view into a "path" // utilized in graph queries. func viewPathToGraphPath(v *view.View, key string, graphDB *cayley.Handle) (*path.Path, error) { // outputPath is the final tranlated graph path. var outputPath *path.Path // Loop over the paths in the view translating the metadata. for idx, pth := range v.Paths { // We create an alias prefix for tags, so we can track which // path a tag is in. alias := strconv.Itoa(idx+1) + "_" // Sort the view Path value. sort.Sort(pth.Segments) // graphPath will contain the entire strict graph path. var graphPath *path.Path // subPaths will contain each sub path of the full graph path, // as a separate graph path. var subPaths []path.Path // Loop over the path segments translating the path. level := 1 for _, segment := range pth.Segments { // Check that the level is the level we expect (i.e., that the levels // are in order) if level != segment.Level { err := fmt.Errorf("Invalid view path level, expected %d but seeing %d", level, segment.Level) return graphPath, err } // Initialize the path, if we are on level 1. if level == 1 { // Add the first level relationship. switch segment.Direction { case inString: graphPath = cayley.StartPath(graphDB, quad.String(key)).In(quad.String(segment.Predicate)) case outString: graphPath = cayley.StartPath(graphDB, quad.String(key)).Out(quad.String(segment.Predicate)) } // Add the tag, if present. if segment.Tag != "" { graphPath = graphPath.Clone().Tag(alias + segment.Tag) } // Track this as a subpath. subPaths = append(subPaths, *graphPath.Clone()) level++ continue } // Add the relationship. switch segment.Direction { case inString: graphPath = graphPath.Clone().In(quad.String(segment.Predicate)) case outString: graphPath = graphPath.Clone().Out(quad.String(segment.Predicate)) } // Add the tag, if present. if segment.Tag != "" { graphPath = graphPath.Clone().Tag(alias + segment.Tag) } // Add this as a subpath. subPaths = append(subPaths, *graphPath.Clone()) level++ } // If we are forcing a strict path, return only the resulting or // tagged items along the full path. if pth.StrictPath { if outputPath == nil { outputPath = graphPath continue } outputPath = outputPath.Clone().Or(graphPath) continue } // Otherwise add all the subpaths to the output path. for _, subPath := range subPaths { if outputPath == nil { addedPath := &subPath outputPath = addedPath.Clone() continue } outputPath = outputPath.Clone().Or(&subPath) } } return outputPath, nil }
// TestImportRemoveItem tests the insert and update of an item. func TestImportRemoveItem(t *testing.T) { db, store := setup(t) defer teardown(t, db, store) t.Log("Given the need to import an item.") { //---------------------------------------------------------------------- // Get the fixture. items, err := itemfix.Get() if err != nil { t.Fatalf("\t%s\tShould be able to retrieve the fixture : %v", tests.Failed, err) } t.Logf("\t%s\tShould be able to retrieve the fixture.", tests.Success) //---------------------------------------------------------------------- // Import the Item. if err := sponge.Import(tests.Context, db, store, &items[0]); err != nil { t.Fatalf("\t%s\tShould be able to import an item : %s", tests.Failed, err) } t.Logf("\t%s\tShould be able to import an item", tests.Success) //---------------------------------------------------------------------- // Check the inferred relationship. p := cayley.StartPath(store, quad.String("ITEST_80aa936a-f618-4234-a7be-df59a14cf8de")).Out(quad.String("authored")) it, _ := p.BuildIterator().Optimize() defer it.Close() for it.Next() { token := it.Result() value := store.NameOf(token) if quad.NativeOf(value) != "ITEST_d1dfa366-d2f7-4a4a-a64f-af89d4c97d82" { t.Fatalf("\t%s\tShould be able to get the inferred relationships from the graph", tests.Failed) } } if err := it.Err(); err != nil { t.Fatalf("\t%s\tShould be able to get the inferred relationships from the graph : %s", tests.Failed, err) } it.Close() t.Logf("\t%s\tShould be able to get the inferred relationships from the graph.", tests.Success) //---------------------------------------------------------------------- // Import the Item again to test for duplicate imports. if err := sponge.Import(tests.Context, db, store, &items[0]); err != nil { t.Fatalf("\t%s\tShould be able to import a duplicate item : %s", tests.Failed, err) } t.Logf("\t%s\tShould be able to import a duplicate item", tests.Success) //---------------------------------------------------------------------- // Remove the item. if err := sponge.Remove(tests.Context, db, store, items[0].ID); err != nil { t.Fatalf("\t%s\tShould be able to remove the item : %s", tests.Failed, err) } t.Logf("\t%s\tShould be able to remove the item", tests.Success) //---------------------------------------------------------------------- // Check the inferred relationships. p = cayley.StartPath(store, quad.String("ITEST_80aa936a-f618-4234-a7be-df59a14cf8de")).Out(quad.String("authored")) it, _ = p.BuildIterator().Optimize() defer it.Close() var count int for it.Next() { count++ } if err := it.Err(); err != nil { t.Fatalf("\t%s\tShould be able to confirm removed relationships : %s", tests.Failed, err) } if count > 0 { t.Fatalf("\t%s\tShould be able to confirm removed relationships.", tests.Failed) } t.Logf("\t%s\tShould be able to confirm removed relationships.", tests.Success) } }