diff --git a/common-content/antora.yml b/common-content/antora.yml index df8fdc3d..08dc3037 100644 --- a/common-content/antora.yml +++ b/common-content/antora.yml @@ -1,4 +1,3 @@ name: common-content title: Neo4j Driver Manuals Common content version: '6' -prerelease: true diff --git a/go-manual/antora.yml b/go-manual/antora.yml index 5ba4e945..7d892aee 100644 --- a/go-manual/antora.yml +++ b/go-manual/antora.yml @@ -1,13 +1,11 @@ name: go-manual title: Neo4j Go Driver Manual -version: '5' +version: '6' start_page: ROOT:index.adoc nav: - modules/ROOT/content-nav.adoc asciidoc: attributes: - go-driver-version: '5.28' # the version of published go api docs - go-driver-apidoc-release: '5.0' # the github branch or release that contains examples included in docs - go-examples: https://raw.githubusercontent.com/neo4j/neo4j-go-driver/{go-driver-apidoc-release}/tests/integration/examples - common-partial: 5@common-content:ROOT:partial$ - common-image: 5@common-content:ROOT:image$ + go-driver-version: '6.0' # the version of published go api docs + common-partial: 6@common-content:ROOT:partial$ + common-image: 6@common-content:ROOT:image$ diff --git a/go-manual/modules/ROOT/pages/bookmarks.adoc b/go-manual/modules/ROOT/pages/bookmarks.adoc index f8af0260..6712350d 100644 --- a/go-manual/modules/ROOT/pages/bookmarks.adoc +++ b/go-manual/modules/ROOT/pages/bookmarks.adoc @@ -1,12 +1,9 @@ = Coordinate parallel transactions -When working with a Neo4j cluster, <> is enforced by default in most cases, which guarantees that a query is able to read changes made by previous queries. +When working with a Neo4j cluster, <> is enforced by default in most cases, guaranteeing that a query is able to read changes made by previous queries. The same does not happen by default for multiple xref:transactions.adoc[transactions] running in parallel though. In that case, you can use _bookmarks_ to have one transaction wait for the result of another to be propagated across the cluster before running its own work. -This is not a requirement, and *you should only use bookmarks if you _need_ casual consistency across different transactions*, as waiting for bookmarks can have a negative performance impact. - -A _bookmark_ is a token that represents some state of the database. -By passing one or multiple bookmarks along with a query, the server will make sure that the query does not get executed before the represented state(s) have been established. +This is not a requirement, and *you should only use <> if you _need_ casual consistency across different transactions*, as waiting for bookmarks can have a negative performance impact. == Bookmarks with `ExecuteQuery()` @@ -43,7 +40,7 @@ neo4j.ExecuteQuery( == Bookmarks within a single session -Bookmark management happens automatically for queries run within a single session, so that you can trust that queries inside one session are causally chained. +Bookmark management happens automatically for queries run within a single session: queries inside the same session are causally chained. [source, go] ---- @@ -79,7 +76,7 @@ package main import ( "fmt" "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -89,7 +86,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -131,7 +128,7 @@ func main() { } // Create a Person node -func createPerson(ctx context.Context, session neo4j.SessionWithContext, name string) (any, error) { +func createPerson(ctx context.Context, session neo4j.Session, name string) (any, error) { return session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { return tx.Run(ctx, @@ -142,7 +139,7 @@ func createPerson(ctx context.Context, session neo4j.SessionWithContext, name st // Create an employment relationship to a pre-existing company node // This relies on the person first having been created -func employ(ctx context.Context, session neo4j.SessionWithContext, personName string, companyName string) (any, error) { +func employ(ctx context.Context, session neo4j.Session, personName string, companyName string) (any, error) { return session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { return session.Run(ctx, ` @@ -157,7 +154,7 @@ func employ(ctx context.Context, session neo4j.SessionWithContext, personName st } // Create a friendship between two people -func createFriendship(ctx context.Context, session neo4j.SessionWithContext, nameA string, nameB string) (any, error) { +func createFriendship(ctx context.Context, session neo4j.Session, nameA string, nameB string) (any, error) { return session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) { return session.Run(ctx, ` @@ -172,7 +169,7 @@ func createFriendship(ctx context.Context, session neo4j.SessionWithContext, nam } // Retrieve and display all friendships -func printFriendships(ctx context.Context, session neo4j.SessionWithContext) (any, error) { +func printFriendships(ctx context.Context, session neo4j.Session) (any, error) { return session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, error) { result, err := session.Run(ctx, @@ -192,19 +189,19 @@ func printFriendships(ctx context.Context, session neo4j.SessionWithContext) (an } ---- -<1> Collect and combine bookmarks from different sessions using `SessionWithContext.LastBookmarks()` and `neo4j.CombineBookmarks()`, storing them in a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#Bookmarks[`Bookmarks`] object. +<1> Collect and combine bookmarks from different sessions using `Session.LastBookmarks()` and `neo4j.CombineBookmarks()`, storing them in a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Bookmarks[`Bookmarks`] object. <2> Use them to initialize another session with the `Bookmarks` config parameter. image:{common-image}/driver-passing-bookmarks.svg[] [TIP] The use of bookmarks can negatively impact performance, since all queries are forced to wait for the latest changes to be propagated across the cluster. -For simple use-cases, try to group queries within a single transaction, or within a single session. +If possible, group queries within a single transaction, or within a single session. == Mix `ExecuteQuery()` and sessions -To ensure causal consistency among transactions executed partly with `ExecuteQuery()` and partly with sessions, you can use the parameter `BookmarkManager` upon session creation, setting it to `driver.ExecuteQueryBookmarkManager()`. +To ensure causal consistency among transactions executed partly with `ExecuteQuery()` and partly with sessions, use the parameter `BookmarkManager` upon session creation, setting it to `driver.ExecuteQueryBookmarkManager()`. Since that is the default bookmark manager for `ExecuteQuery()` calls, this will ensure that all work is executed under the same bookmark manager and thus causally consistent. [source, go] diff --git a/go-manual/modules/ROOT/pages/concurrency.adoc b/go-manual/modules/ROOT/pages/concurrency.adoc index 126e8407..bd753e14 100644 --- a/go-manual/modules/ROOT/pages/concurrency.adoc +++ b/go-manual/modules/ROOT/pages/concurrency.adoc @@ -1,6 +1,6 @@ = Run concurrent transactions -You may leverage link:https://go.dev/tour/concurrency/1[Goroutines and channels] to run concurrent queries, or to delegate the processing of a query's result to multiple threads. +Use link:https://go.dev/tour/concurrency/1[Goroutines and channels] to run concurrent queries, or to delegate the processing of a query's result to multiple threads. The examples below also use the Go link:https://pkg.go.dev/sync[`sync` package] to coordinate different routines. If you are not familiar with concurrency in Go, checkout link:https://go.dev/blog/pipelines[The Go Programming Language -> Go Concurrency Patterns: Pipelines and cancellation]. @@ -20,7 +20,7 @@ import ( "context" "time" "sync" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -30,7 +30,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -65,7 +65,7 @@ func main() { } } -func queryToChannel(ctx context.Context, driver neo4j.DriverWithContext) chan *neo4j.Record { +func queryToChannel(ctx context.Context, driver neo4j.Driver) chan *neo4j.Record { recordsC := make(chan *neo4j.Record, 10) // <2> session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: ""}) defer session.Close(ctx) @@ -100,7 +100,7 @@ func consumer(wg *sync.WaitGroup, records <-chan *neo4j.Record, log chan string, } ---- -<1> A Goroutine runs the query to the Neo4j server with a xref:transactions.adoc[managed transaction]. +<1> A Goroutine runs the query to the Neo4j server with a xref:transactions.adoc#managed-transactions[managed transaction]. Notice that the driver session is created _inside_ the routine, as sessions are not thread-safe. <2> The channel `recordsC` is where the query result records get streamed to. The transaction function from `.ExecuteWrite()` writes to it, and the various ``consumer``s read from it. @@ -126,7 +126,7 @@ import ( "fmt" "context" "sync" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -136,7 +136,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -167,7 +167,7 @@ func main() { } // Run Neo4j query with random sleep time, returning the sleep time in ms -func runQuery(wg *sync.WaitGroup, ctx context.Context, driver neo4j.DriverWithContext, log chan string) { +func runQuery(wg *sync.WaitGroup, ctx context.Context, driver neo4j.Driver, log chan string) { defer wg.Done() // will communicate that routine is done result, err := neo4j.ExecuteQuery(ctx, driver, ` WITH round(rand()*2000) AS waitTime diff --git a/go-manual/modules/ROOT/pages/connect-advanced.adoc b/go-manual/modules/ROOT/pages/connect-advanced.adoc index d695e29b..ac0a50e9 100644 --- a/go-manual/modules/ROOT/pages/connect-advanced.adoc +++ b/go-manual/modules/ROOT/pages/connect-advanced.adoc @@ -12,11 +12,10 @@ include::{common-partial}/connect-advanced.adoc[tag=connection-protocols] === Basic authentication The basic authentication scheme relies on traditional username and password. -These can either be the credentials for your local installation, or the ones provided with an Aura instance. [source, go] ---- -driver, err := neo4j.NewDriverWithContext( +driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) ---- @@ -32,7 +31,7 @@ It can only be used if the server has the link:{neo4j-docs-base-uri}/kerberos-ad [source, go, test-skip] ---- -driver, err := neo4j.NewDriverWithContext(dbUri, neo4j.KerberosAuth(ticket)) +driver, err := neo4j.NewDriver(dbUri, neo4j.KerberosAuth(ticket)) ---- @@ -42,7 +41,7 @@ The bearer authentication scheme requires a base64-encoded token provided by an [source, go, test-skip] ---- -driver, err := neo4j.NewDriverWithContext(dbUri, neo4j.BearerAuth(token)) +driver, err := neo4j.NewDriver(dbUri, neo4j.BearerAuth(token)) ---- [NOTE] @@ -52,27 +51,26 @@ Once configured, clients can discover Neo4j's configuration through the link:htt === Custom authentication -Use the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#CustomAuth[`CustomAuth`] to log into a server having a custom authentication scheme. +Use the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#CustomAuth[`CustomAuth`] to log into a server having a custom authentication scheme. === No authentication -Use the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#NoAuth[`NoAuth`] to access a server where authentication is disabled. +Use the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#NoAuth[`NoAuth`] to access a server where authentication is disabled. [source, go, test-skip] ---- -driver, err := neo4j.NewDriverWithContext(dbUri, neo4j.NoAuth()) +driver, err := neo4j.NewDriver(dbUri, neo4j.NoAuth()) ---- [#rotating-tokens] -[role=label--new-5.14] == Rotating authentication tokens It is possible to rotate authentication tokens that are expected to expire (e.g. SSO). -You need to provide a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#TokenManager[`TokenManager`] instance when instantiating the `Driver`, rather than a static authentication token. +You need to provide a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/auth#TokenManager[`TokenManager`] instance when instantiating the `Driver`, rather than a static authentication token. -The easiest way to get started is to use one of built-in implementations: link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#BasicTokenManager[`BasicTokenManager`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#BearerTokenManager[`BearerTokenManager`]. +The easiest way to get started is to use one of built-in implementations: link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/auth#BasicTokenManager[`BasicTokenManager`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/auth#BearerTokenManager[`BearerTokenManager`]. .Rotating a bearer token expiring every 60 seconds [source, go, test-skip] @@ -92,7 +90,7 @@ fetchAuthTokenFromProvider := func(ctx context.Context) (neo4j.AuthToken, *time. } // create a new driver with a bearer token manager -_, _ = neo4j.NewDriverWithContext(dbUri, auth.BearerTokenManager(fetchAuthTokenFromProvider)) +_, _ = neo4j.NewDriver(dbUri, auth.BearerTokenManager(fetchAuthTokenFromProvider)) ---- [NOTE] @@ -104,7 +102,7 @@ You can switch users at both xref:query-simple.adoc#impersonation[query level] a [#mtls] -[role=label--new-5.27 label--not-on-aura] +[role=label--not-on-aura] == Mutual TLS (client-side certificates as 2FA) Mutual TLS (mTLS) allows you to use a client certificate as second factor for authenticating with the server. @@ -119,8 +117,8 @@ For mTLS to work, the driver's connection with the server must be encrypted, i.e ===== [.include-with-static-certificate] ====== -Use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#NewStaticClientCertificateProvider[`auth.NewStaticClientCertificateProvider()`] for static certificates. + -The method takes a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#ClientCertificate[`ClientCertificate`] instance. +Use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/auth#NewStaticClientCertificateProvider[`auth.NewStaticClientCertificateProvider()`] for static certificates. + +The method takes a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/auth#ClientCertificate[`ClientCertificate`] instance. [source, go, test-skip] ---- @@ -132,7 +130,7 @@ certProvider, err := auth.NewStaticClientCertificateProvider(auth.ClientCertific if err != nil { log.Fatalf("Failed to load certificate: %v", err) } -_, _ = neo4j.NewDriverWithContext(dbUri, neo4j.BasicAuth(dbUser, dbPassword, ""), func(config *config.Config) { +_, _ = neo4j.NewDriver(dbUri, neo4j.BasicAuth(dbUser, dbPassword, ""), func(config *config.Config) { config.ClientCertificateProvider = certProvider }) ---- @@ -141,8 +139,8 @@ _, _ = neo4j.NewDriverWithContext(dbUri, neo4j.BasicAuth(dbUser, dbPassword, "") [.include-with-rotating-certificate] ====== -Use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#NewRotatingClientCertificateProvider[`auth.NewRotatingClientCertificateProvider()`] for rotating certificates. + -The method takes a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#ClientCertificate[`ClientCertificate`] instance. +Use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/auth#NewRotatingClientCertificateProvider[`auth.NewRotatingClientCertificateProvider()`] for rotating certificates. + +The method takes a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/auth#ClientCertificate[`ClientCertificate`] instance. [source, go, test-skip] ---- @@ -155,7 +153,7 @@ certProvider, err := auth.NewRotatingClientCertificateProvider(auth.ClientCertif if err != nil { log.Fatalf("Failed to load certificate: %v", err) } -_, _ = neo4j.NewDriverWithContext(dbUri, neo4j.BasicAuth(dbUser, dbPassword, ""), func(config *config.Config) { +_, _ = neo4j.NewDriver(dbUri, neo4j.BasicAuth(dbUser, dbPassword, ""), func(config *config.Config) { config.ClientCertificateProvider = certProvider }) // use the driver a bit... @@ -175,21 +173,21 @@ if err != nil { ===== -For more information, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#ClientCertificateProvider[API docs -> `ClientCertificateProvider`]. +For more information, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/auth#ClientCertificateProvider[API docs -> `ClientCertificateProvider`]. == Custom address resolver -When creating a `DriverWithContext` object, you can specify a _resolver_ function to resolve the connection address the driver is initialized with. +When creating a `Driver` object, you can specify a _resolver_ function to resolve the connection address the driver is initialized with. Note that addresses that the driver receives in routing tables are not resolved with the custom resolver. -Your resolver function is called with a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/config#ServerAddress[`ServerAddress`] object and should return a list of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/config#ServerAddress[`ServerAddress`] objects. +Your resolver function is called with a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#ServerAddress[`ServerAddress`] object and should return a list of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#ServerAddress[`ServerAddress`] objects. .Connection to `example.com` on port `9999` is resolved to `localhost` on port `7687` [source, go] ---- -// import "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" +// import "github.com/neo4j/neo4j-go-driver/v6/neo4j/config" -driver, err := neo4j.NewDriverWithContext( +driver, err := neo4j.NewDriver( "neo4j://example.com:9999", neo4j.BasicAuth(dbUser, dbPassword, ""), func(conf *config.Config) { conf.AddressResolver = func(address config.ServerAddress) []config.ServerAddress { @@ -204,7 +202,7 @@ defer driver.Close(ctx) == Further connection parameters -You can find all `DriverWithContext` configuration parameters in the link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/config#Config[API documentation -> config package]. +You can find all `Driver` configuration parameters in the link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#Config[API documentation -> config package]. ifndef::backend-pdf[] diff --git a/go-manual/modules/ROOT/pages/connect.adoc b/go-manual/modules/ROOT/pages/connect.adoc index e64627d0..030d6512 100644 --- a/go-manual/modules/ROOT/pages/connect.adoc +++ b/go-manual/modules/ROOT/pages/connect.adoc @@ -5,7 +5,7 @@ Once you have xref:install.adoc#install-driver[installed the driver] and have xr == Connect to the database -You connect to a Neo4j database by creating a <> object and providing a URL and an authentication token. +You connect to a Neo4j database by creating a <> object and providing a URL and an authentication token. [source, go, role=nocollapse] ---- @@ -14,7 +14,7 @@ package main import ( "context" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -22,7 +22,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( // <1> + driver, err := neo4j.NewDriver( // <1> dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -38,17 +38,17 @@ func main() { } ---- -<1> Creating a `DriverWithContext` instance only provides information on _how_ to access the database, but does not actually _establish_ a connection. -Connection is instead deferred to when the first query is executed. +<1> Creating a `Driver` instance only provides information on _how_ to access the database, but does not actually _establish_ a connection. +Connection is deferred to when the first query is executed. <2> Most driver functions require a `context.Context` parameter, see the link:https://pkg.go.dev/context[context] package. <3> To verify immediately that the driver can connect to the database (valid credentials, compatible versions, etc), use the `.VerifyConnectivity(ctx)` method after initializing the driver. -<4> Always close `DriverWithContext` objects to free up all allocated resources, even upon unsuccessful connection or runtime errors in subsequent querying. -The safest practice is to `defer` the call to `DriverWithContext.Close(ctx)` after the object is successfully created. +<4> Always close `Driver` objects to free up all allocated resources, even upon unsuccessful connection or runtime errors in subsequent querying. +The safest practice is to `defer` the call to `Driver.Close(ctx)` after the object is successfully created. Note that there are corner cases in which `.Close()` might return an error, so you may want to catch that as well. -**`Driver` objects are immutable, thread-safe, and expensive to create**, so your application should create only one instance and pass it around (you may share `Driver` instances across threads). -If you need to query the database through several different users, use xref:query-simple#impersonation[impersonation] without creating a new `DriverWithContext` instance. -If you want to alter a `DriverWithContext` configuration, you need to create a new object. +**`Driver` objects are immutable, thread-safe, and expensive to create**, so your application should create only one instance and pass it around (you can share `Driver` instances across threads). +If you need to query the database through several different users, use xref:query-simple#impersonation[impersonation] without creating a new `Driver` instance. +If you want to alter a `Driver` configuration, you need to create a new object. [TIP] The driver also supports other xref:connect-advanced.adoc#authentication-methods[authentication methods] (kerberos, bearer, custom). @@ -56,11 +56,10 @@ The driver also supports other xref:connect-advanced.adoc#authentication-methods == Connect to an Aura instance -When you create an <> instance, you may download a text file (a so-called _Dotenv file_) containing the connection information to the database in the form of environment variables. +When you create an <> instance, you may download a text file (a so-called _Dotenv file_) containing the connection information to the database as environment variables. The file has a name of the form `Neo4j-a0a2fa1d-Created-2023-11-06.txt`. -You can either manually extract the URI and the credentials from that file, or use a third party-module to load them. -We recommend the module package link:https://pkg.go.dev/github.com/joho/godotenv[`godotenv`] for that purpose. +You can either manually extract the URI and the credentials from that file, or use a third party-module (ex. link:https://pkg.go.dev/github.com/joho/godotenv[`godotenv`]) to load them. [source, go] ---- @@ -71,7 +70,7 @@ import ( "os" "fmt" "github.com/joho/godotenv" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -83,7 +82,7 @@ func main() { dbUri := os.Getenv("NEO4J_URI") dbUser := os.Getenv("NEO4J_USERNAME") dbPassword := os.Getenv("NEO4J_PASSWORD") - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -106,7 +105,7 @@ When interacting with a Neo4j database through the driver, it doesn't make a dif == Further connection parameters -For more `DriverWithContext` configuration parameters and further connection settings, see xref:connect-advanced.adoc[Advanced connection information]. +For more `Driver` configuration parameters and further connection settings, see xref:connect-advanced.adoc[Advanced connection information]. ifndef::backend-pdf[] diff --git a/go-manual/modules/ROOT/pages/data-types.adoc b/go-manual/modules/ROOT/pages/data-types.adoc index a10fe511..2cdc9adf 100644 --- a/go-manual/modules/ROOT/pages/data-types.adoc +++ b/go-manual/modules/ROOT/pages/data-types.adoc @@ -58,7 +58,7 @@ import ( "fmt" "context" "time" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" "reflect" ) @@ -69,10 +69,13 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, _ := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) - driver.VerifyConnectivity(ctx) + defer driver.Close(ctx) + if err != nil { + panic(err) + } // Define a date, with timezone location, _ := time.LoadLocation("Europe/Stockholm") @@ -107,7 +110,7 @@ import ( "fmt" "context" "time" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" "reflect" ) @@ -118,10 +121,13 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, _ := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) - driver.VerifyConnectivity(ctx) + defer driver.Close(ctx) + if err != nil { + panic(err) + } // Query and return a neo4j.Time object result, err := neo4j.ExecuteQuery(ctx, driver, ` @@ -160,7 +166,7 @@ duration := neo4j.Duration{ fmt.Println(duration) // 'P1Y2DT3.000000004S' ---- -For full documentation, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype#Duration[API documentation -> Duration]. +For full documentation, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/dbtype#Duration[API documentation -> Duration]. == Spatial types @@ -277,7 +283,7 @@ fmt.Println("Node properties:", node.Props) // Node properties: map[name:Alice] ---- -For full documentation, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype#Node[API documentation -> Node]. +For full documentation, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/dbtype#Node[API documentation -> Node]. === Relationship @@ -315,7 +321,7 @@ fmt.Println("Relationship end elID:", relationship.EndElementId) // Relationship end elID: 4:2691aa68-87cc-467d-9d09-431df9f5c456:1 ---- -For full documentation, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype#Relationship[API documentation -> Relationship]. +For full documentation, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/dbtype#Relationship[API documentation -> Relationship]. === Path @@ -330,7 +336,7 @@ package main import ( "fmt" "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -340,10 +346,13 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, _ := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) - driver.VerifyConnectivity(ctx) + defer driver.Close(ctx) + if err != nil { + panic(err) + } // Create some :Person nodes linked by :KNOWS relationships addFriend(ctx, driver, "Alice", "BFF", "Bob") @@ -372,7 +381,7 @@ func main() { } } -func addFriend(ctx context.Context, driver neo4j.DriverWithContext, name string, status string, friendName string) { +func addFriend(ctx context.Context, driver neo4j.Driver, name string, status string, friendName string) { _, err := neo4j.ExecuteQuery(ctx, driver, ` MERGE (p:Person {name: $name}) MERGE (p)-[r:KNOWS {status: $status, since: date()}]->(friend:Person {name: $friendName}) @@ -389,7 +398,80 @@ func addFriend(ctx context.Context, driver neo4j.DriverWithContext, name string, ---- -For full documentation, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype#Path[API documentation -> Path]. +For full documentation, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/dbtype#Path[API documentation -> Path]. + + +[role=label--new-6.0] +[#vector] +== Vector + +The type link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Vector[`Vector`] maps to the Cypher type link:https://neo4j.com/docs/cypher-manual/current/values-and-types/vector/[`VECTOR`]. +Vector objects are stored as contiguous blocks of memory, containing homogeneous values. +You can create vectors from lists of `float64`, `float32`, `int8`, `int16`, `int32`, `int64`. + +[IMPORTANT] +Storing `VECTOR` objects in the database requires a server version >= 2025.10, Enterprise Edition. + +.Create, store, and retrieve a vector +[source, go] +---- +package main + +import ( + "context" + "fmt" + + "github.com/neo4j/neo4j-go-driver/v6/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j/dbtype" +) + +func main() { + // Connection to database + ctx := context.Background() + dbUri := "" + dbUser := "" + dbPassword := "" + driver, err := neo4j.NewDriver( + dbUri, + neo4j.BasicAuth(dbUser, dbPassword, "")) + defer driver.Close(ctx) + if err != nil { + panic(err) + } + + // Create and store a vector + vec := dbtype.Vector[float64]{1.0, 2.0, 3.0} + + _, err := neo4j.ExecuteQuery(ctx, driver, + "MERGE (n:Doc {vector: $vec})", + map[string]any{"vec": vec}, + neo4j.EagerResultTransformer) + if err != nil { + panic(err) + } + + result, err := neo4j.ExecuteQuery(ctx, driver, + "MATCH (n:Doc) RETURN n.vec AS vec LIMIT 1", + nil, + neo4j.EagerResultTransformer) + if err != nil { + panic(err) + } + + if v, ok := result.Records[0].Values[0].(dbtype.Vector[float64]); ok { + fmt.Printf("Read vector: %v\n", v) + } + // Read vector: vector([1.0, 2.0, 3.0], 3, FLOAT64 NOT NULL) +} +---- + + +[role=label--new-6.0] +[#unsupportedtype] +== UnsupportedType + +The type link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#UnsupportedType[`UnsupportedType`] is used for data types returned by Cypher queries, but that the driver doesn't recognize. +This happens when the client version is too old with respect to the server version. ifndef::backend-pdf[] diff --git a/go-manual/modules/ROOT/pages/install.adoc b/go-manual/modules/ROOT/pages/install.adoc index 38ebaebe..d6b81ef0 100644 --- a/go-manual/modules/ROOT/pages/install.adoc +++ b/go-manual/modules/ROOT/pages/install.adoc @@ -15,16 +15,16 @@ cd neo4j-app go mod init neo4j-app ---- -From within a module, use `go get` to install the link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/[Neo4j Go Driver]: +From within a module, use `go get` to install the link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/[Neo4j Go Driver]: [source, bash] ---- -go get github.com/neo4j/neo4j-go-driver/v5 +go get github.com/neo4j/neo4j-go-driver/v6 ---- -Always use the latest version of the driver, as it will always work both with the previous Neo4j <> release and with the current and next major releases. -The latest `5.x` driver supports connection to any Neo4j instances version 4.4, 5.x, and 2025.x. -For a detailed list of changes across versions, see the link:https://github.com/neo4j/neo4j-go-driver/wiki/5.x-changelog[driver's changelog]. +The latest version of the driver is guaranteed to work with all <> releases of the Neo4j server, as well as with the current and next major releases. +The latest `6.x` driver supports connection to Neo4j instances version 4.4, 5.x, and 2025.x. +For a detailed list of changes across versions, see the link:https://github.com/neo4j/neo4j-go-driver/wiki/6.x-changelog[driver's changelog]. [NOTE] The Neo4j Go Driver is compatible (and requires) any link:https://go.dev/doc/devel/release#policy[officially maintained Go version]. diff --git a/go-manual/modules/ROOT/pages/performance.adoc b/go-manual/modules/ROOT/pages/performance.adoc index 261793ae..9e2b49c2 100644 --- a/go-manual/modules/ROOT/pages/performance.adoc +++ b/go-manual/modules/ROOT/pages/performance.adoc @@ -1,5 +1,6 @@ = Performance recommendations + [[target-database]] == Always specify the target database @@ -78,7 +79,7 @@ res, err := session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, err ---- An even faster approach is to skip `.ExecuteRead/Write()` and call `.Run()` directly on the session. -The queries run as auto-commit transaction, and are still isolated from other concurrent queries, but if any of them fail, they will not be retried. +The queries run as auto-commit transactions, and are still isolated from other concurrent queries, but if any of them fail, they will not be retried. With this method, you trade some robustness for more throughput, as the queries are shot to the server as fast as it can handle. One upper limit on the client size is given by the size of the connection pool: each call to `.Run()` borrows a connection, so the amount of parallel work is limited by the number of available connections. @@ -134,7 +135,7 @@ import ( "context" "time" "fmt" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) // Returns 250 records, each with properties @@ -153,7 +154,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -173,7 +174,7 @@ func main() { eagerLoading(ctx, driver) } -func lazyLoading(ctx context.Context, driver neo4j.DriverWithContext) { +func lazyLoading(ctx context.Context, driver neo4j.Driver) { defer timer("lazyLoading")() sleepTimeParsed, err := time.ParseDuration(sleepTime) @@ -200,7 +201,7 @@ func lazyLoading(ctx context.Context, driver neo4j.DriverWithContext) { }) } -func eagerLoading(ctx context.Context, driver neo4j.DriverWithContext) { +func eagerLoading(ctx context.Context, driver neo4j.Driver) { defer timer("eagerLoading")() log("Submit query") @@ -239,7 +240,7 @@ func timer(name string) func() { ---- .Output -[source, output, role=nocollapse] +[source, output] ---- [ 1718802595 ] LAZY LOADING (executeRead) [ 1718802595 ] Submit query @@ -269,7 +270,7 @@ With lazy loading, the client can also stop requesting records after some condit [TIP] ==== -The driver's link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SessionConfig[fetch size] affects the behavior of lazy loading. +The driver's link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#SessionConfig[fetch size] affects the behavior of lazy loading. It instructs the server to stream an amount of records equal to the fetch size, and then wait until the client has caught up before retrieving and sending more. The fetch size usually allows to bound memory consumption on the client side, especially for results in which the variance of the records size is small. diff --git a/go-manual/modules/ROOT/pages/query-advanced.adoc b/go-manual/modules/ROOT/pages/query-advanced.adoc index 0edd4efe..4c12d53f 100644 --- a/go-manual/modules/ROOT/pages/query-advanced.adoc +++ b/go-manual/modules/ROOT/pages/query-advanced.adoc @@ -3,12 +3,12 @@ [[implicit-transactions]] == Implicit (or auto-commit) transactions -This is the most basic and limited form with which to run a Cypher query. -The driver will not automatically retry implicit transactions, as it does instead for queries run with xref:query-simple.adoc[`ExecuteQuery()`] and with xref:transactions#managed-transactions[managed transactions]. -Implicit transactions should only be used when the other driver query interfaces do not fit the purpose, or for quick prototyping. +This is the most basic and limited form to run a Cypher query with. +The driver does *not* automatically retry implicit transactions, as it does instead for queries run with xref:query-simple.adoc[`ExecuteQuery()`] and with xref:transactions#managed-transactions[managed transactions]. +Implicit transactions should only be used when the other query interfaces do not fit the purpose, or for quick prototyping. -You run an implicit transaction with the method `SessionWithContext.Run()`. -It returns a `ResultWithContext` object that needs to be xref:transactions#process-result[processed accordingly]. +You run an implicit transaction with the method `Session.Run()`. +It returns a `Result` object that needs to be xref:transactions#process-result[processed accordingly]. [source, go] ---- @@ -26,15 +26,16 @@ An implicit transaction gets committed _at the latest_ when the session is destr Other than that, there is no clear guarantee on when exactly an implicit transaction will be committed during the lifetime of a session. To ensure an implicit transaction is committed, you can call the `.Consume(ctx)` method on its result. -Since the driver cannot figure out whether the query in a `SessionWithContext.Run()` call requires a read or write session with the database, it defaults to write. If your implicit transaction contains read queries only, there is a performance gain in xref:transactions#_request_routing[making the driver aware] by setting the session config `AccessMode: neo4j.AccessModeRead` when creating the session. +Since the driver cannot figure out whether the query in a `Session.Run()` call requires a read or write session with the database, it defaults to write. +If your implicit transaction contains read queries only, there is a performance gain in xref:transactions#_request_routing[making the driver aware] by setting the session config `AccessMode: neo4j.AccessModeRead` when creating the session. [TIP] -*Implicit transactions are the only ones that can be used for link:{neo4j-docs-base-uri}/cypher-manual/{page-version}/subqueries/subqueries-in-transactions[`CALL { ... } IN TRANSACTIONS`] queries*. +Implicit transactions are the only ones that can be used for link:{neo4j-docs-base-uri}/cypher-manual/current/subqueries/subqueries-in-transactions[`CALL { ... } IN TRANSACTIONS`] queries. === Import CSV files -The most common use case for using `SessionWithContext.Run()` is for importing large CSV files into the database with the link:{neo4j-docs-base-uri}/cypher-manual/current/clauses/load-csv[`LOAD CSV`] Cypher clause, and preventing timeout errors due to the size of the transaction. +The most common use case for using `Session.Run()` is for importing large CSV files into the database with the link:{neo4j-docs-base-uri}/cypher-manual/current/clauses/load-csv[`LOAD CSV`] Cypher clause, and preventing timeout errors due to the size of the transaction. .Import CSV data into a Neo4j database [source, go] @@ -64,7 +65,7 @@ For more information, see link:{neo4j-docs-base-uri}/cypher-manual/current/claus === Transaction configuration -You can exert further control on implicit transactions by providing configuration callbacks after the third argument in `SessionWithContext.Run()` calls. +You can exert further control on implicit transactions by providing configuration callbacks after the third argument in `Session.Run()` calls. The configuration callbacks allow to specify a query timeout and to attach metadata to the transaction. For more information, see xref:transactions.adoc#_transaction_configuration[Transactions -- Transaction configuration]. @@ -84,36 +85,33 @@ people, err := session.Run(ctx, In general, you should not concatenate parameters directly into a query, but rather use xref:query-simple#query-parameters[query parameters]. There can however be circumstances where your query structure prevents the usage of parameters in all its parts. -In fact, although parameters can be used for literals and expressions as well as node and relationship ids, they cannot be used for the following constructs: +In fact, although parameters work for literals and expressions, as well as link:https://neo4j.com/docs/cypher-manual/current/clauses/match/#dynamic-match[node labels and relationship types], they can't be used for property keys, so `MATCH (n) WHERE n.$param = 'something'` is invalid. -- property keys, so `MATCH (n) WHERE n.$param = 'something'` is invalid; -- relationship types, so `MATCH (n)-[:$param]->(m)` is invalid; -- labels, so `MATCH (n:$param)` is invalid. - -For those queries, you are forced to use string concatenation. -To protect against link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Cypher injections], you should enclose the dynamic values in backticks and escape them yourself. +When using string concatenation, enclose the dynamic values in backticks and escape them yourself to protect against link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Cypher injections]. Notice that Cypher processes Unicode, so take care of the Unicode literal `\u0060` as well. -.Manually escaping dynamic labels before concatenation +.Manually escaping dynamic property keys before concatenation [source, go] ---- -dangerousLabel := "Person\\u0060n" +dangerousKey := "name\\u0060n" // convert \u0060 to literal backtick and then escape backticks -// remember to import `strings` -escapedLabel := strings.ReplaceAll(dangerousLabel, "\\u0060", "`") -escapedLabel = strings.ReplaceAll(escapedLabel, "`", "``") +escapedKey := strings.ReplaceAll(dangerousKey, "\\u0060", "`") +escapedKey = strings.ReplaceAll(escapedKey, "`", "``") result, err := neo4j.ExecuteQuery(ctx, driver, - "MATCH (p:`" + escapedLabel + "` WHERE p.name = $name) RETURN p.name", + "MATCH (p:Person {`" + escapedKey + "`: $name}) RETURN p.name", map[string]any{ "name": "Alice", }, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("")) + + +// rewritten to +// MATCH (p:Person {`name```: $name}) RETURN p.name ---- -Another workaround, which avoids string concatenation, is using the <> procedure link:{neo4j-docs-base-uri}/apoc/5/overview/apoc.merge/apoc.merge.node/[`apoc.merge.node`]. -It supports dynamic labels and property keys, but only for node merging. +Another workaround to avoid string concatenation is to use <> procedures, such as link:{neo4j-docs-base-uri}/apoc/current/overview/apoc.merge/apoc.merge.node/[`apoc.merge.node`], which supports dynamic labels and property keys. .Using `apoc.merge.node` to create a node with dynamic labels/property keys [source, go] @@ -143,13 +141,14 @@ To enable driver logging, use the `Config.Log` option when instantiating the dri [source, go] ---- -// import "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" +// import "github.com/neo4j/neo4j-go-driver/v6/neo4j/config" +// import "github.com/neo4j/neo4j-go-driver/v6/neo4j/log" -driver, err := neo4j.NewDriverWithContext( +driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, ""), func(conf *config.Config) { - conf.Log = neo4j.ConsoleLogger(neo4j.DEBUG) + conf.Log = log.ToConsole(log.DEBUG) }) ---- @@ -188,7 +187,7 @@ This applies to individual queries run using xref:query-simple.adoc[`ExecuteQuer result, err := neo4j.ExecuteQuery(ctx, driver, "RETURN 42 AS n", nil, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase(""), - neo4j.ExecuteQueryWithBoltLogger(neo4j.ConsoleBoltLogger())) + neo4j.ExecuteQueryWithBoltLogger(log.BoltToConsole())) ---- [source, go] @@ -196,7 +195,7 @@ result, err := neo4j.ExecuteQuery(ctx, driver, ---- session := driver.NewSession(ctx, neo4j.SessionConfig{ DatabaseName: "", - BoltLogger: neo4j.ConsoleBoltLogger(), + BoltLogger: log.BoltToConsole(), }) defer session.Close(ctx) session.Run(ctx, "RETURN 42 AS n", nil) diff --git a/go-manual/modules/ROOT/pages/query-simple.adoc b/go-manual/modules/ROOT/pages/query-simple.adoc index 5551c219..da64a79c 100644 --- a/go-manual/modules/ROOT/pages/query-simple.adoc +++ b/go-manual/modules/ROOT/pages/query-simple.adoc @@ -1,10 +1,6 @@ = Query the database -Once you have xref:connect.adoc[connected to the database], you can run queries using <> and the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExecuteQuery[`ExecuteQuery()`]. - -[TIP] -`ExecuteQuery()` was introduced with the version 5.8 of the driver. + -For queries with earlier versions, use xref:transactions.adoc[sessions and transactions]. +Once you have xref:connect.adoc[connected to the database], you can run queries using <> and the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ExecuteQuery[`ExecuteQuery()`]. == Write to the database @@ -157,7 +153,7 @@ fmt.Println("Query updated the database?", == Query parameters *Do not hardcode or concatenate parameters directly into queries*. -Instead, always use placeholders and specify the link:{neo4j-docs-base-uri}/cypher-manual/current/syntax/parameters/[Cypher parameters], as shown in the previous examples. +Instead, always use placeholders and provide dynamic data as link:{neo4j-docs-base-uri}/cypher-manual/current/syntax/parameters/[Cypher parameters], as shown in the previous examples. This is for: 1. *performance benefits*: Neo4j compiles and caches queries, but can only do so if the query structure is unchanged; @@ -189,15 +185,15 @@ For those rare use cases, see xref:query-advanced#_dynamic_values_in_property_ke A query run may fail for a number of reasons. When using `ExecuteQuery()`, the driver automatically retries to run a failed query if the failure is deemed to be transient (for example due to temporary server unavailability). -An error will be raised if the operation keeps failing after the configured link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/config#Config.MaxTransactionRetryTime[maximum retry time]. +An error is raised if the operation keeps failing after the configured link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#Config.MaxTransactionRetryTime[maximum retry time]. -All link:https://neo4j.com/docs/status-codes/current/errors/all-errors/[errors coming from the server] are of type link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#Neo4jError[`Neo4jError`]. -You can use an error's code to stably identify a specific error; error messages are instead not stable markers, and should not be relied upon. +All link:https://neo4j.com/docs/status-codes/current/errors/all-errors/[errors coming from the server] are of type link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Neo4jError[`Neo4jError`]. +You can use an exception's code to stably identify a specific error; error messages are instead not stable markers, and should not be relied upon. .Basic error handling [source, go] ---- -res, err := neo4j.ExecuteQuery(ctx, driver, "MATCH (p:Person) RETURN", nil, +result, err := neo4j.ExecuteQuery(ctx, driver, "MATCH (p:Person) RETURN", nil, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("")) if err != nil { @@ -218,16 +214,96 @@ Error message: Invalid input '': expected an expression, '*', 'ALL' or 'DISTINCT */ ---- +Exception objects also expose errors as GQL-status objects. +The main difference between link:https://neo4j.com/docs/status-codes/current/errors/all-errors/[Neo4j error codes] and link:https://neo4j.com/docs/status-codes/current/errors/gql-errors/[GQL error codes] is that the GQL ones are more granular: a single Neo4j error code might be broken in several, more specific GQL error codes. + +The actual _cause_ that triggered an exception is sometimes found in the optional GQL-status object `.GqlCause`, which is itself a `Neo4jError`. +You might need to recursively traverse the cause chain before reaching the root cause of the exception you caught. +In the example below, the exception's GQL status code is `42001`, but the actual source of the error has status code `42I06`. + +.Usage of `Neo4jError` with GQL-related methods +[source, go] +---- +result, err := neo4j.ExecuteQuery(ctx, driver, "MATCH (p:Person) RETURN", nil, + neo4j.EagerResultTransformer, + neo4j.ExecuteQueryWithDatabase("")) +if err != nil { + var neo4jErr *neo4j.Neo4jError + if errors.As(err, &neo4jErr) { + // Error is of type Neo4jError + fmt.Println("Error GQL status code:", neo4jErr.GqlStatus) + fmt.Println("Error GQL status description:", neo4jErr.GqlStatusDescription) + fmt.Println("Error GQL classification:", neo4jErr.GqlClassification) + if neo4jErr.GqlDiagnosticRecord != nil { + fmt.Printf("Error GQL diagnostic record: %+v\n", neo4jErr.GqlDiagnosticRecord) + } + if neo4jErr.GqlCause != nil { + fmt.Println("Error GQL cause:", neo4jErr.GqlCause.Error()) + } + } else { + fmt.Println("Non-Neo4j error:", err.Error()) + } +} +/* +Error GQL status code: 42001 +Error GQL status description: error: syntax error or access rule violation - invalid syntax +Error GQL classification: CLIENT_ERROR +Error GQL diagnostic record: map[CURRENT_SCHEMA:/ OPERATION: OPERATION_CODE:0 _classification:CLIENT_ERROR _position:map[column:24 line:1 offset:23]] +Error GQL cause: Neo4jError: Neo.DatabaseError.General.UnknownError (42I06: Invalid input '', expected: an expression, '*', 'ALL' or 'DISTINCT'.) +*/ +---- + +GQL status codes are particularly helpful when you want your application to behave differently depending on the exact error that was raised by the server. + +.Distinguishing between different error codes +[source, go] +---- +result, err := neo4j.ExecuteQuery(ctx, driver, "MATCH (p:Person) RETURN", nil, + neo4j.EagerResultTransformer, + neo4j.ExecuteQueryWithDatabase("")) +if err != nil { + var neo4jErr *neo4j.Neo4jError + if errors.As(err, &neo4jErr) { + if hasGqlStatus(neo4jErr, '42001') { + // Neo.ClientError.Statement.SyntaxError + // special handling of syntax error in query + fmt.Println(neo4jErr.Error()) + } else if hasGqlStatus(neo4jErr, '42NFF') { + // Neo.ClientError.Security.Forbidden + // special handling of user not having CREATE permissions + fmt.Println(neo4jErr.Error()) + } else { + // handling of all other exceptions + fmt.Println(neo4jErr.Error()) + } + } +} +---- + +[NOTE] +==== +The GQL status code `50N42` is returned when an error does not have a GQL-status object. +This can happen if the driver is connected to an older Neo4j server. +Don't rely on this status code, as future Neo4j server versions might change it with a more appropriate one. +==== + +[TIP] +==== +Transient server errors can be retried without need to alter the original request. +You can discover whether an error is transient via the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#IsRetryable[`neo4j.IsRetryable(error)`], which gives insights into whether a further attempt might be successful. +This is particular useful when running queries in xref:transactions#explicit-transactions[explicit transactions], to know if a failed query is worth re-running. +==== + == Query configuration -You can supply further configuration parameters to alter the default behavior of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExecuteQuery[`ExecuteQuery()`]. +You can supply further configuration parameters to alter the default behavior of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ExecuteQuery[`ExecuteQuery()`]. These are provided as an arbitrary number of callbacks from the 4th function argument onward. === Database selection -It is recommended to *always specify the database explicitly* with the `neo4j.ExecuteQueryWithDatabase("")` callback, even on single-database instances. +*Always specify the database explicitly* with the `neo4j.ExecuteQueryWithDatabase("")` callback, even on single-database instances. This allows the driver to work more efficiently, as it saves a network round-trip to the server to resolve the home database. If no database is given, the link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/#manage-databases-default[user's home database] is used. @@ -242,8 +318,8 @@ neo4j.ExecuteQuery(ctx, driver, [TIP] Specifying the database through the configuration method is preferred over the link:{neo4j-docs-base-uri}/cypher-manual/current/clauses/use/[`USE`] Cypher clause. -If the server runs on a cluster, queries with `USE` require server-side routing to be enabled. -Queries may also take longer to execute as they may not reach the right cluster member at the first attempt, and need to be routed to one containing the requested database. +If the server runs on a cluster, queries with `USE` require link:https://neo4j.com/docs/operations-manual/current/clustering/setup/routing/#clustering-routing[server-side routing] to be enabled. +Queries can also take longer to execute as they may not reach the right cluster member at the first attempt, and need to be routed to one containing the requested database. === Request routing @@ -263,19 +339,17 @@ neo4j.ExecuteQuery(ctx, driver, [NOTE] ==== -Although executing a _write_ query in read mode likely results in a runtime error, *you should not rely on this for access control.* The difference between the two modes is that _read_ transactions will be routed to any node of a cluster, whereas _write_ ones are directed to primaries. -In other words, there is no guarantee that a write query submitted in read mode will be rejected. +Although executing a _write_ query in read mode results in a runtime error, *you should not rely on this for access control.* The difference between the two modes is that _read_ transactions will be routed to any node of a cluster, whereas _write_ ones are directed to primaries. +There is no security guarantee that a write query submitted in read mode will be rejected. ==== [#impersonation] -[role=label--new-5.14] === Run queries as a different user You can execute a query through a different user with the configuration callback `neo4j.ExecuteQueryWithAuthToken()`. -Switching user at the query level is cheaper than creating a new `DriverWithContext` object. +Switching user at the query level is cheaper than creating a new `Driver` object. The query is then run within the security context of the given user (i.e., home database, permissions, etc.). + -Query-scoped authentication requires a server version >= 5.8. [source, go] ---- @@ -288,7 +362,7 @@ neo4j.ExecuteQuery(ctx, driver, neo4j.ExecuteQueryWithAuthToken(queryAuth)) ---- -The callback `neo4j.ExecuteQueryWithImpersonatedUser()` provides a similar functionality, and is available in driver/server versions >= 4.4. +The callback `neo4j.ExecuteQueryWithImpersonatedUser()` provides a similar functionality. The difference is that you don't need to know a user's password to impersonate them, but the user under which the `Driver` was created needs to have the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-impersonation[appropriate permissions]. [source, go] @@ -311,7 +385,7 @@ package main import ( "fmt" "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -321,7 +395,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -401,7 +475,7 @@ func main() { } ---- -For more information see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExecuteQuery[API documentation -> ExecuteQuery()]. +For more information see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ExecuteQuery[API documentation -> ExecuteQuery()]. ifndef::backend-pdf[] diff --git a/go-manual/modules/ROOT/pages/result-summary.adoc b/go-manual/modules/ROOT/pages/result-summary.adoc index 9d46479d..32b0fc0d 100644 --- a/go-manual/modules/ROOT/pages/result-summary.adoc +++ b/go-manual/modules/ROOT/pages/result-summary.adoc @@ -1,7 +1,7 @@ = Explore the query execution summary -After all results coming from a query have been processed, the server ends the transaction by returning a summary of execution. -It comes as a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ResultSummary[`ResultSummary`] object, and it contains information among which: +After all records of a query result have been processed, the server ends the transaction by returning a summary of execution. +It comes as a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ResultSummary[`ResultSummary`] object, and it contains information among which: - xref:_query_counters[**Query counters**] -- What changes the query triggered on the server - xref:_query_execution_plan[**Query execution plan**] -- How the database would execute (or executed) the query @@ -25,8 +25,7 @@ summary := result.Summary ---- If you are using xref:transactions.adoc[transaction functions], you can retrieve the query execution summary with the method `Result.Consume()`. -**Notice that once you ask for the execution summary, the result stream is exhausted**. -This means that any record which has not yet been processed is discarded. +**Notice that once you ask for the execution summary, the result stream is __exhausted__**: any record yet to be processed is not available anymore. [source, go] ---- @@ -46,7 +45,7 @@ summary, _ := session.ExecuteWrite(ctx, == Query counters -The method `ResultSummary.Counters()` returns counters for the operations that a query triggered (as a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#Counters[`Counters`] object). +The method `ResultSummary.Counters()` returns counters for the operations that a query triggered (as a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Counters[`Counters`] object). .Insert some data and display the query counters [source, go] @@ -72,19 +71,19 @@ fmt.Println("Relationships created:", counters.RelationshipsCreated()) // Relationships created: 1 ---- -There are two additional boolean methods which act as meta-counters: +Two additional boolean methods act as meta-counters: - `.ContainsUpdates()` -- whether the query triggered any write operation on the database on which it ran -- `.ContainsSystemUpdates()` -- whether the query updated the `system` database +- `.ContainsSystemUpdates()` -- whether the query triggered any write operation on the `system` database == Query execution plan -If you prefix a query with `EXPLAIN`, the server will return the plan it _would_ use to run the query, but will not actually run it. +If you prefix a query with `EXPLAIN`, the server returns the plan it _would_ use to run the query, but does not actually run it. You can retrieve the plan by calling `ResultSummary.Plan()`, which contains the list of link:https://neo4j.com/docs/cypher-manual/current/planning-and-tuning/operators/[Cypher operators] that would be used to retrieve the result set. -You may use this information to locate potential bottlenecks or room for performance improvements (for example through the creation of indexes). +Use this information to locate potential bottlenecks or room for performance improvements (for example through the creation of indexes). -[source, go, role=nocollapse] +[source, go] ---- result, _ := neo4j.ExecuteQuery(ctx, driver, "EXPLAIN MATCH (p {name: $name}) RETURN p", @@ -114,12 +113,12 @@ Total database accesses: ? */ ---- -If you instead prefix a query with the keyword `PROFILE`, the server will return the execution plan it has used to run the query, together with profiler statistics. +If you instead prefix a query with the keyword `PROFILE`, the server returns the execution plan it has used to run the query, together with profiler statistics. This includes the list of operators that were used and additional profiling information about each intermediate step. You can access the plan by calling `ResultSummary.Profile()`. Notice that the query is also _run_, so the result object also contains any result records. -[source, go, role=nocollapse] +[source, go] ---- result, _ := neo4j.ExecuteQuery(ctx, driver, "PROFILE MATCH (p {name: $name}) RETURN p", @@ -154,57 +153,22 @@ For more information and examples, see link:{neo4j-docs-base-uri}/cypher-manual/ == Notifications -After executing a query, the server can return link:{neo4j-docs-base-uri}/status-codes/5/notifications/[notifications] alongside the query result. +After executing a query, the server can return link:{neo4j-docs-base-uri}/status-codes/current/notifications/[notifications] alongside the query result. Notifications contain recommendations for performance improvements, warnings about the usage of deprecated features, and other hints about sub-optimal usage of Neo4j. -[TIP] -For driver version >= 5.25 and server version >= 5.23, two forms of notifications are available (_Neo4j status codes_ and _GQL status codes_). -For earlier versions, only _Neo4j status codes_ are available. + -GQL status codes are planned to supersede Neo4j status codes. - .An unbounded shortest path raises a performance notification [.tabbed-example] ===== -[.include-with-neo4j-status-code] -====== -The method `Summary.Notifications()` returns a list of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#Notification[`Notification`] objects. - -[source, go, role=nocollapse] ----- -result, _ := neo4j.ExecuteQuery(ctx, driver, ` - MATCH p=shortestPath((:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'})) - RETURN p - `, nil, - neo4j.EagerResultTransformer, - neo4j.ExecuteQueryWithDatabase("")) - -for _, notification := range result.Summary.Notifications() { - fmt.Println("Code:", notification.Code()) - fmt.Println("Title:", notification.Title()) - fmt.Println("Description:", notification.Description()) - fmt.Println("Severity:", notification.SeverityLevel()) - fmt.Println("Category:", notification.Category(), "\n") -} -/* -Code: Neo.ClientNotification.Statement.UnboundedVariableLengthPattern -Title: The provided pattern is unbounded, consider adding an upper limit to the number of node hops. -Description: Using shortest path with an unbounded pattern will likely result in long execution times. It is recommended to use an upper limit to the number of node hops in your pattern. -Severity: INFORMATION -Category: PERFORMANCE -*/ ----- - -====== [.include-with-GQL-status-code] ====== -With version >= 5.25, the method `Summary.GqlStatusObjects()` returns a list of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#GqlStatusObject[`GqlStatusObject`]s. +The method `Summary.GqlStatusObjects()` returns a list of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#GqlStatusObject[`GqlStatusObject`]s. These are GQL-compliant status objects. Some (but not all) `GqlStatusObjects` are notifications, whereas some report an _outcome_ status: `00000` for "success", `02000` for "no data", and `00001` for "omitted result". `Summary.GqlStatusObjects()` always contains at least one entry, containing the outcome status. -[source, go, role=nocollapse] +[source, go] ---- result, _ := neo4j.ExecuteQuery(ctx, driver, ` MATCH p=shortestPath((:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'})) @@ -253,14 +217,47 @@ Diagnostic record: map[CURRENT_SCHEMA:/ OPERATION: OPERATION_CODE:0 _classificat ---- ====== + +[.include-with-neo4j-status-code] +====== +label:deprecated[Deprecated in 6.0] + +The method `Summary.Notifications()` returns a list of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Notification[`Notification`] objects. + +[source, go] +---- +result, _ := neo4j.ExecuteQuery(ctx, driver, ` + MATCH p=shortestPath((:Person {name: 'Alice'})-[*]->(:Person {name: 'Bob'})) + RETURN p + `, nil, + neo4j.EagerResultTransformer, + neo4j.ExecuteQueryWithDatabase("")) + +for _, notification := range result.Summary.Notifications() { + fmt.Println("Code:", notification.Code()) + fmt.Println("Title:", notification.Title()) + fmt.Println("Description:", notification.Description()) + fmt.Println("Severity:", notification.SeverityLevel()) + fmt.Println("Category:", notification.Category(), "\n") +} +/* +Code: Neo.ClientNotification.Statement.UnboundedVariableLengthPattern +Title: The provided pattern is unbounded, consider adding an upper limit to the number of node hops. +Description: Using shortest path with an unbounded pattern will likely result in long execution times. It is recommended to use an upper limit to the number of node hops in your pattern. +Severity: INFORMATION +Category: PERFORMANCE +*/ +---- + +====== + ===== -[role=label--new-5.7] === Filter notifications By default, the server analyses each query for all categories and severity of notifications. -Starting from version 5.7, you can use the parameters `NotificationsMinSeverity` and/or `NotificationsDisabledCategories`/`NotificationsDisabledClassifications` to restrict the severity and/or category/classification of notifications that you are interested into. +Use the parameters `NotificationsMinSeverity` and/or `NotificationsDisabledCategories`/`NotificationsDisabledClassifications` to restrict the severity and/or category/classification of notifications that you are interested into. There is a slight performance gain in restricting the amount of notifications the server is allowed to raise. The severity filter applies to both Neo4j and GQL notifications. @@ -273,12 +270,12 @@ You can disable notifications altogether by setting the minimum severity to `"OF [source, go] ---- // import ( -// "github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications" -// "github.com/neo4j/neo4j-go-driver/v5/neo4j/config" +// "github.com/neo4j/neo4j-go-driver/v6/neo4j/notifications" +// "github.com/neo4j/neo4j-go-driver/v6/neo4j/config" // ) // At driver level -driverNot, _ := neo4j.NewDriverWithContext( +driverNot, _ := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, ""), func (conf *config.Config) { diff --git a/go-manual/modules/ROOT/pages/transactions.adoc b/go-manual/modules/ROOT/pages/transactions.adoc index ee05639d..da65548b 100644 --- a/go-manual/modules/ROOT/pages/transactions.adoc +++ b/go-manual/modules/ROOT/pages/transactions.adoc @@ -2,16 +2,16 @@ When xref:query-simple.adoc[querying the database with `ExecuteQuery()`], the driver automatically creates a _transaction_. A transaction is a unit of work that is either _committed_ in its entirety or _rolled back_ on failure. You can include multiple Cypher statements in a single query, as for example when using `MATCH` and `CREATE` in sequence to xref:query-simple#_update_the_database[update the database], but you cannot have multiple queries and interleave some client-logic in between them. -For these more advanced use-cases, the driver provides functions to take full control over the transaction lifecycle. -These are called _managed transactions_, and you can think of them as a way of unwrapping the flow of `executableQuery()` and being able to specify its desired behavior in more places. +For these more advanced use-cases, the driver provides functions to manually control transactions. +The most common form is _managed transactions_, and you can think of them as a way of unwrapping the flow of `ExecutableQuery()` and being able to specify its desired behavior in more places. == Create a session Before running a transaction, you need to obtain a _session_. -Sessions act as concrete query channels between the driver and the server, and ensure <> is enforced. +Sessions act as query channels between the driver and the server, and ensure <> is enforced. -Sessions are created with the method `DriverWithContext.NewSession()`. +Sessions are created with the method `Driver.NewSession()`. Use the second argument to alter the session's configuration, among which for example the xref:_database_selection[target database]. For further configuration parameters, see xref:_session_configuration[Session configuration]. @@ -21,20 +21,20 @@ session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: "> compliant, *queries within a transaction will either be executed as a whole or not at all*: you cannot get a part of the transaction succeeding and another failing. Use transactions to group together related queries which work together to achieve a single _logical_ database operation. -A managed transaction is created with the methods link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SessionWithContext[`SessionWithContext.ExecuteRead()`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SessionWithContext[`SessionWithContext.ExecuteWrite()`], depending on whether you want to retrieve data from the database or alter it. +You create a managed transaction with the methods link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Session[`Session.ExecuteRead()`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Session[`Session.ExecuteWrite()`], depending on whether you want to retrieve data from the database or alter it. Both methods take a <> callback, which is responsible for actually carrying out the queries and processing the result. .Retrieve people whose name starts with `Al` @@ -67,8 +67,8 @@ for _, person := range people.([]*neo4j.Record) { <1> Create a session. A single session can be the container for multiple queries. Remember to close it when done (here we `defer` its closure just after opening). <2> The `.ExecuteRead()` (or `.ExecuteWrite()`) method is the entry point into a transaction. <3> The transaction function callback is responsible of running queries. -<4> Use the method link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ManagedTransaction[`ManagedTransaction.Run()`] to run queries. Each query run returns a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ResultWithContext[`ResultWithContext`] object. -<5> xref:process-result[Process the result] using any of the methods on `ResultWithContext`. The method `.Collect()` retrieves all records into a list. +<4> Use the method link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ManagedTransaction[`ManagedTransaction.Run()`] to run queries. Each query run returns a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Result[`Result`] object. +<5> xref:process-result[Process the result] using any of the methods on `Result`. The method `.Collect()` retrieves all records into a list. *Do not hardcode or concatenate parameters directly into the query*. Use xref:query-simple#query-parameters[query parameters] instead, both for performance and security reasons. @@ -77,9 +77,6 @@ Use xref:query-simple#query-parameters[query parameters] instead, both for perfo Instead, always xref:process-result[process the result] in some way. Within a transaction function, a `return` statement where `error` is `nil` results in the transaction being committed, while the transaction is automatically rolled back if the returned `error` value is not `nil`. -[CAUTION] -The methods `.ExecuteRead()` and `.ExecuteWrite()` have replaced `.ReadTransaction()` and `.WriteTransaction()`, which are deprecated in version 5.x and will be removed in version 6.0. - .A transaction with multiple queries, client logic, and potential roll backs [source, go] ---- @@ -90,7 +87,7 @@ import ( "context" "strconv" "errors" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -101,7 +98,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -234,10 +231,11 @@ func addPersonToOrganization(ctx context.Context, tx neo4j.ManagedTransaction, p } ---- -Should a transaction fail for a reason that the driver deems transient, it automatically retries to run the transaction function (with an exponentially increasing delay). -For this reason, *transaction functions must be _idempotent_* (i.e., they should produce the same effect when run several times), because you do not know upfront how many times they are going to be executed. -In practice, this means that you should not edit nor rely on globals, for example. -Note that although transactions functions might be executed multiple times, the queries inside it will always run only once. +The driver automatically retries to run a failed transaction if the failure is deemed to be transient (for example due to temporary server unavailability). +An exception will be raised if the operation keeps failing after the configured link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#Config.MaxTransactionRetryTime[maximum retry time]. + +Because a transaction might be re-run, *transaction functions should produce the same effect when run several times (_idempotent_)*, because you do not know upfront how many times they are going to be executed. +Note that although transactions functions might be executed multiple times, the database queries inside it will always run only once. A session can chain multiple transactions, but *only one single transaction can be active within a session at any given time*. To maintain multiple concurrent transactions, use multiple concurrent sessions. @@ -246,8 +244,8 @@ To maintain multiple concurrent transactions, use multiple concurrent sessions. [#explicit-transactions] == Run an explicit transaction -You can achieve full control over transactions by manually beginning one with the method link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SessionWithContext[`SessionWithContext.BeginTransaction()`]. -You run queries inside an explicit transaction with the method link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExplicitTransaction[`ExplicitTransaction.Run()`]. +You can achieve full control over transactions by manually beginning one with the method link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Session[`Session.BeginTransaction()`]. +You run queries inside an explicit transaction with the method link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ExplicitTransaction[`ExplicitTransaction.Run()`]. [source, go] ---- @@ -262,15 +260,15 @@ if err != nil { // tx.Rollback() to rollback the transaction ---- +An explicit transaction can be committed with link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ExplicitTransaction[`ExplicitTransaction.Commit()`] or rolled back with link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ExplicitTransaction[`ExplicitTransaction.Rollback()`]. +If no explicit action is taken, the driver automatically rolls back the transaction at the end of its lifetime. + [TIP] ==== Queries run with `tx.Run()` failing due to a transient server error can be retried without need to alter the original request. -You can discover whether an error is transient via the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#IsRetryable[`neo4j.IsRetryable(error)`], which gives insights into whether a further attempt might be successful. +You can discover whether an error is transient via the function link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#IsRetryable[`neo4j.IsRetryable(error)`], which gives insights into whether a further attempt might be successful. ==== -An explicit transaction can be committed with link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExplicitTransaction[`ExplicitTransaction.Commit()`] or rolled back with link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExplicitTransaction[`ExplicitTransaction.Rollback()`]. -If no explicit action is taken, the driver automatically rolls back the transaction at the end of its lifetime. - Explicit transactions are most useful for applications that need to distribute Cypher execution across multiple functions for the same transaction, or for applications that need to run multiple queries within a single transaction but without the automatic retries provided by managed transactions. .A sketch of an explicit transaction interacting with external APIs @@ -281,7 +279,7 @@ package main import ( "fmt" "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -291,7 +289,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) if err != nil { @@ -310,7 +308,7 @@ func main() { transferToOtherBank(ctx, driver, customerId, otherBankId, 999) } -func createCustomer(ctx context.Context, driver neo4j.DriverWithContext) (string, error) { +func createCustomer(ctx context.Context, driver neo4j.Driver) (string, error) { result, err := neo4j.ExecuteQuery(ctx, driver, ` MERGE (c:Customer {id: randomUUID()}) RETURN c.id AS id @@ -324,7 +322,7 @@ func createCustomer(ctx context.Context, driver neo4j.DriverWithContext) (string return customerId.(string), err } -func transferToOtherBank(ctx context.Context, driver neo4j.DriverWithContext, customerId string, otherBankId int, amount float32) { +func transferToOtherBank(ctx context.Context, driver neo4j.Driver, customerId string, otherBankId int, amount float32) { session := driver.NewSession(ctx, neo4j.SessionConfig{DatabaseName: ""}) defer session.Close(ctx) tx, err := session.BeginTransaction(ctx) @@ -396,7 +394,7 @@ func requestInspection(ctx context.Context, customerId string, otherBankId int, [[process-result]] == Process query results -The driver's output of a query is a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ResultWithContext[`ResultWithContext`] object, which does not directly contain the result records. +The driver's output of a query is a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Result[`Result`] object, which does not directly contain the result records. Rather, it encapsulates the Cypher result in a rich data structure that requires some parsing on the client side. There are two main points to be aware of: @@ -420,11 +418,11 @@ The animation below follows the path of a single query: it shows how the driver type="video/mp4"> ++++ -**The easiest way of processing a result is by calling `.Collect(ctx)` on it**, which yields an array of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/db#Record[`Record`] objects. -Otherwise, a `ResultWithContext` object implements a number of methods for processing records. +**The easiest way of processing a result is by calling `.Collect(ctx)` on it**, which yields an array of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/db#Record[`Record`] objects. +Otherwise, a `Result` object implements a number of methods for processing records. The most commonly needed ones are listed below. -// .An essential list of `ResultWithContext` methods +// .An essential list of `Result` methods [cols="3,4"] |=== |Name |Description @@ -450,14 +448,14 @@ In that case, it also advances the result iterator. It exhausts the result, so should only be called when data processing is over. |=== -For a complete list of `ResultWithContext` methods, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ResultWithContext[API documentation -> ResultWithContext]. +For a complete list of `Result` methods, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Result[API documentation -> Result]. == Session configuration === Database selection -It is recommended to *always specify the database explicitly* with the configuration parameter `DatabaseName` upon session creation, even on single-database instances. +*Always specify the database explicitly* with the configuration parameter `DatabaseName` upon session creation, even on single-database instances. This allows the driver to work more efficiently, as it saves a network round-trip to the server to resolve the home database. If no database is given, the link:{neo4j-docs-base-uri}/operations-manual/current/database-administration/#manage-databases-default[default database] set in the Neo4j instance settings is used. @@ -470,8 +468,8 @@ session := driver.NewSession(ctx, neo4j.SessionConfig{ [TIP] Specifying the database through the configuration method is preferred over the link:{neo4j-docs-base-uri}/cypher-manual/current/clauses/use/[`USE`] Cypher clause. -If the server runs on a cluster, queries with `USE` require server-side routing to be enabled. -Queries may also take longer to execute as they may not reach the right cluster member at the first attempt, and need to be routed to one containing the requested database. +If the server runs on a cluster, queries with `USE` require link:https://neo4j.com/docs/operations-manual/current/clustering/setup/routing/#clustering-routing[server-side routing] to be enabled. +Queries can also take longer to execute as they may not reach the right cluster member at the first attempt, and need to be routed to one containing the requested database. === Request routing @@ -492,21 +490,19 @@ session := driver.NewSession(ctx, neo4j.SessionConfig{ // .Routing ≠ Access control [NOTE] ==== -Although executing a _write_ query in read mode likely results in a runtime error, *you should not rely on this for access control.* The difference between the two modes is that _read_ transactions will be routed to any node of a cluster, whereas _write_ ones are directed to primaries. -In other words, there is no guarantee that a write query submitted in read mode will be rejected. +Although executing a _write_ query in read mode results in a runtime error, *you should not rely on this for access control.* The difference between the two modes is that _read_ transactions will be routed to any node of a cluster, whereas _write_ ones are directed to primaries. +There is no security guarantee that a write query submitted in read mode will be rejected. Similar remarks hold for the `.ExecuteRead()` and `.ExecuteWrite()` methods. ==== [#impersonation] -[role=label--new-5.14] === Run queries as a different user You can execute a query through a different user with the configuration option `Auth`. -Switching user at the session level is cheaper than creating a new `DriverWithContext` object. +Switching user at the session level is cheaper than creating a new `Driver` object. Queries are then run within the security context of the given user (i.e., home database, permissions, etc.). + -Session-scoped authentication requires a server version >= 5.8. [source, go] ---- @@ -517,8 +513,7 @@ session := driver.NewSession(ctx, neo4j.SessionConfig{ }) ---- -The option `ImpersonatedUser` provides a similar functionality, and is available in driver/server versions >= 4.4. -The difference is that you don't need to know a user's password to impersonate them, but the user under which the `Driver` was created needs to have the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-impersonation[appropriate permissions]. +The option `ImpersonatedUser` provides a similar functionality: the difference is that you don't need to know a user's password to impersonate them, but the user under which the `Driver` was created needs to have the link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-impersonation[appropriate permissions]. [source, go] ---- diff --git a/go-manual/modules/ROOT/pages/upgrade.adoc b/go-manual/modules/ROOT/pages/upgrade.adoc index e99ba1b3..ead9ef22 100644 --- a/go-manual/modules/ROOT/pages/upgrade.adoc +++ b/go-manual/modules/ROOT/pages/upgrade.adoc @@ -1,9 +1,10 @@ -:driver-changelog-url: https://github.com/neo4j/neo4j-go-driver/wiki/5.x-changelog +:driver-changelog-url: https://github.com/neo4j/neo4j-go-driver/wiki/6.x-changelog = Upgrade from older versions include::{common-partial}/upgrade.adoc[tag=intro] +//// [[recommended-steps]] == Recommended steps @@ -48,6 +49,8 @@ You can add any spurious entry to the ignored list, so that the assistant won't go get github.com/neo4j/neo4j-go-driver/v5 ---- +//// + [[new-features]] == New features @@ -56,89 +59,19 @@ go get github.com/neo4j/neo4j-go-driver/v5 |=== | -.Support for Go up to 1.23 -[%collapsible] -==== -The driver is compatible with any Go version starting from 1.18 up to 1.23. -==== -| -.`neo4j.ExecuteQuery()` to run transactions with less knowledge of driver's internals -[%collapsible] -==== -The new function is a wrapper for `SessionWithContext.ExecuteRead/Write()`, but it abstracts away the result processing part and returns a list of records to the caller directly. - -See xref:query-simple.adoc[]. -==== - -| -.Support for user-provided `Context` instances -[%collapsible] -==== -A long requested feature from the community: the driver now exposes a set of APIs that take user-provided instances of `context.Context` into account. -The entry point is `neo4j.NewDriverWithContext()`. -==== -| -.Improved support for generics -[%collapsible] -==== -Functions link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#GetRecordValue[`neo4j.GetRecordValue()`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#GetProperty[`neo4j.GetProperty()`] allow to extract properties and record values of the specified type. - -Functions link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExecuteRead[`neo4j.ExecuteRead()`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExecuteWrite[`neo4j.ExecuteWrite()`] provide generic variants of `SessionWithContext.ExecuteRead/Write()`. -==== - - -| -.Re-authentication +.New type `Vector` [%collapsible] ==== -Allows for xref:connect-advanced.adoc#rotating-tokens[rotating authentication tokens] as well as xref:transactions.adoc#impersonation[session-scoped] and xref:query-simple.adoc#impersonation[query-scoped] authentication. +The type xref:data-types.adoc#vector[`Vector`] allows for storing and retrieving Cypher `VECTOR` objects to/from the database. +The `VECTOR` type is suitable for efficiently storing lists of homogeneous numbers, such as embeddings. ==== | -.Mutual TLS (mTLS) as second authentication factor (2FA) +.GQL status objects in exceptions [%collapsible] ==== -Allows for configuring client side TLS certificates to authenticate against the server. - -See xref:connect-advanced.adoc#mtls[Mutual TLS]. -==== - -| -.`BookmarkManager` support -[%collapsible] -==== -Bookmark managers make it easier to achieve causal chaining of sessions. - -See xref:bookmarks.adoc[]. -==== -| -.Notification filtering API -[%collapsible] -==== -Filtering allows to receive only a subset of notifications from the server, and to improve performance server-side. - -See xref:result-summary.adoc#_filter_notifications[Explore the query execution summary -> Filter notifications]. -==== - -| -.GQL statuses of queries in the `ResultSummary` -[%collapsible] -==== -These are GQL-compliant status objects. -A new method `ResultSummary.GqlStatusObject()` is available. -It returns a list of `GqlStatusObject`, containing information about the execution of the query. - -This API is planned to supersede the current notifications API. - -See xref:result-summary.adoc#_notifications[Explore the query execution summary -> Notifications]. -==== -| -.Telemetry -[%collapsible] -==== -The driver sends anonymous API usage statistics to the server. -Use the driver configuration `TelemtryDisabled = true` to opt out. - -For more information, see link:https://github.com/neo4j/neo4j-go-driver/pull/533[Telemetry API]. +Exception objects also expose errors as GQL-status objects. +The main difference between Neo4j error codes and GQL error codes is that the latter are more granular. +For more information, see xref:query-simple.adoc#error-handling[Error handling]. ==== |=== @@ -147,195 +80,83 @@ For more information, see link:https://github.com/neo4j/neo4j-go-driver/pull/533 [[breaking-changes]] == Breaking changes and deprecations -The column `Assistant` denotes whether the Neo4j Drivers Migration Assistant can detect issues related to the given changelog entry. -Entries with a cross mark {cross-mark} require manual inspection. - -Deprecated features are likely to be removed in version 6. +Deprecated features are likely to be removed in the next major version. -[cols="1,5a,1,1"] +[cols="1,5a,1"] |=== -|Version |Message |Status |Assistant - -|5.0 -|Minimum supported Go version is 1.18. -|label:removed[Changed] -|{cross-mark} - -|5.0 -|link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExplicitTransaction[`ExplicitTransaction`] -- Calling `Rollback()` or `Commit()` multiple times is now prohibited and results in a `UsageError`. -|label:removed[Changed] -|{cross-mark} - -|5.0 -|Operations on closed sessions fail with error with `Operation attempted on a closed session`. -|label:removed[Changed] -|{cross-mark} - -|5.0 -|link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#Counters.ContainsUpdates[`ResultSummary.ContainUpdates()`] -- In 4.x, the function returned `true` if any of the summary counters were strictly positive. -It now returns true only if at least a deletion or creation of the following entities happened: constraint, index, node, relationship, label, property. -|label:removed[Changed] -|{cross-mark} +|Version |Message |Status -|5.0 -|A link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#WithTxTimeout[transaction timeout] of `0` (which is not the default value anymore) now disables the timeout on the server side. -If no timeout is specified, the server uses the server-defined timeout. +|6.0 +|Minimum supported Go version is 1.24. |label:removed[Changed] -|{check-mark} -|5.0 -|`Driver.Session()` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#DriverWithContext.NewSession[`DriverWithContext.NewSession()`]. +|6.0 +|`Session.WriteTransaction()` and `Session.ReadTransaction()` have been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Session.ExecuteWrite[`Session.ExecuteWrite()`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Session.ExecuteRead[`Session.ExecuteRead()`]. +The logic is the same, but the argument type changed from `Transaction` to `ManagedTransaction`, and explicit transaction handling methods (`.Commit()`, `.Rollback()`, `.Close()`) are not available on managed transactions anymore. |label:removed[] -|{check-mark} -|5.0 -|`Query.Params()` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#Query.Parameters[`Query.Parameters()`]. +|6.0 +|`neo4j.Transaction` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ExplicitTransaction[`neo4j.ExplicitTransaction`]. |label:removed[] -|{check-mark} -|5.0 -|`Record.GetByIndex()` has been removed. -Access the link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/db#Record[`Record.Values`] struct slice directly instead. +|6.0 +|`neo4j.TransactionWork` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#ManagedTransactionWork[`neo4j.ManagedTransactionWork`]. |label:removed[] -|{check-mark} -|5.0 -|`ResultSummary.Statement()` has been removed in favor of `ResultSummary.Query()`. -The new method returns a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ResultSummary.Query[`Query`] rather than a `Statement` object. +|6.0 +|`Config.RootCAs` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#Config.TlsConfig[`Config.TlsConfig`]. |label:removed[] -|{check-mark} -|5.0 -|`Session.LastBookmark()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#Session.LastBookmarks[`Session.LastBookmarks()`]. -The logic is similar, but the new method returns `neo4j.Bookmarks` instead of `string`. +|6.0 +|`neo4j.Config` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#Config[`config.Config`]. |label:removed[] -|{check-mark} -|5.0 -|Some of the previously exported types from `neo4j.db` are now internal. + -For `db.AccessMode`, `db.WriteMode` and `db.ReadMode`, use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#AccessMode[`neo4j.AccessMode`], link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#AccessModeWrite[`neo4j.AccessModeWrite`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#AccessModeRead[`neo4j.AccessModeRead`]. + -For `db.TxConfig`, use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#TransactionConfig[`neo4j.TransactionConfig`]. + -All others have no replacement: `db.TxHandle`, `db.StreamHandle`, `db.Command`, `db.Connection`, `db.RoutingTable`, `db.DefaultDatabase`, `db.DatabaseSelector`. -|label:removed[] -|{check-mark} +|6.0 +|`neo4j.ServerAddress` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#ServerAddress[`config.ServerAddress`]. -|5.0 -|`ServerInfo.Version` has been removed. -Use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ServerInfo[`ServerInfo.Agent`, `ServerInfo.ProtocolVersion`], or call the link:https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_dbms_components[`dbms.components`] Cypher procedure instead. +`neo4j.NewServerAddress()` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#NewServerAddress[`config.NewServerAddress()`]. |label:removed[] -|{check-mark} - -|5.0 -|`NewDriver()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#NewDriverWithContext[`NewDriverWithContext()`]. -The behavior is similar, except that the new function takes a `ctx.Context` as first argument. -|label:deprecated[] -|{check-mark} - -|5.0 -|`Session.WriteTransaction()` and `Session.ReadTransaction()` are deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SessionWithContext.ExecuteWrite[`Session.ExecuteWrite()`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SessionWithContext.ExecuteRead[`Session.ExecuteRead()`]. -The logic is the same, but the argument type changed from `Transaction` to `ManagedTransaction`, and explicit transaction handling methods (`.Commit()`, `.Rollback()`, `.Close()`) are not available on managed transactions anymore. -|label:deprecated[] -|{check-mark} - -|5.0 -|link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype#Node[`Node.Id`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/dbtype#Relationship[`Relationship.Id`] are deprecated in favor of `Node.ElementId` and `Relationship.ElementId`. -Old identifiers were Int64, whereas new ElementIds are String. -|label:deprecated[] -|{check-mark} -|5.0 -|`neo4j.SingleT()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SingleTWithContext[`neo4j.SingleTWithContext()`]. -The behavior is similar, except that the new function takes a `ctx.Context` as first argument. -|label:deprecated[] -|{check-mark} - -|5.0 -|`neo4j.Collect()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#CollectWithContext[`neo4j.CollectWithContext()`]. -The behavior is similar, except that the new function takes a `ctx.Context` as first argument. -|label:deprecated[] -|{check-mark} - -|5.0 -|`neo4j.CollectT()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#CollectTWithContext[`neo4j.CollectTWithContext()`]. -The behavior is similar, except that the new function takes a `ctx.Context` as first argument. -|label:deprecated[] -|{check-mark} +|6.0 +|`neo4j.ServerAddressResolver` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/config#ServerAddressResolver[`config.ServerAddressResolver`]. +|label:removed[] -|5.0 -|`neo4j.Session` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SessionWithContext[`neo4j.SessionWithContext`]. -|label:deprecated[] -|{check-mark} +|6.0 +|`neo4j.(LogLevel\|ERROR\|WARNING\|INFO\|DEBUG)` have been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/log#Level[`log.(Level\|ERROR\|WARNING\|INFO\|DEBUG)`]. +|label:removed[] -|5.0 -|`neo4j.Driver` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#DriverWithContext[`neo4j.DriverWithContext`]. -|label:deprecated[] -|{check-mark} +|6.0 +|*Logging* -|5.0 -|`neo4j.Transaction` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ExplicitTransaction[`neo4j.ExplicitTransaction`]. -|label:deprecated[] -|{check-mark} +- `neo4j.ConsoleLogger()` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/log#ToConsole[`neo4j.log.ToConsole()`]. The behavior is similar, except that the new function takes a `ctx.Context` as first argument. +- `neo4j.ConsoleBoltLogger()` has been removed in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/log#BoltToConsole[`neo4j.log.BoltToConsole()`]. +- Types `neo4j.log.Console` and `neo4j.log.Void` have been removed and unified into link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/log#Logger[`neo4j.log.Logger`]. +- Functions `neo4j.ConsoleLogger()` and `neo4j.Void()` have been removed in favor of https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/log#ToConsole[`neo4j.log.ToConsole()`] and https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/log#ToVoid[`neo4j.log.ToVoid()`], both returning a `neo4j.log.Logger` object. +|label:removed[] -|5.0 -|`neo4j.TransactionWork` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ManagedTransactionWork[`neo4j.ManagedTransactionWork`]. -|label:deprecated[] -|{check-mark} +|6.0 +|Notifications-related objects have been moved from the global namespace `neo4j` into link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/notifications[`neo4j/notifications`]. -|5.0 -|`neo4j.Result` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#ResultWithContext[`neo4j.ResultWithContext`]. -|label:deprecated[] -|{check-mark} +As a result, `neo4j.(NotificationSeverity\|Warning\|Information\|UnknownSeverity\|NotificationCategory\|Hint\|Unrecognized\|Unsupported\|Performance\|Deprecation\|Generic\|Security\|Topology\|UnknownCategory)` have been removed in favor of `notifications.(NotificationSeverity\|Warning\|Information\|UnknownSeverity\|NotificationCategory\|Hint\|Unrecognized\|Unsupported\|Performance\|Deprecation\|Generic\|Security\|Topology\|UnknownCategory)`. +|label:removed[] |5.0 -|`Config.RootCAs` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/config#Config.TlsConfig[`Config.TlsConfig`]. -|label:deprecated[] -|{check-mark} - -|5.8 -|`neo4j.Config` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/config#Config[`config.Config`]. -|label:deprecated[] -|{check-mark} - -|5.8 -|`neo4j.ServerAddress` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/config#ServerAddress[`config.ServerAddress`]. -|label:deprecated[] -|{check-mark} - -|5.8 -|`neo4j.ServerAddressResolver` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/config#ServerAddressResolver[`config.ServerAddressResolver`]. -|label:deprecated[] -|{check-mark} - -|5.17 -|`neo4j.(LogLevel\|ERROR\|WARNING\|INFO\|DEBUG)` are deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/log#Level[`log.(Level\|ERROR\|WARNING\|INFO\|DEBUG)`]. -|label:deprecated[] -|{check-mark} - -|5.17 -|`neo4j.ConsoleLogger()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/log#ToConsole[`neo4j.log.ToConsole()`]. -The behavior is similar, except that the new function takes a `ctx.Context` as first argument. -|label:deprecated[] -|{check-mark} - -|5.17 -|`neo4j.ConsoleBoltLogger()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/log#BoltToConsole[`neo4j.log.BoltToConsole()`]. -|label:deprecated[] -|{check-mark} - -|5.17 -|Types `neo4j.log.Console` and `neo4j.log.Void` are deprecated and unified into link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/log#Logger[`neo4j.log.Logger`]. + -Functions `neo4j.ConsoleLogger()` and `neo4j.Void()` are deprecated in favor of https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/log#ToConsole[`neo4j.log.ToConsole()`] and https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/log#ToVoid[`neo4j.log.ToVoid()`], both returning a `neo4j.log.Logger` object. +|link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/dbtype#Node[`Node.Id`] and link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j/dbtype#Relationship[`Relationship.Id`] are deprecated in favor of `Node.ElementId` and `Relationship.ElementId`. +Old identifiers were Int64, whereas new ElementIds are String. |label:deprecated[] -|{check-mark} -|5.23 -|Notifications-related objects have been moved from the global namespace `neo4j` into link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/notifications[`neo4j/notifications`] -- `neo4j.(NotificationSeverity\|Warning\|Information\|UnknownSeverity\|NotificationCategory\|Hint\|Unrecognized\|Unsupported\|Performance\|Deprecation\|Generic\|Security\|Topology\|UnknownCategory)` are deprecated in favor of `notifications.(NotificationSeverity\|Warning\|Information\|UnknownSeverity\|NotificationCategory\|Hint\|Unrecognized\|Unsupported\|Performance\|Deprecation\|Generic\|Security\|Topology\|UnknownCategory)`. -|label:deprecated[] -|{check-mark} +|6.0 +|`*WithContext` functions and types are deprecated in favor of the non-suffixed names, which are context-aware by default. +In particular, -|5.28.2 -|link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#Single[`neo4j.Single`] is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#SingleWithContext[`neo4j.SingleWithContext`]. +- `NewDriverWithContext()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#NewDriver[`NewDriver()`]. +- `neo4j.SingleTWithContext()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#SingleT[`neo4j.SingleT()`]. +- `neo4j.CollectWithContext()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Collect[`neo4j.Collect()`]. +- `neo4j.CollectTWithContext()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#CollectT[`neo4j.CollectT()`]. +- `neo4j.SingleWithContext()` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Single[`neo4j.Single()`]. +- `neo4j.SessionWithContext` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Session[`neo4j.Session`]. +- `neo4j.DriverWithContext` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Driver[`neo4j.Driver`]. +- `neo4j.ResultWithContext` is deprecated in favor of link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Result[`neo4j.Result`]. |label:deprecated[] -|{check-mark} |=== diff --git a/go-manual/modules/ROOT/partials/glossary.adoc b/go-manual/modules/ROOT/partials/glossary.adoc index b5dafe2a..abe9a41f 100644 --- a/go-manual/modules/ROOT/partials/glossary.adoc +++ b/go-manual/modules/ROOT/partials/glossary.adoc @@ -1,2 +1,2 @@ [[transaction_function]]transaction function:: A transaction function is a callback executed by an `ExecuteRead` or `ExecuteWrite` call. The driver automatically re-executes the callback in case of server failure. -[[DriverWithContext]]DriverWithContext:: A link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j#DriverWithContext[`DriverWithContext`] object holds the details required to establish connections with a Neo4j database. +[[Driver]]Driver:: A link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/neo4j#Driver[`Driver`] object holds the details required to establish connections with a Neo4j database. diff --git a/go-manual/modules/ROOT/partials/quickstart.adoc b/go-manual/modules/ROOT/partials/quickstart.adoc index dd32851b..1dcac864 100644 --- a/go-manual/modules/ROOT/partials/quickstart.adoc +++ b/go-manual/modules/ROOT/partials/quickstart.adoc @@ -1,23 +1,23 @@ // This doc is also sourced in the Aura Developer Hub. -// Because of that, cross-links are absolute and -// source blocks have copy=true . +// Because of that, cross-links are absolute, +// source blocks have copy=true , and Antora attributes +// (version) are explicitly resolved. The Neo4j Go driver is the official library to interact with a Neo4j instance through a Go application. At the hearth of Neo4j lies <>, the query language to interact with a Neo4j database. -While this guide does not _require_ you to be a seasoned Cypher querier, it is going to be easier to focus on the Go-specific bits if you already know some Cypher. -For this reason, although this guide does _also_ provide a gentle introduction to Cypher along the way, consider checking out link:https://neo4j.com/docs/getting-started/cypher/[Getting started -> Cypher] for a more detailed walkthrough of graph databases modelling and querying if this is your first approach. -You may then apply that knowledge while following this guide to develop your Go application. +Although this guide does not _require_ you to be a seasoned Cypher querier, it's easier to focus on the Go-specific bits if you know some Cypher already. +You will also get a _gentle_ introduction to Cypher in these pages, but check out link:https://neo4j.com/docs/getting-started/cypher/[Getting started -> Cypher] for a more detailed walkthrough of graph databases modelling and querying if this is your first approach. [#install] == Install -From within a module, use `go get` to install the link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/[Neo4j Go Driver]: +From within a module, use `go get` to install the link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v6/[Neo4j Go Driver]: [source, bash, copy=true] ---- -go get github.com/neo4j/neo4j-go-driver/v5 +go get github.com/neo4j/neo4j-go-driver/v6 ---- link:https://neo4j.com/docs/go-manual/current/install/[More info on installing the driver] @@ -26,8 +26,8 @@ link:https://neo4j.com/docs/go-manual/current/install/[More info on installing t [#connect] == Connect to the database -Connect to a database by creating a `DriverWithContext` object and providing a URL and an authentication token. -Once you have a `DriverWithContext` instance, use the `.VerifyConnectivity()` method to ensure that a working connection can be established. +Connect to a database by creating a `Driver` object and providing a URL and an authentication token. +Once you have a `Driver` instance, use the `.VerifyConnectivity()` method to ensure that a working connection can be established. [source, go, role=nocollapse, copy=true] ---- @@ -36,7 +36,7 @@ package main import ( "fmt" "context" - "github.com/neo4j/neo4j-go-driver/v5/neo4j" + "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) func main() { @@ -45,7 +45,7 @@ func main() { dbUri := "" dbUser := "" dbPassword := "" - driver, err := neo4j.NewDriverWithContext( + driver, err := neo4j.NewDriver( dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) defer driver.Close(ctx) @@ -131,12 +131,12 @@ link:https://neo4j.com/docs/go-manual/current/query-simple/[More info on queryin [#close] == Close connections and sessions -Call the `.close()` method on all `DriverWithContext` and `SessionWithContext` instances to release any resources still held by them. +Call the `.close()` method on all `Driver` and `Session` instances to release any resources still held by them. The best practice is to call the methods with the `defer` keyword as soon as you create new objects. [source, go, copy=true] ---- -driver, err := neo4j.NewDriverWithContext(dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) +driver, err := neo4j.NewDriver(dbUri, neo4j.BasicAuth(dbUser, dbPassword, "")) defer driver.Close(ctx) ---- diff --git a/javascript-manual/modules/ROOT/pages/upgrade.adoc b/javascript-manual/modules/ROOT/pages/upgrade.adoc index eb3f48ac..40461705 100644 --- a/javascript-manual/modules/ROOT/pages/upgrade.adoc +++ b/javascript-manual/modules/ROOT/pages/upgrade.adoc @@ -107,7 +107,7 @@ To retrieve the server info, use link:https://neo4j.com/docs/api/javascript-driv |label:removed[Changed] |6.0 -|link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/result-summary.js~ServerInfo.html[`ServerInfo`] -- The return type of attribute link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/result-summary.js~ServerInfo.html#instance-member-protocolVersion[`protocolVersion`] changed from `number` to `ProtocolVersion`. +|link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/result-summary.js~ServerInfo.html[`ServerInfo`] -- The return type of attribute link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/result-summary.js~ServerInfo.html#instance-member-protocolVersion[`protocolVersion`] changed from `number` to link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/protocol-version.js~ProtocolVersion.html[`ProtocolVersion`]. |label:removed[Changed] |5.0 diff --git a/preview.yml b/preview.yml index 03bf262c..33d971f3 100644 --- a/preview.yml +++ b/preview.yml @@ -14,6 +14,7 @@ content: - javascript-manual - python-manual - java-manual + - go-manual - common-content exclude: - '!**/_includes/*' @@ -24,7 +25,6 @@ content: edit_url: https://github.com/neo4j/docs-drivers/tree/{refname}/{path} worktrees: true start_paths: - - go-manual - dotnet-manual exclude: - '!**/_includes/*' diff --git a/publish.yml b/publish.yml index 16e3c2a8..d309fca2 100644 --- a/publish.yml +++ b/publish.yml @@ -13,6 +13,7 @@ content: - java-manual - javascript-manual - python-manual + - go-manual - common-content exclude: - '!**/_includes/*' @@ -24,7 +25,6 @@ content: worktrees: true start_paths: - dotnet-manual - - go-manual exclude: - '!**/_includes/*' - '!**/readme.adoc'