Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion common-content/antora.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
name: common-content
title: Neo4j Driver Manuals Common content
version: '6'
prerelease: true
10 changes: 4 additions & 6 deletions go-manual/antora.yml
Original file line number Diff line number Diff line change
@@ -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$
27 changes: 12 additions & 15 deletions go-manual/modules/ROOT/pages/bookmarks.adoc
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
= Coordinate parallel transactions

When working with a Neo4j cluster, <<causal_consistency>> 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, <<causal_consistency>> 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 <<bookmark,bookmarks>> if you _need_ casual consistency across different transactions*, as waiting for bookmarks can have a negative performance impact.


== Bookmarks with `ExecuteQuery()`
Expand Down Expand Up @@ -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]
----
Expand Down Expand Up @@ -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() {
Expand All @@ -89,7 +86,7 @@ func main() {
dbUri := "<database-uri>"
dbUser := "<username>"
dbPassword := "<password>"
driver, err := neo4j.NewDriverWithContext(
driver, err := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""))
if err != nil {
Expand Down Expand Up @@ -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,
Expand All @@ -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, `
Expand All @@ -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, `
Expand All @@ -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,
Expand All @@ -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]
Expand Down
16 changes: 8 additions & 8 deletions go-manual/modules/ROOT/pages/concurrency.adoc
Original file line number Diff line number Diff line change
@@ -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].

Expand All @@ -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() {
Expand All @@ -30,7 +30,7 @@ func main() {
dbUri := "<database-uri>"
dbUser := "<username>"
dbPassword := "<password>"
driver, err := neo4j.NewDriverWithContext(
driver, err := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""))
if err != nil {
Expand Down Expand Up @@ -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: "<database-name>"})
defer session.Close(ctx)
Expand Down Expand Up @@ -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.
Expand All @@ -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() {
Expand All @@ -136,7 +136,7 @@ func main() {
dbUri := "<database-uri>"
dbUser := "<username>"
dbPassword := "<password>"
driver, err := neo4j.NewDriverWithContext(
driver, err := neo4j.NewDriver(
dbUri,
neo4j.BasicAuth(dbUser, dbPassword, ""))
if err != nil {
Expand Down Expand Up @@ -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
Expand Down
46 changes: 22 additions & 24 deletions go-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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, ""))
----
Expand All @@ -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))
----


Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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.
Expand All @@ -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]
----
Expand All @@ -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
})
----
Expand All @@ -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]
----
Expand All @@ -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...
Expand All @@ -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 {
Expand All @@ -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[]
Expand Down
Loading
Loading