From db09a2125211a4c47814cd35067bd1085942d990 Mon Sep 17 00:00:00 2001 From: Gaurav Gogia Date: Sun, 11 Mar 2018 17:51:08 +0530 Subject: [PATCH 1/6] an attempt at update of slow 'n' clean part --- README.md | 180 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 0bdf10e..aa37a10 100644 --- a/README.md +++ b/README.md @@ -71,72 +71,56 @@ func quickNDirty() { #### Slow n' Clean ```go -func slowNClean() { - 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) - } +// Constants to be used throughout the example +const ( + URI = "bolt://localhost:7687" + CreateNode = "CREATE (n:NODE {foo: {foo}, bar: {bar}})" + GetNode = "MATCH (n:NODE) RETURN n.foo, n.bar" + RelationNode = "MATCH path=(n:NODE)-[:REL]->(m) RETURN path" + DeleteNodes = "MATCH (n) DETACH DELETE n" +) - // 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 +func main() { + con := createConnection() + defer con.Close() - // Closing the statment will also close the rows - stmt.Close() + st := prepareSatement(CreateNode, con) + executeStatement(st) - // 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) - } + st = prepareSatement(GetNode, con) + rows := queryStatement(st) + consumeRows(rows, st) - // 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) - } + pipe := preparePipeline(con) + executePipeline(pipe) - // 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) - } + st = prepareSatement(RelationNode, con) + rows = queryStatement(st) + consumeMetadata(rows, st) - // 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 + cleanUp(DeleteNodes, con) +} + +func createConnection() bolt.Conn { + driver := bolt.NewDriver() + con, err := driver.OpenNeo(URI) + handleError(err) + return con +} - stmt.Close() +// Here we prepare a new statement. This gives us the flexibility to +// cancel that statement without any request sent to Neo +func prepareSatement(query string, con bolt.Conn) bolt.Stmt { + st, err := con.PrepareNeo(query) + handleError(err) + return st +} - // Here we prepare a new pipeline statement for running multiple - // queries concurrently - pipeline, err := conn.PreparePipeline( +// Here we prepare a new pipeline statement for running multiple +// queries concurrently +func preparePipeline(con bolt.Conn) bolt.PipelineStmt { + pipeline, err := con.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)", @@ -144,14 +128,13 @@ func slowNClean() { "MATCH (n:NODE) CREATE (n)-[:REL]->(b:BAR)", "MATCH (n:NODE) CREATE (n)-[:REL]->(z:BAZ)", ) - if err != nil { - panic(err) - } + handleError(err) + return pipeline +} +func executePipeline(pipeline bolt.PipelineStmt) { pipelineResults, err := pipeline.ExecPipeline(nil, nil, nil, nil, nil, nil) - if err != nil { - panic(err) - } + handleError(err) for _, result := range pipelineResults { numResult, _ := result.RowsAffected() @@ -159,22 +142,24 @@ func slowNClean() { } 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) - } + handleError(err) +} - rows, err = stmt.QueryNeo(nil) - if err != nil { - panic(err) - } +func queryStatement(st bolt.Stmt) bolt.Rows { + // 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 := st.QueryNeo(nil) + handleError(err) + return rows +} +func consumeMetadata(rows bolt.Rows, st bolt.Stmt) { // Here we loop through the rows until we get the metadata object // back, meaning the row stream has been fully consumed + + var err error + err = nil + for err == nil { var row []interface{} row, _, err = rows.NextNeo() @@ -184,14 +169,53 @@ func slowNClean() { fmt.Printf("PATH: %#v\n", row[0].(graph.Path)) // Prints all paths } } + st.Close() +} - stmt.Close() +func consumeRows(rows bolt.Rows, st bolt.Stmt) { + // 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() + handleError(err) - result, _ = conn.ExecNeo(`MATCH (n) DETACH DELETE n`, nil) + // 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() + handleError(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 + + st.Close() +} + +// Executing a statement just returns summary information +func executeStatement(st bolt.Stmt) { + result, err := st.ExecNeo(map[string]interface{}{"foo": 1, "bar": 2.2}) + handleError(err) + numResult, err := result.RowsAffected() + handleError(err) + fmt.Printf("CREATED ROWS: %d\n", numResult) // CREATED ROWS: 1 + + // Closing the statment will also close the rows + st.Close() +} + +func cleanUp(query string, con bolt.Conn) { + result, _ := con.ExecNeo(query, nil) fmt.Println(result) - numResult, _ = result.RowsAffected() + numResult, _ := result.RowsAffected() fmt.Printf("Rows Deleted: %d", numResult) // Rows Deleted: 13 } + +// Here we create a simple function that will take care of errors, helping with some code clean up +func handleError(err error) { + if err != nil { + panic(err) + } +} ``` ## API From f2b0dfd1c972daae6451da77ace7f769fefe32d8 Mon Sep 17 00:00:00 2001 From: Gaurav Gogia Date: Mon, 12 Mar 2018 02:28:55 +0530 Subject: [PATCH 2/6] URI update in example code --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa37a10..74f348d 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ go get github.com/johnnadratowski/golang-neo4j-bolt-driver ```go func quickNDirty() { driver := bolt.NewDriver() - conn, _ := driver.OpenNeo("bolt://localhost:7687") + conn, _ := driver.OpenNeo("bolt://username:password@localhost:7687") defer conn.Close() // Start by creating a node @@ -74,7 +74,7 @@ func quickNDirty() { // Constants to be used throughout the example const ( - URI = "bolt://localhost:7687" + URI = "bolt://username:password@localhost:7687" CreateNode = "CREATE (n:NODE {foo: {foo}, bar: {bar}})" GetNode = "MATCH (n:NODE) RETURN n.foo, n.bar" RelationNode = "MATCH path=(n:NODE)-[:REL]->(m) RETURN path" From b2dcdab574946c3ee523f7c9663df59cf258de6b Mon Sep 17 00:00:00 2001 From: Alex Crowder the_kid Date: Wed, 13 Jun 2018 11:45:54 -0600 Subject: [PATCH 3/6] Add check for rowsAffected = 0 --- result.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/result.go b/result.go index c37a921..97830b7 100644 --- a/result.go +++ b/result.go @@ -39,6 +39,11 @@ func (r boltResult) LastInsertId() (int64, error) { // updated. If this changes in the future, number updated will be added to the output of this // interface. func (r boltResult) RowsAffected() (int64, error) { + // metadata omits stats when rowsAffected == 0, check existence to prevent panic + if _, ok := r.metadata["stats"]; !ok { + return 0, nil + } + stats, ok := r.metadata["stats"].(map[string]interface{}) if !ok { return -1, errors.New("Unrecognized type for stats metadata: %#v", r.metadata) From 4e7dc8ea70d90f1d3466453a0bab7b8b5bfe58b4 Mon Sep 17 00:00:00 2001 From: John Nadratowski Date: Sat, 14 Jul 2018 11:09:59 -0400 Subject: [PATCH 4/6] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 74f348d..3007e87 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![GoDoc](https://godoc.org/github.com/johnnadratowski/golang-neo4j-bolt-driver?status.svg)](https://godoc.org/github.com/johnnadratowski/golang-neo4j-bolt-driver) +**ANNOUNCEMENT: I must apologize to the community for not being more responsive. Because of personal life events I am really not able to properly maintain this codebase. Certain events lead me to believe an official Neo4J Golang driver was to be released soon, but it seems like that may not necessarily be the case. Since I am unable to properly maintain this codebase at this juncture. At this point I think it makes sense to open up this repo to collaborators who are interested in helping with maintenance. Please feel free to email me directly if you're interested.** + Implements the Neo4J Bolt Protocol specification: As of the time of writing this, the current version is v3.1.0-M02 From 337d45d904c16398f07b6ce44019f4400af43516 Mon Sep 17 00:00:00 2001 From: John Nadratowski Date: Sat, 14 Jul 2018 11:10:24 -0400 Subject: [PATCH 5/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3007e87..e744608 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![GoDoc](https://godoc.org/github.com/johnnadratowski/golang-neo4j-bolt-driver?status.svg)](https://godoc.org/github.com/johnnadratowski/golang-neo4j-bolt-driver) -**ANNOUNCEMENT: I must apologize to the community for not being more responsive. Because of personal life events I am really not able to properly maintain this codebase. Certain events lead me to believe an official Neo4J Golang driver was to be released soon, but it seems like that may not necessarily be the case. Since I am unable to properly maintain this codebase at this juncture. At this point I think it makes sense to open up this repo to collaborators who are interested in helping with maintenance. Please feel free to email me directly if you're interested.** +**ANNOUNCEMENT: I must apologize to the community for not being more responsive. Because of personal life events I am really not able to properly maintain this codebase. Certain other events lead me to believe an official Neo4J Golang driver was to be released soon, but it seems like that may not necessarily be the case. Since I am unable to properly maintain this codebase at this juncture. At this point I think it makes sense to open up this repo to collaborators who are interested in helping with maintenance. Please feel free to email me directly if you're interested.** Implements the Neo4J Bolt Protocol specification: As of the time of writing this, the current version is v3.1.0-M02 From c49e97fab6ec807646f80ff78a4facbaadc61098 Mon Sep 17 00:00:00 2001 From: John Nadratowski Date: Sat, 14 Jul 2018 11:10:50 -0400 Subject: [PATCH 6/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e744608..810a6af 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![GoDoc](https://godoc.org/github.com/johnnadratowski/golang-neo4j-bolt-driver?status.svg)](https://godoc.org/github.com/johnnadratowski/golang-neo4j-bolt-driver) -**ANNOUNCEMENT: I must apologize to the community for not being more responsive. Because of personal life events I am really not able to properly maintain this codebase. Certain other events lead me to believe an official Neo4J Golang driver was to be released soon, but it seems like that may not necessarily be the case. Since I am unable to properly maintain this codebase at this juncture. At this point I think it makes sense to open up this repo to collaborators who are interested in helping with maintenance. Please feel free to email me directly if you're interested.** +**ANNOUNCEMENT: I must apologize to the community for not being more responsive. Because of personal life events I am really not able to properly maintain this codebase. Certain other events lead me to believe an official Neo4J Golang driver was to be released soon, but it seems like that may not necessarily be the case. Since I am unable to properly maintain this codebase at this juncture, at this point I think it makes sense to open up this repo to collaborators who are interested in helping with maintenance. Please feel free to email me directly if you're interested.** Implements the Neo4J Bolt Protocol specification: As of the time of writing this, the current version is v3.1.0-M02