func movieHandler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") query := req.URL.Path[len("/movie/"):] cypher := ` MATCH (movie:Movie {title:{title}}) OPTIONAL MATCH (movie)<-[r]-(person:Person) WITH movie.title as title, collect({name:person.name, job:head(split(lower(type(r)),'_')), role:r.roles}) as cast LIMIT 1 UNWIND cast as c RETURN title, c.name as name, c.job as job, c.role as role` db, err := driver.NewDriver().OpenNeo(neo4jURL) if err != nil { log.Println("error connecting to neo4j:", err) w.WriteHeader(500) w.Write([]byte("An error occurred connecting to the DB")) return } defer db.Close() data, _, _, err := db.QueryNeoAll(cypher, map[string]interface{}{"title": query}) if err != nil { log.Println("error querying movie:", err) w.WriteHeader(500) w.Write([]byte("An error occurred querying the DB")) return } else if len(data) == 0 { w.WriteHeader(404) return } movie := Movie{ Title: data[0][0].(string), Cast: make([]Person, len(data)), } for idx, row := range data { movie.Cast[idx] = Person{ Name: row[1].(string), Job: row[2].(string), } if row[3] != nil { movie.Cast[idx].Role = interfaceSliceToString(row[3].([]interface{})) } } err = json.NewEncoder(w).Encode(movie) if err != nil { log.Println("error writing movie response:", err) w.WriteHeader(500) w.Write([]byte("An error occurred writing response")) } }
func searchHandler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") query := req.URL.Query()["q"][0] cypher := ` MATCH (movie:Movie) WHERE movie.title =~ {query} RETURN movie.title as title, movie.tagline as tagline, movie.released as released` db, err := driver.NewDriver().OpenNeo(neo4jURL) if err != nil { log.Println("error connecting to neo4j:", err) w.WriteHeader(500) w.Write([]byte("An error occurred connecting to the DB")) return } defer db.Close() param := "(?i).*" + query + ".*" data, _, _, err := db.QueryNeoAll(cypher, map[string]interface{}{"query": param}) if err != nil { log.Println("error querying search:", err) w.WriteHeader(500) w.Write([]byte("An error occurred querying the DB")) return } else if len(data) == 0 { w.WriteHeader(404) return } results := make([]MovieResult, len(data)) for idx, row := range data { results[idx] = MovieResult{ Movie{ Title: row[0].(string), Tagline: row[1].(string), Released: int(row[2].(int64)), }, } } err = json.NewEncoder(w).Encode(results) if err != nil { log.Println("error writing search response:", err) w.WriteHeader(500) w.Write([]byte("An error occurred writing response")) } }
func main() { driver := bolt.NewDriver() conn, _ := driver.OpenNeo("bolt://localhost:7687") defer conn.Close() // Start by creating a node result, _ := conn.ExecNeo("CREATE (n:NODE {foo: {foo}, bar: {bar}})", map[string]interface{}{"foo": 1, "bar": 2.2}) numResult, _ := result.RowsAffected() fmt.Printf("CREATED ROWS: %d\n", numResult) // CREATED ROWS: 1 // Lets get the node data, rowsMetadata, _, _ := conn.QueryNeoAll("MATCH (n:NODE) RETURN n.foo, n.bar", nil) fmt.Printf("COLUMNS: %#v\n", rowsMetadata["fields"].([]interface{})) // COLUMNS: n.foo,n.bar fmt.Printf("FIELDS: %d %f\n", data[0][0].(int64), data[0][1].(float64)) // FIELDS: 1 2.2 // oh cool, that worked. lets blast this baby and tell it to run a bunch of statements // in neo concurrently with a pipeline results, _ := conn.ExecPipeline([]string{ "MATCH (n:NODE) CREATE (n)-[:REL]->(f:FOO)", "MATCH (n:NODE) CREATE (n)-[:REL]->(b:BAR)", "MATCH (n:NODE) CREATE (n)-[:REL]->(z:BAZ)", "MATCH (n:NODE) CREATE (n)-[:REL]->(f:FOO)", "MATCH (n:NODE) CREATE (n)-[:REL]->(b:BAR)", "MATCH (n:NODE) CREATE (n)-[:REL]->(z:BAZ)", }, nil, nil, nil, nil, nil, nil) for _, result := range results { numResult, _ := result.RowsAffected() fmt.Printf("CREATED ROWS: %d\n", numResult) // CREATED ROWS: 2 (per each iteration) } data, _, _, _ = conn.QueryNeoAll("MATCH (n:NODE)-[:REL]->(m) RETURN m", nil) for _, row := range data { fmt.Printf("NODE: %#v\n", row[0].(graph.Node)) // Prints all nodes } result, _ = conn.ExecNeo(`MATCH (n) DETACH DELETE n`, nil) numResult, _ = result.RowsAffected() fmt.Printf("Rows Deleted: %d", numResult) // Rows Deleted: 13 }
func main() { driver := bolt.NewDriver() conn, err := driver.OpenNeo("bolt://localhost:7687") if err != nil { panic(err) } defer conn.Close() // Here we prepare a new statement. This gives us the flexibility to // cancel that statement without any request sent to Neo stmt, err := conn.PrepareNeo("CREATE (n:NODE {foo: {foo}, bar: {bar}})") if err != nil { panic(err) } // Executing a statement just returns summary information result, err := stmt.ExecNeo(map[string]interface{}{"foo": 1, "bar": 2.2}) if err != nil { panic(err) } numResult, err := result.RowsAffected() if err != nil { panic(err) } fmt.Printf("CREATED ROWS: %d\n", numResult) // CREATED ROWS: 1 // Closing the statment will also close the rows stmt.Close() // Lets get the node. Once again I can cancel this with no penalty stmt, err = conn.PrepareNeo("MATCH (n:NODE) RETURN n.foo, n.bar") if err != nil { panic(err) } // Even once I get the rows, if I do not consume them and close the // rows, Neo will discard and not send the data rows, err := stmt.QueryNeo(nil) if err != nil { panic(err) } // This interface allows you to consume rows one-by-one, as they // come off the bolt stream. This is more efficient especially // if you're only looking for a particular row/set of rows, as // you don't need to load up the entire dataset into memory data, _, err := rows.NextNeo() if err != nil { panic(err) } // This query only returns 1 row, so once it's done, it will return // the metadata associated with the query completion, along with // io.EOF as the error _, _, err = rows.NextNeo() if err != io.EOF { panic(err) } fmt.Printf("COLUMNS: %#v\n", rows.Metadata()["fields"].([]interface{})) // COLUMNS: n.foo,n.bar fmt.Printf("FIELDS: %d %f\n", data[0].(int64), data[1].(float64)) // FIELDS: 1 2.2 stmt.Close() // Here we prepare a new pipeline statement for running multiple // queries concurrently pipeline, err := conn.PreparePipeline( "MATCH (n:NODE) CREATE (n)-[:REL]->(f:FOO)", "MATCH (n:NODE) CREATE (n)-[:REL]->(b:BAR)", "MATCH (n:NODE) CREATE (n)-[:REL]->(z:BAZ)", "MATCH (n:NODE) CREATE (n)-[:REL]->(f:FOO)", "MATCH (n:NODE) CREATE (n)-[:REL]->(b:BAR)", "MATCH (n:NODE) CREATE (n)-[:REL]->(z:BAZ)", ) if err != nil { panic(err) } pipelineResults, err := pipeline.ExecPipeline(nil, nil, nil, nil, nil, nil) if err != nil { panic(err) } for _, result := range pipelineResults { numResult, _ := result.RowsAffected() fmt.Printf("CREATED ROWS: %d\n", numResult) // CREATED ROWS: 2 (per each iteration) } err = pipeline.Close() if err != nil { panic(err) } stmt, err = conn.PrepareNeo("MATCH path=(n:NODE)-[:REL]->(m) RETURN path") if err != nil { panic(err) } rows, err = stmt.QueryNeo(nil) if err != nil { panic(err) } // Here we loop through the rows until we get the metadata object // back, meaning the row stream has been fully consumed for err == nil { var row []interface{} row, _, err = rows.NextNeo() if err != nil && err != io.EOF { panic(err) } else if err != io.EOF { fmt.Printf("PATH: %#v\n", row[0].(graph.Path)) // Prints all paths } } stmt.Close() result, _ = conn.ExecNeo(`MATCH (n) DETACH DELETE n`, nil) fmt.Println(result) numResult, _ = result.RowsAffected() fmt.Printf("Rows Deleted: %d", numResult) // Rows Deleted: 13 }
func graphHandler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") limits := req.URL.Query()["limit"] limit := 50 var err error if len(limits) > 0 { limit, err = strconv.Atoi(limits[0]) if err != nil { w.WriteHeader(400) w.Write([]byte("Limit must be an integer")) } } cypher := ` MATCH (m:Movie)<-[:ACTED_IN]-(a:Person) RETURN m.title as movie, collect(a.name) as cast LIMIT {limit}` db, err := driver.NewDriver().OpenNeo(neo4jURL) if err != nil { log.Println("error connecting to neo4j:", err) w.WriteHeader(500) w.Write([]byte("An error occurred connecting to the DB")) return } defer db.Close() stmt, err := db.PrepareNeo(cypher) if err != nil { log.Println("error preparing graph:", err) w.WriteHeader(500) w.Write([]byte("An error occurred querying the DB")) return } defer stmt.Close() rows, err := stmt.QueryNeo(map[string]interface{}{"limit": limit}) if err != nil { log.Println("error querying graph:", err) w.WriteHeader(500) w.Write([]byte("An error occurred querying the DB")) return } d3Resp := D3Response{} row, _, err := rows.NextNeo() for row != nil && err == nil { title := row[0].(string) actors := interfaceSliceToString(row[1].([]interface{})) d3Resp.Nodes = append(d3Resp.Nodes, Node{Title: title, Label: "movie"}) movIdx := len(d3Resp.Nodes) - 1 for _, actor := range actors { idx := -1 for i, node := range d3Resp.Nodes { if actor == node.Title && node.Label == "actor" { idx = i break } } if idx == -1 { d3Resp.Nodes = append(d3Resp.Nodes, Node{Title: actor, Label: "actor"}) d3Resp.Links = append(d3Resp.Links, Link{Source: len(d3Resp.Nodes) - 1, Target: movIdx}) } else { d3Resp.Links = append(d3Resp.Links, Link{Source: idx, Target: movIdx}) } } row, _, err = rows.NextNeo() } if err != nil && err != io.EOF { log.Println("error querying graph:", err) w.WriteHeader(500) w.Write([]byte("An error occurred querying the DB")) return } else if len(d3Resp.Nodes) == 0 { w.WriteHeader(404) return } err = json.NewEncoder(w).Encode(d3Resp) if err != nil { log.Println("error writing graph response:", err) w.WriteHeader(500) w.Write([]byte("An error occurred writing response")) } }