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) } }
// StartQueryFromNods returns a new query starting at the node with the given IDs. func (gl *GraphLayer) StartQueryFromNode(nodeId GraphNodeId) GraphQuery { singleStartingValue := nodeIdToValue(nodeId) return GraphQuery{ path: cayley.StartPath(gl.cayleyStore, singleStartingValue), layer: gl, tagCount: 0, singleStartingValue: singleStartingValue, singlePredicate: nil, singleDirection: 0, } }
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 storiesByKeys(g *cayley.Handle, keys []string) []*Story { stories := make([]*Story, 0, len(keys)) it, _ := cayley.StartPath(g, keys...).Save(StoryID, StoryID).Save(StoryTitle, StoryTitle).Save(StoryDescription, StoryDescription).Save(SiteID, SiteID).BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { s := Story{} results := map[string]graph.Value{} it.TagResults(results) s.Id = atoi(g.NameOf(results[StoryID])) s.Title = g.NameOf(results[StoryTitle]) s.Desc = g.NameOf(results[StoryDescription]) s.Site = Site(Site_value[g.NameOf(results[SiteID])]) s.annotate() stories = append(stories, &s) } return stories }
// StartQueryFromNodes returns a new query starting at the nodes with the given IDs. func (gl *GraphLayer) StartQueryFromNodes(nodeIds ...GraphNodeId) GraphQuery { quadValues := graphIdsToQuadValues(nodeIds) var singleStartingValue quad.Value = nil if len(quadValues) == 1 { singleStartingValue = quadValues[0] } return GraphQuery{ path: cayley.StartPath(gl.cayleyStore, quadValues...), layer: gl, tagCount: 0, singleStartingValue: singleStartingValue, singlePredicate: nil, singleDirection: 0, } }
// StartQuery returns a new query starting at the nodes with the given values (either graph node IDs // or arbitrary values). func (gl *GraphLayer) StartQuery(values ...interface{}) GraphQuery { quadValues := toQuadValues(values, gl) var singleStartingValue quad.Value = nil if len(values) == 1 { singleStartingValue = quadValues[0] } return GraphQuery{ path: cayley.StartPath(gl.cayleyStore, quadValues...), layer: gl, tagCount: 0, singleStartingValue: singleStartingValue, singlePredicate: nil, singleDirection: 0, } }
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) } }
// 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) } }
func (s Story) checkExistsTitle(g *cayley.Handle) bool { it, _ := cayley.StartPath(g, s.key()).Out(StoryTitle).BuildIterator().Optimize() defer it.Close() return cayley.RawNext(it) }
func (u User) checkExists(g *cayley.Handle) bool { it, _ := cayley.StartPath(g, u.key()).Out(UserID).BuildIterator().Optimize() defer it.Close() return cayley.RawNext(it) }
// 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 }
// 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) } } }
// 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) } }