From 0b3cd624f588885601e0e1a4ce8df4581470c3d0 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Mon, 29 Sep 2025 04:50:56 +0200 Subject: [PATCH 1/8] preparations --- dotnet-manual/antora.yml | 9 +++++---- dotnet-manual/modules/ROOT/partials/quickstart.adoc | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/dotnet-manual/antora.yml b/dotnet-manual/antora.yml index 0ed76d1f..8637227e 100644 --- a/dotnet-manual/antora.yml +++ b/dotnet-manual/antora.yml @@ -1,11 +1,12 @@ name: dotnet-manual title: Neo4j .NET Driver Manual -version: '5' +version: '6' +prerelease: true start_page: ROOT:index.adoc nav: - modules/ROOT/content-nav.adoc asciidoc: attributes: - dotnet-driver-version: '5.28' # the version of the package published - common-partial: 5@common-content:ROOT:partial$ - common-image: 5@common-content:ROOT:image$ + dotnet-driver-version: '6.0' # the version of the package published + common-partial: 6@common-content:ROOT:partial$ + common-image: 6@common-content:ROOT:image$ diff --git a/dotnet-manual/modules/ROOT/partials/quickstart.adoc b/dotnet-manual/modules/ROOT/partials/quickstart.adoc index a19366f4..b769ffab 100644 --- a/dotnet-manual/modules/ROOT/partials/quickstart.adoc +++ b/dotnet-manual/modules/ROOT/partials/quickstart.adoc @@ -1,12 +1,13 @@ // 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 .NET driver is the official library to interact with a Neo4j instance through a .NET 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 .NET-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 .NET application. +Although this guide does not _require_ you to be a seasoned Cypher querier, it's easier to focus on the .NET-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] From 7824da7464395c24a29df7d554d152f8fe11c3f8 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Tue, 14 Oct 2025 17:52:41 +0200 Subject: [PATCH 2/8] Full review --- dotnet-manual/antora.yml | 1 - .../modules/ROOT/pages/bookmarks.adoc | 41 +++--- .../modules/ROOT/pages/connect-advanced.adoc | 18 +-- dotnet-manual/modules/ROOT/pages/connect.adoc | 8 +- .../modules/ROOT/pages/data-types.adoc | 100 +++++++++++-- dotnet-manual/modules/ROOT/pages/install.adoc | 4 +- .../modules/ROOT/pages/object-mapping.adoc | 5 +- .../modules/ROOT/pages/performance.adoc | 10 +- .../modules/ROOT/pages/query-advanced.adoc | 37 +++-- .../modules/ROOT/pages/query-simple.adoc | 119 ++++++++++++--- .../modules/ROOT/pages/reactive.adoc | 4 +- .../modules/ROOT/pages/result-summary.adoc | 137 +++++++----------- .../modules/ROOT/pages/transactions.adoc | 73 +++++----- dotnet-manual/modules/ROOT/pages/upgrade.adoc | 94 ++---------- .../modules/ROOT/partials/glossary.adoc | 2 +- .../modules/ROOT/partials/quickstart.adoc | 2 +- 16 files changed, 344 insertions(+), 311 deletions(-) diff --git a/dotnet-manual/antora.yml b/dotnet-manual/antora.yml index 8637227e..bb51e217 100644 --- a/dotnet-manual/antora.yml +++ b/dotnet-manual/antora.yml @@ -1,7 +1,6 @@ name: dotnet-manual title: Neo4j .NET Driver Manual version: '6' -prerelease: true start_page: ROOT:index.adoc nav: - modules/ROOT/content-nav.adoc diff --git a/dotnet-manual/modules/ROOT/pages/bookmarks.adoc b/dotnet-manual/modules/ROOT/pages/bookmarks.adoc index 84c92d03..c5578ead 100644 --- a/dotnet-manual/modules/ROOT/pages/bookmarks.adoc +++ b/dotnet-manual/modules/ROOT/pages/bookmarks.adoc @@ -1,12 +1,10 @@ = 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. [#executablequery] @@ -38,7 +36,7 @@ await driver.ExecutableQuery("") [#single-session] == Bookmarks within a single session -Bookmark management happens automatically for queries run within a single session, so 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, csharp] ---- @@ -131,44 +129,39 @@ async Task printFriendships(IAsyncQueryRunner tx) { } ---- -<1> Collect and combine bookmarks from different sessions using `AsyncSession.LastBookmarks`, storing them in a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Bookmarks.html[`Bookmarks`] object. +<1> Collect and combine bookmarks from different sessions using `AsyncSession.LastBookmarks`, storing them in a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Bookmarks.html[`Bookmarks`] object. <2> Use them to initialize another session with the `.WithBookmarks()` config method. 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. -//// -Note: not possible on .NET == Mix `.ExecutableQuery()` and sessions -To ensure causal consistency among transactions executed partly with `.ExecutableQuery()` and partly with sessions, you can retrieve the default bookmark manager for `ExecutableQuery` instances through `driver.executableQueryBookmarkManager()` and pass it to new sessions through the `.WithBookmarkManager()` config method. +To ensure causal consistency among transactions executed partly with `.ExecutableQuery()` and partly with sessions, you can retrieve the default bookmark manager for the `ExecutableQuery` interface through `driver.GetExecutableQueryBookmarkManager()` and pass it to new sessions through the link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.SessionConfigBuilder.WithBookmarkManager.html#Neo4j_Driver_SessionConfigBuilder_WithBookmarkManager_Neo4j_Driver_IBookmarkManager_[`SessionConfigBuilder.WithBookmarkManager()`] config method. This will ensure that all work is executed under the same bookmark manager and thus causally consistent. -[source, java] +[source, dotnet] ---- -// import org.neo4j.driver.Driver; -// import org.neo4j.driver.SessionConfig; -driver.executableQuery("").execute(); +await driver.ExecutableQuery("").ExecuteAsync(); -try (var session = driver.session(SessionConfig.builder() - .withBookmarkManager(driver.executableQueryBookmarkManager()) - .build())) { +using var session = driver.AsyncSession(conf => conf. + WithDatabase("") + .WithBookmarkManager(driver.GetExecutableQueryBookmarkManager())); + +// every query inside this session will be causally chained +// (i.e., can read what was written by ) +await session.ExecuteWriteAsync(async tx => await tx.RunAsync("")); - // every query inside this session will be causally chained - // (i.e., can read what was written by ) - session.executeWriteWithoutResult(tx -> tx.run("")); -} -// subsequent executableQuery calls will also be causally chained +// subsequent ExecutableQuery calls will also be causally chained // (i.e., can read what was written by ) -driver.executableQuery("").execute(); +await driver.ExecutableQuery("").ExecuteAsync(); ---- -//// ifndef::backend-pdf[] diff --git a/dotnet-manual/modules/ROOT/pages/connect-advanced.adoc b/dotnet-manual/modules/ROOT/pages/connect-advanced.adoc index 7269f80e..1e4a4856 100644 --- a/dotnet-manual/modules/ROOT/pages/connect-advanced.adoc +++ b/dotnet-manual/modules/ROOT/pages/connect-advanced.adoc @@ -12,7 +12,6 @@ include::{common-partial}/connect-advanced.adoc[tag=connection-protocols] === Basic authentication (default) 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, csharp] ---- @@ -49,7 +48,7 @@ await using var driver = GraphDatabase.Driver(dbUri, AuthTokens.Bearer(ticket)); [#auth-custom] === Custom authentication -Use link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.AuthTokens.Custom.html[`AuthTokens.Custom()`] to log into a server having a custom authentication scheme. +Use link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.AuthTokens.Custom.html[`AuthTokens.Custom()`] to log into a server having a custom authentication scheme. [source, csharp, test-skip] ---- @@ -68,13 +67,12 @@ If authentication is disabled on the server, the authentication parameter can be [#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 an link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAuthTokenManager.html[`IAuthTokenManager`] instance when instantiating the `IDriver`, rather than a static authentication token. +You need to provide an link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAuthTokenManager.html[`IAuthTokenManager`] instance when instantiating the `IDriver`, rather than a static authentication token. -The easiest way to get started is to use link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.AuthTokenManagers.html[one of built-in `AuthTokenManagers` implementations]. AuthTokenManagers work with link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.AuthTokenAndExpiration.html[`AuthTokenAndExpiration`] objects. +The easiest way to get started is to use link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.AuthTokenManagers.html[one of built-in `AuthTokenManagers` implementations]. AuthTokenManagers work with link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.AuthTokenAndExpiration.html[`AuthTokenAndExpiration`] objects. .Rotating a bearer token expiring every 60 seconds [source, csharp, test-skip] @@ -110,7 +108,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. @@ -121,7 +119,7 @@ The client's certificate and public key must be placed in the server's ``. [source, csharp, test-skip] @@ -152,7 +150,7 @@ await driver.VerifyConnectivityAsync(); [#logging] == Logging -To enable logging use link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.ConfigBuilder.WithLogger.html[`.withLogger(ILogger)`] config method when creating a `Driver` object, providing an implementation of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.ILogger.html[`ILogger`]. +To enable logging use link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ConfigBuilder.WithLogger.html[`.withLogger(ILogger)`] config method when creating a `Driver` object, providing an implementation of link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ILogger.html[`ILogger`]. .Enable debug logging with example implementation of `ILogger` [source, csharp] @@ -251,7 +249,7 @@ public class Neo4jLogger : ILogger { When creating an `IDriver` 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. -You specify a resolver through the link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.ConfigBuilder.WithResolver.html)[`.WithResolver()`] config method, which works with link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IServerAddressResolver.html[`IServerAddressResolver`] objects. +You specify a resolver through the link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ConfigBuilder.WithResolver.html)[`.WithResolver()`] config method, which works with link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IServerAddressResolver.html[`IServerAddressResolver`] objects. *The resolved address must have the same port and URI scheme.* In the example below, the driver is initialized with a `neo4j://` URI scheme and port `7687`, so the resolved address provided in `dbUri` must also be available at `neo4j://` and port `7687`. @@ -291,7 +289,7 @@ class ListAddressResolver : IServerAddressResolver { == Further connection parameters -You can find all `IDriver` configuration parameters in the link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.ConfigBuilder.html[API documentation -> ConfigBuilder]. +You can find all `IDriver` configuration parameters in the link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ConfigBuilder.html[API documentation -> ConfigBuilder]. ifndef::backend-pdf[] diff --git a/dotnet-manual/modules/ROOT/pages/connect.adoc b/dotnet-manual/modules/ROOT/pages/connect.adoc index 35820d05..39dd5bc4 100644 --- a/dotnet-manual/modules/ROOT/pages/connect.adoc +++ b/dotnet-manual/modules/ROOT/pages/connect.adoc @@ -22,13 +22,13 @@ Console.WriteLine("Connection established."); ---- <1> Creating a `IDriver` 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. +Connection is deferred to when the first query is executed. <2> To verify immediately that the driver can connect to the database (valid credentials, compatible versions, etc), use the `.VerifyConnectivityAsync()` method after initializing the driver. -Both the creation of a `IDriver` object and the connection verification can raise a number of different link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Neo4jException.html[exceptions]. +Both the creation of a `IDriver` object and the connection verification can raise a number of different link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Neo4jException.html[exceptions]. Since a connection error is a blocker for any subsequent task, the most common choice is to let the program crash should an exception occur while establishing a connection. -**`IDriver` objects are immutable, thread-safe, and expensive to create**, so your application should create only one instance and pass it around (you may share `IDriver` instances across threads). +**`IDriver` objects are immutable, thread-safe, and expensive to create**, so your application should create only one instance and pass it around (you can share `IDriver` instances across threads). You can xref:query-simple#impersonation[run queries through several different users] without creating a new `IDriver` instance. If you want to alter a `IDriver` configuration, you need to create a new object. @@ -39,7 +39,7 @@ The driver also supports other xref:connect-advanced.adoc#authentication-methods [#aura] == Connect to an Aura instance -When you create an <> instance, you receive 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 receive 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. diff --git a/dotnet-manual/modules/ROOT/pages/data-types.adoc b/dotnet-manual/modules/ROOT/pages/data-types.adoc index 19287133..9741be59 100644 --- a/dotnet-manual/modules/ROOT/pages/data-types.adoc +++ b/dotnet-manual/modules/ROOT/pages/data-types.adoc @@ -32,12 +32,12 @@ Inbound conversion is carried out using link:http://cldr.unicode.org/development |=== | Cypher type | Driver type -| `DATE` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.LocalDate.html[`LocalDate`] -| `ZONED TIME` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.OffsetTime.html[`OffsetTime`] -| `LOCAL TIME` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.LocalTime.html[`LocalTime`] -| `ZONED DATETIME` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.ZonedDateTime.html[`ZonedDateTime`] -| `LOCAL DATETIME` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.LocalDateTime.html[`LocalDateTime`] -| `DURATION` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Duration.html[`Duration`] +| `DATE` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.LocalDate.html[`LocalDate`] +| `ZONED TIME` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.OffsetTime.html[`OffsetTime`] +| `LOCAL TIME` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.LocalTime.html[`LocalTime`] +| `ZONED DATETIME` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ZonedDateTime.html[`ZonedDateTime`] +| `LOCAL DATETIME` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.LocalDateTime.html[`LocalDateTime`] +| `DURATION` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Duration.html[`Duration`] |=== @@ -72,10 +72,10 @@ You can think of it as a unique identifier for each spatial type. |=== | Cypher type | Driver type | SRID -| `POINT` (2D Cartesian) | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Point.html[`Point`] | 7203 -| `POINT` (2D WGS-84) | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Point.html[`Point`] | 4326 -| `POINT` (3D Cartesian) | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Point.html[`Point`] | 9157 -| `POINT` (3D WGS-84) | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Point.html[`Point`] | 4979 +| `POINT` (2D Cartesian) | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Point.html[`Point`] | 7203 +| `POINT` (2D WGS-84) | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Point.html[`Point`] | 4326 +| `POINT` (3D Cartesian) | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Point.html[`Point`] | 9157 +| `POINT` (3D WGS-84) | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Point.html[`Point`] | 4979 |=== @@ -111,9 +111,9 @@ Console.WriteLine(result.Result[0].Get("p").Get("location")); |=== | Cypher Type | Driver type -| `NODE` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.INode.html[`INode`] -| `RELATIONSHIP` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IRelationship.html[`IRelationship`] -| `PATH` | link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IPath.html[`IPath`] +| `NODE` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.INode.html[`INode`] +| `RELATIONSHIP` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IRelationship.html[`IRelationship`] +| `PATH` | link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IPath.html[`IPath`] |=== @@ -161,7 +161,7 @@ Name property: Carla */ ---- -For full documentation, see link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.INode.html[API documentation -> INode]. +For full documentation, see link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.INode.html[API documentation -> INode]. [#irelationship] @@ -219,7 +219,7 @@ Status property: BFF */ ---- -For full documentation, see link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IRelationship.html[API documentation -> IRelationship]. +For full documentation, see link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IRelationship.html[API documentation -> IRelationship]. [#ipath] @@ -283,7 +283,75 @@ async Task addFriend(IDriver driver, string name, string status, string friendNa } ---- -For full documentation, see link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IPath.html[API documentation -> IPath]. +For full documentation, see link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IPath.html[API documentation -> IPath]. + + +//// +[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/dotnet-manual/modules/ROOT/pages/install.adoc b/dotnet-manual/modules/ROOT/pages/install.adoc index 4bb312c2..b67a2206 100644 --- a/dotnet-manual/modules/ROOT/pages/install.adoc +++ b/dotnet-manual/modules/ROOT/pages/install.adoc @@ -33,8 +33,8 @@ dotnet install package Neo4j.Driver To compile and run, use `dotnet run`. ==== -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. +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 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-dotnet-driver/wiki/5.X-Change-Log[driver's changelog]. diff --git a/dotnet-manual/modules/ROOT/pages/object-mapping.adoc b/dotnet-manual/modules/ROOT/pages/object-mapping.adoc index ab3ca722..61600e9a 100644 --- a/dotnet-manual/modules/ROOT/pages/object-mapping.adoc +++ b/dotnet-manual/modules/ROOT/pages/object-mapping.adoc @@ -1,7 +1,6 @@ = Map query results to objects -:page-role: new-5.24 -When xref:query-simple.adoc#read[reading data from the database], results come back as link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IRecord.html[`IRecord`] objects. +When xref:query-simple.adoc#read[reading data from the database], results come back as link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IRecord.html[`IRecord`] objects. To process records, you can either extract properties with `.Get()` and other helper methods, or you can map them to objects in your application. You can either map records to classes you have explicitly defined, or provide a record-specific blueprint from which the driver will spawn new objects. @@ -205,7 +204,7 @@ The constructor signature implies that the `name` return key is mapped to the cl Especially if your queries return a variety of result keys, it can feel pointless to define a number of different classes just for the mapping to work, or to engineer one single class and try to make it adapt to a variety of return keys with attribute decorators. In those cases, you can provide a skeleton of definition (a _blueprint_) to the driver and let it instantiate anonymous objects out of that. -You provide a blueprint with the method link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Mapping.RecordExtensions.AsObjectFromBlueprint.html[`IRecord.AsObjectFromBlueprint()`], and the record must match it exactly (i.e. you can't use decorators). +You provide a blueprint with the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Mapping.RecordExtensions.AsObjectFromBlueprint.html[`IRecord.AsObjectFromBlueprint()`], and the record must match it exactly (i.e. you can't use decorators). [source, csharp] ---- diff --git a/dotnet-manual/modules/ROOT/pages/performance.adoc b/dotnet-manual/modules/ROOT/pages/performance.adoc index 6aac63cc..a8f95041 100644 --- a/dotnet-manual/modules/ROOT/pages/performance.adoc +++ b/dotnet-manual/modules/ROOT/pages/performance.adoc @@ -74,7 +74,7 @@ await session.ExecuteWriteAsync(async tx => { ---- An even faster approach is to skip `.ExecuteRead/WriteAsync()` and call `.RunAsync()` 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 `.RunAsync()` borrows a connection, so the amount of parallel work is limited by the number of available connections. @@ -184,7 +184,7 @@ void log(string msg) { ---- .Output -[source, output, role=nocollapse] +[source, output] ---- [1734336457] LAZY LOADING (ExecuteReadAsync) [1734336457] Submit query @@ -216,14 +216,14 @@ With lazy loading, the client can also stop requesting records after some condit [TIP] ==== -When using `.ExecutableQuery()`, you can process records as they arrive using the link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IExecutableQuery-2.WithStreamProcessor.html[`.WithStreamProcessor()`] method. +When using `.ExecutableQuery()`, you can process records as they arrive using the link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IExecutableQuery-2.WithStreamProcessor.html[`.WithStreamProcessor()`] method. This will speed up response by not waiting for all records before processing them; it does not however improve memory footprint, as you can't control the flux of records. -You can reduce the memory footprint of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.EagerResult-1.html[`EagerResult`] using link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IConfiguredQuery-2.WithMap.html[`.WithMap()`] or link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IConfiguredQuery-2.WithReduce.html[`.WithReduce()`]. +You can reduce the memory footprint of link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.EagerResult-1.html[`EagerResult`] using link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IConfiguredQuery-2.WithMap.html[`.WithMap()`] or link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IConfiguredQuery-2.WithReduce.html[`.WithReduce()`]. ==== [NOTE] ==== -The driver's link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.SessionConfigBuilder.WithFetchSize.html[fetch size] affects the behavior of lazy loading. +The driver's link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.SessionConfigBuilder.WithFetchSize.html[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 allows to bound memory consumption on the client side. diff --git a/dotnet-manual/modules/ROOT/pages/query-advanced.adoc b/dotnet-manual/modules/ROOT/pages/query-advanced.adoc index f0f7a071..df2cce76 100644 --- a/dotnet-manual/modules/ROOT/pages/query-advanced.adoc +++ b/dotnet-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[`.ExecutableQuery()`] and with xref:transactions#managed-transactions[managed transactions]. +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[`.ExecutableQuery()`] 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. -You run an implicit transaction with the method link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncSession.RunAsync.html[`ISessionAsync.RunAsync()]`. -It returns a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IResultCursor.html[`IResultCursor`] object that needs to be xref:transactions#process-result[processed accordingly]. +You run an implicit transaction with the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncSession.RunAsync.html[`ISessionAsync.RunAsync()]`. +It returns a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IResultCursor.html[`IResultCursor`] object that needs to be xref:transactions#process-result[processed accordingly]. [source, csharp] ---- @@ -20,10 +20,11 @@ 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 `.ConsumeAsync()` method on its result. -Since the driver cannot figure out whether the query in a `ISessionAsync.RunAsync()` 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] through the config method `.WithDefaultAccessMode(AccessMode.Read)` when creating the session. +Since the driver cannot figure out whether the query in a `ISessionAsync.RunAsync()` 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] through the config method `.WithDefaultAccessMode(AccessMode.Read)` 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] @@ -57,7 +58,7 @@ For more information, see link:{neo4j-docs-base-uri}/cypher-manual/current/claus [#tx-config] === Transaction configuration -You can exert further control on implicit transactions by providing a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.TransactionConfigBuilder.html[`TransactionConfigBuilder`] object as optional last parameter to `IAsyncSession.RunAsync()` calls. +You can exert further control on implicit transactions by providing a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.TransactionConfigBuilder.html[`TransactionConfigBuilder`] object as optional last parameter to `IAsyncSession.RunAsync()` calls. The configuration callbacks allow to specify a query timeout and to attach metadata to the transaction. For more information, see xref:transactions.adoc#tx-config[Transactions -> Transaction configuration]. @@ -78,30 +79,28 @@ await session.RunAsync( 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, csharp] ---- -var label = "Person\\u0060n"; +var dangerousKey = "name\\u0060n"; // convert \u0060 to literal backtick and then escape backticks -var escapedLabel = label.Replace("\\u0060", "`").Replace("`", "``"); +var escapedKey = dangerousKey.Replace("\\u0060", "`").Replace("`", "``"); -var result = await driver.ExecutableQuery("MATCH (p:`" + escapedLabel + "` {name: $name}) RETURN p.name") +var result = await driver.ExecutableQuery("MATCH (p:Person {`" + escapedKey + "`}) RETURN p.name") .WithParameters( new { name = "Alice" }) .WithConfig(new QueryConfig(database: "")) .ExecuteAsync(); + +// rewritten to +// MATCH (p:Person {`name```: $name}) RETURN p.name ---- -Another workaround, which avoids string concatenation, is using <> procedures, such as link:{neo4j-docs-base-uri}/apoc/5/overview/apoc.merge/apoc.merge.node/[`apoc.merge.node`], which supports dynamic labels and property keys. +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, csharp] diff --git a/dotnet-manual/modules/ROOT/pages/query-simple.adoc b/dotnet-manual/modules/ROOT/pages/query-simple.adoc index 28593f34..56e10d6c 100644 --- a/dotnet-manual/modules/ROOT/pages/query-simple.adoc +++ b/dotnet-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 execute <> queries through the method `IDriver.ExecutableQuery()`. - -[TIP] -`IDriver.ExecutableQuery()` 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 execute <> queries through the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IDriver.ExecutableQuery.html[`IDriver.ExecutableQuery()`]. [#write] @@ -58,7 +54,7 @@ var summary = result.Summary; // <.> Console.WriteLine($"The query `{summary.Query.Text}` returned {result.Result.Count()} results in {summary.ResultAvailableAfter.Milliseconds} ms."); ---- -<.> `result.Result` contains the result as a list of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IRecord.html[`IRecord`] objects +<.> `result.Result` contains the result as a list of link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IRecord.html[`IRecord`] objects <.> `.Get(key)` extracts the entry named `key` from the returned record and casts it to `type`. For more information on types, see xref:data-types.adoc[]. <.> The xref:result-summary.adoc[summary of execution] returned by the server @@ -147,7 +143,7 @@ Console.WriteLine($"Query updated the database? {result.Summary.Counters.Contain == 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; @@ -173,10 +169,10 @@ For those rare use cases, see xref:query-advanced.adoc#dynamic-advanced[Dynamic A query run may fail for a number of reasons. When using `IDriver.ExecutableQuery()`, 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://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.ConfigBuilder.WithMaxTransactionRetryTime.html[maximum retry time]. +An error is raised if the operation keeps failing after the configured link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ConfigBuilder.WithMaxTransactionRetryTime.html[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://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Neo4jException.html[`Neo4jException`]. -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://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Neo4jException.html[`Neo4jException`]. +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, csharp] @@ -196,18 +192,100 @@ Exception message: Invalid input '': expected an expression, '*', 'ALL' or 'DIST */ ---- +//// + +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("neo4j")) +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("neo4j")) +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 property link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Neo4jException.IsRetriable.html#Neo4j_Driver_Neo4jException_IsRetriable[`IsRetriable`], 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-config] == Query configuration You can supply further configuration parameters to alter the default behavior of `.ExecutableQuery()`. -You do so through the method `.WithConfig()`, which takes a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.QueryConfig.html[`QueryConfig`] object. +You do so through the method `.WithConfig()`, which takes a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.QueryConfig.html[`QueryConfig`] object. [#database-selection] === Database selection -It is recommended to *always specify the database explicitly*, even on single-database instances. +*Always specify the database explicitly*, 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:https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default[user's home database] is used. @@ -220,8 +298,8 @@ await driver.ExecutableQuery("MATCH (p:Person) RETURN p.name") [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] @@ -242,21 +320,19 @@ await driver.ExecutableQuery("MATCH (p:Person) RETURN p.name") [NOTE] ==== -Although executing a _write_ query in read mode likely results in a runtime error, *you should not rely on this for access control.* +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 guarantee that a write query submitted in read mode will be rejected, and an issue with this feature will not be classified as a security vulnerability. +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 config key `authToken`. Switching user at the query level is cheaper than creating a new `IDriver` object. The query is then run within the security context of the given user (i.e., home database, permissions, etc.). -See link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.AuthTokens.html[`AuthTokens`] for available native implementations of authentication methods. + -Query-scoped authentication requires a Neo4j server version >= 5.8. +See link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.AuthTokens.html[`AuthTokens`] for available native implementations of authentication methods. [source, csharp] ---- @@ -268,8 +344,7 @@ await driver.ExecutableQuery("MATCH (p:Person) RETURN p.name") .ExecuteAsync(); ---- -The `QueryConfig` key `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 `IDriver` was created needs to have link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-impersonation[`IMPERSONATE` privileges]. +The `QueryConfig` key `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 `IDriver` was created needs to have link:{neo4j-docs-base-uri}/operations-manual/current/authentication-authorization/dbms-administration/#access-control-dbms-administration-impersonation[`IMPERSONATE` privileges]. [source, csharp] ---- @@ -379,7 +454,7 @@ try { } ---- -For more information see link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IDriver.ExecutableQuery.html[API documentation -> IDriver.ExecutableQuery()]. +For more information see link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IDriver.ExecutableQuery.html[API documentation -> IDriver.ExecutableQuery()]. ifndef::backend-pdf[] diff --git a/dotnet-manual/modules/ROOT/pages/reactive.adoc b/dotnet-manual/modules/ROOT/pages/reactive.adoc index 43b40d8d..2fe0eb4f 100644 --- a/dotnet-manual/modules/ROOT/pages/reactive.adoc +++ b/dotnet-manual/modules/ROOT/pages/reactive.adoc @@ -31,7 +31,7 @@ The basic driver's concepts are the same as the synchronous case, except that qu === Managed transaction with reactive sessions -.A managed transaction `.executeRead()` example +.A managed transaction `.ExecuteRead()` example [source, csharp] ---- using Neo4j.Driver; @@ -75,7 +75,7 @@ await tcs.Task; The following example is very similar to the previous one, except it uses an xref:query-advanced.adoc#implicit-transactions[implicit transaction]. -.An implicit transaction `.run()` example +.An implicit transaction `.Run()` example [source, csharp] ---- using Neo4j.Driver; diff --git a/dotnet-manual/modules/ROOT/pages/result-summary.adoc b/dotnet-manual/modules/ROOT/pages/result-summary.adoc index e1f692be..cd886e48 100644 --- a/dotnet-manual/modules/ROOT/pages/result-summary.adoc +++ b/dotnet-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://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IResultSummary.html[`IResultSummary`] 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://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IResultSummary.html[`IResultSummary`] object, and it contains information among which: - xref:query-counters[**Query counters**] -- What changes the query triggered on the server - xref:execution-plan[**Query execution plan**] -- How the database would execute (or executed) the query @@ -25,8 +25,7 @@ var resultSummary = result.Summary; ---- If you are using xref:transactions.adoc[transaction functions], you can retrieve the query execution summary with the method `Result.ConsumeAsync()`. -**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, csharp] ---- @@ -46,7 +45,7 @@ var resultSummary = await session.ExecuteWriteAsync( [#query-counters] == Query counters -The property `IResultSummary.Counters` contains counters for the operations that a query triggered (as a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.ICounters.html[`ICounters`] object). +The property `IResultSummary.Counters` contains counters for the operations that a query triggered (as a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ICounters.html[`ICounters`] object). .Insert some data and display the query counters [source, csharp] @@ -67,20 +66,20 @@ ConstraintsAdded=0, ConstraintsRemoved=0, SystemUpdates=0} */ ---- -There are two additional boolean properties which act as meta-counters: +Two additional boolean properties 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 [#execution-plan] == 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. -The plan is then available as a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IPlan.html[`IPlan`] object in the property `IResultSummary.Plan`, and contains the list of link:{neo4j-docs-base-uri}/cypher-manual/current/execution-plans/operators/[Cypher operators] that would be used to retrieve the result set. -You may use this information to locate potential bottlenecks or room for xref:performance.adoc[performance improvements] (for example through the creation of indexes). +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. +The plan is then available as a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IPlan.html[`IPlan`] object in the property `IResultSummary.Plan`, and contains the list of link:{neo4j-docs-base-uri}/cypher-manual/current/execution-plans/operators/[Cypher operators] that would be used to retrieve the result set. +Use this information to locate potential bottlenecks or room for xref:performance.adoc[performance improvements] (for example through the creation of indexes). -[source, csharp, role=nocollapse] +[source, csharp] ---- var result = await driver.ExecutableQuery("EXPLAIN MATCH (p {name: $name}) RETURN p") .WithParameters(new { name = "Alice" }) @@ -108,12 +107,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. -The plan is available as a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IProfiledPlan.html[`IProfiledPlan`] object in the property `IResultSummary.Profile`. +The plan is available as a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IProfiledPlan.html[`IProfiledPlan`] object in the property `IResultSummary.Profile`. Notice that the query is also _run_, so the result object also contains any result records. -[source, csharp, role=nocollapse] +[source, csharp] ---- var result = await driver.ExecutableQuery("PROFILE MATCH (p {name: $name}) RETURN p") .WithParameters(new { name = "Alice" }) @@ -148,58 +147,21 @@ For more information and examples, see link:https://neo4j.com/docs/cypher-manual [#notifications] == 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.26 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 property `IResultSummary.Notifications` contains a list of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.INotification.html[`INotification`] objects. - -[source, csharp, role=nocollapse] ----- -var result = await driver.ExecutableQuery(@" - MATCH p=shortestPath((:Person {name: $start})-[*]->(:Person {name: $end})) - RETURN p - ") - .WithParameters(new { start = "Alice", end = "Bob" }) - .WithConfig(new QueryConfig(database: "")) - .ExecuteAsync(); -var notifications = result.Summary.Notifications; -foreach (var n in notifications) { - Console.WriteLine(n); -} -/* -Notification{ - 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. - Position=InputPosition{Offset=30, Line=2, Column=30} - SeverityLevel=Information - Category=Performance - RawSeverityLevel=INFORMATION - RawCategory=PERFORMANCE -} -*/ ----- - -====== [.include-with-GQL-status-code] ====== -With version >= 5.26, the property `IResultSummary.GqlStatusObjects` contains a list of GQL-compliant status objects (of type link:https://neo4j.com/docs/api/dotnet-driver/5.28/api/Neo4j.Driver.IGqlStatusObject.html[`IGqlStatusObject`]). +The property `IResultSummary.GqlStatusObjects` contains a list of GQL-compliant status objects (of type link:https://neo4j.com/docs/api/dotnet-driver/5.28/api/Neo4j.Driver.IGqlStatusObject.html[`IGqlStatusObject`]). These objects contain either a notification or the query's _outcome_ status (`00000` for "success", `02000` for "no data", and `00001` for "omitted result"). The list always contains at least one entry, containing the outcome status. -[source, csharp, role=nocollapse] +[source, csharp] ---- var result = await driver.ExecutableQuery(@" MATCH p=shortestPath((:Person {name: $start})-[*]->(:Person {name: $end})) @@ -233,18 +195,53 @@ GqlStatusObject { ---- ====== + +[.include-with-neo4j-status-code] +====== +label:deprecated[Deprecated in 6.0] + +The property `IResultSummary.Notifications` contains a list of link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.INotification.html[`INotification`] objects. + +[source, csharp] +---- +var result = await driver.ExecutableQuery(@" + MATCH p=shortestPath((:Person {name: $start})-[*]->(:Person {name: $end})) + RETURN p + ") + .WithParameters(new { start = "Alice", end = "Bob" }) + .WithConfig(new QueryConfig(database: "")) + .ExecuteAsync(); +var notifications = result.Summary.Notifications; +foreach (var n in notifications) { + Console.WriteLine(n); +} +/* +Notification{ + 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. + Position=InputPosition{Offset=30, Line=2, Column=30} + SeverityLevel=Information + Category=Performance + RawSeverityLevel=INFORMATION + RawCategory=PERFORMANCE +} +*/ +---- + +====== + ===== [#filter-notifications] -[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 configuration method link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.ConfigBuilder.WithNotifications.html[`.WithNotifications()`] to tweak the severity and/or category/classification of notifications that you are interested in. +Use the configuration method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ConfigBuilder.WithNotifications.html[`.WithNotifications()`] to tweak the severity and/or category/classification of notifications that you are interested in. There is a slight performance gain in restricting the amount of notifications the server is allowed to raise. -`.WithNotifications()` takes either a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Severity.html[`Severity`] and a list of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Classification.html[`Classification`] (v5.26+), or a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Severity.html[`Severity`] and a list of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Category.html[`Category`]. The provided classifications/categories are *excluded* from output. + +`.WithNotifications()` takes either a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Severity.html[`Severity`] and a list of link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Classification.html[`Classification`], or a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Severity.html[`Severity`] and a list of link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Category.html[`Category`] (deprecated). The provided classifications/categories are *excluded* from output. + The severity filter applies to both Neo4j and GQL notifications. + The classification filter acts on both categories and classifications. + To disable all notifications, use `.WithNotificationsDisabled()`. @@ -286,29 +283,3 @@ using var session = driver.AsyncSession(conf => conf .WithNotificationsDisabled() ); ---- - -.Notifications filtering on versions < 5.26 -[%collapsible] -==== -For versions earlier than 5.26, notification filtering works the same, except you don't supply a list of _classifications_ but a list of _categories_. - -.Allow only `Warning` notifications, but not of classification/category `Hint` or `Generic` -[source, csharp] ----- -// at driver level -await using var driver = GraphDatabase.Driver( - dbUri, AuthTokens.Basic(dbUser, dbPassword), conf => - conf.WithNotifications( - Severity.Warning, - disabledCategories: [Category.Hint, Category.Generic]) -); - -// at session level -using var session = driver.AsyncSession(conf => conf - .WithDatabase("") - .WithNotifications( - Severity.Warning, - disabledCategories: [Category.Hint, Category.Generic]) -); ----- -==== diff --git a/dotnet-manual/modules/ROOT/pages/transactions.adoc b/dotnet-manual/modules/ROOT/pages/transactions.adoc index 72e91451..b09f4667 100644 --- a/dotnet-manual/modules/ROOT/pages/transactions.adoc +++ b/dotnet-manual/modules/ROOT/pages/transactions.adoc @@ -2,17 +2,17 @@ When xref:query-simple.adoc[querying the database with `.ExecutableQuery()`], 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[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. [#session-create] == 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 link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IDriver.AsyncSession.html#Neo4j_Driver_IDriver_AsyncSession[`IDriver.AsyncSession()`]. +Sessions are created with the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IDriver.AsyncSession.html#Neo4j_Driver_IDriver_AsyncSession[`IDriver.AsyncSession()`]. Use the optional argument to alter the session's configuration, among which for example the xref:database-selection[target database]. For further configuration parameters, see xref:session-config[Session configuration]. @@ -21,20 +21,20 @@ For further configuration parameters, see xref:session-config[Session configurat using var session = driver.AsyncSession(conf => conf.WithDatabase("")); ---- -Session creation is a lightweight operation, so sessions can be created and destroyed without significant cost. +Creating a session is a lightweight operation, so sessions can be created and destroyed without significant cost. Always xref:close-session[close sessions] when you are done with them. -*Sessions are _not_ thread safe*: you can share the main `IDriver` object across threads, but make sure each thread creates its own sessions. +*Sessions are _not_ thread safe*: you can share the main `IDriver` object across threads, but each thread should create its own sessions. [#managed-transactions] == Run a managed transaction -A transaction can contain any number of queries. +A transaction can contain multiple queries. As Neo4j is <> 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://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncSession.ExecuteReadAsync.html)[`IAsyncSession.ExecuteRead()`] and link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncSession.ExecuteWriteAsync.html)[`IAsyncSession.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://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncSession.ExecuteReadAsync.html)[`IAsyncSession.ExecuteRead()`] and link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncSession.ExecuteWriteAsync.html)[`IAsyncSession.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`. @@ -70,7 +70,7 @@ foreach (var person in people) { <1> Create a session. A single session can be the container for multiple queries. Unless created as a resource with the `using` keyword, remember to close it when done. <2> The `.ExecuteRead()` (or `.ExecuteWrite()`) method is the entry point into a transaction. It takes a callback to a _transaction function_, which is responsible of running queries. -<3> Use the method link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncQueryRunner.RunAsync.html[`tx.RunAsync()`] to execute queries. You can provide a map of query parameters as second argument. Each query run returns a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IResultCursor.html[`IResultCursor`] object. +<3> Use the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncQueryRunner.RunAsync.html[`tx.RunAsync()`] to execute queries. You can provide a map of query parameters as second argument. Each query run returns a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IResultCursor.html[`IResultCursor`] object. <4> xref:process-result[Process the result] using any of the methods on `IResultCursor`. The method `.FetchAsync()` retrieves the next record in the queue and stores it in the property `.Current`; the method `.ToListAsync()` retrieves all records into a list. *Do not hardcode or concatenate parameters directly into the query*. @@ -80,9 +80,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 results in the transaction being committed, while the transaction is automatically rolled back if an exception is raised. -[CAUTION] -The methods `.ExecuteRead()` and `.ExecuteWrite()` have replaced `.ReadTransaction()` and `.WriteTransaction()`, which are deprecated in version 5.x and removed in version 6.0. - .A transaction with multiple queries, client logic, and potential roll backs [source, csharp] ---- @@ -173,10 +170,11 @@ async Task addPersonToOrganization(IAsyncQueryRunner tx, string personName, stri } ---- -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://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ConfigBuilder.WithMaxTransactionRetryTime.html[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. @@ -185,8 +183,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://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncSession.BeginTransactionAsync.html[`IAsyncSession.BeginTransactionAsync()`], which returns a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncTransaction.html[`IAsyncTransaction`] object. -You may then run queries inside an explicit transaction with the method link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncQueryRunner.RunAsync.html[`.RunAsync()`]. +You can achieve full control over transactions by manually beginning one with the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncSession.BeginTransactionAsync.html[`IAsyncSession.BeginTransactionAsync()`], which returns a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncTransaction.html[`IAsyncTransaction`] object. +You may then run queries inside an explicit transaction with the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncQueryRunner.RunAsync.html[`.RunAsync()`]. [source, csharp] ---- @@ -197,15 +195,15 @@ using var tx = await session.BeginTransactionAsync(); // tx.RollbackAsync() to roll the transaction back ---- +An explicit transaction can be committed with link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncTransaction.CommitAsync.html[`.CommitAsync()`] or rolled back with link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncTransaction.RollbackAsync.html[`.RollbackAsync()`]. +If no explicit action is taken, the driver will automatically roll the transaction back 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 link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Neo4jException.IsRetriable.html#Neo4j_Driver_Neo4jException_IsRetriable[`IsRetriable`] property, which gives insights into whether a further attempt might be successful. +You can discover whether an error is transient via the link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Neo4jException.IsRetriable.html#Neo4j_Driver_Neo4jException_IsRetriable[`IsRetriable`] property, which gives insights into whether a further attempt might be successful. ==== -An explicit transaction can be committed with link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncTransaction.CommitAsync.html[`.CommitAsync()`] or rolled back with link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncTransaction.RollbackAsync.html[`.RollbackAsync()`]. -If no explicit action is taken, the driver will automatically roll the transaction back 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. .An explicit transaction example involving an external API @@ -294,7 +292,7 @@ void requestInspection(string customerId, int otherBankId, float amount, Excepti [#process-result] == Process query results -The driver's output of a query is a link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IResultCursor.html[`IResultCursor`] object, which encapsulates the Cypher result in a rich data structure that requires some parsing on the client side. +The driver's output of a query is a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IResultCursor.html[`IResultCursor`] object, which 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: - *The result records are not immediately and entirely fetched and returned by the server*. @@ -312,12 +310,12 @@ The animation below follows the path of a single query: it shows how the driver class="rounded-corners" controls width="100%" - src="../../../common-content/5/_images/result.mp4" - poster="../../../common-content/5/_images/result-poster.jpg" + src="../../../common-content/6/_images/result.mp4" + poster="../../../common-content/6/_images/result-poster.jpg" type="video/mp4"> ++++ -**The easiest way of processing a result is by calling `.ToListAsync()` on it**, which yields a list of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IRecord.html[`IRecord`] objects. +**The easiest way of processing a result is by calling `.ToListAsync()` on it**, which yields a list of link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IRecord.html[`IRecord`] objects. Otherwise, a `IResultCursor` object implements a number of properties and methods for processing records. The most commonly needed ones are listed below. @@ -345,20 +343,20 @@ This leaves the record in the buffer for further processing. It exhausts the result, so should only be called when data processing is over. |=== -For a complete list of `IResultCursor` methods, see link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IResultCursor.html[API documentation -> IResultCursor]. +For a complete list of `IResultCursor` methods, see link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IResultCursor.html[API documentation -> IResultCursor]. [#session-config] == Session configuration You can customize the behavior of a session via the optional `SessioncConfig` parameter when creating a session. -Use link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.SessionConfigBuilder.html[`SessionConfigBuilder`] to create a session configuration object. +Use link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.SessionConfigBuilder.html[`SessionConfigBuilder`] to create a session configuration object. [#database-selection] === Database selection -It is recommended to *always specify the database explicitly* through the `.WithDatabase("")` session config method, even on single-database instances. +*Always specify the database explicitly* through the `.WithDatabase("")` session config method, 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/manage-databases/introduction#manage-databases-default[default database] set in the Neo4j instance settings is used. @@ -371,8 +369,8 @@ using var session = driver.AsyncSession(conf => conf [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] @@ -394,22 +392,20 @@ using var session = driver.AsyncSession(conf => conf // .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.* +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 are routed to any node of a cluster, whereas _write_ ones are directed to primaries. -There is no guarantee that a write query submitted in read mode will be rejected, and an issue with this feature will not be classified as a security vulnerability. +There is no security guarantee that a write query submitted in read mode will be rejected. Similar remarks hold for the `.ExecuteReadAsync()` and `.ExecuteWriteAsync()` methods. ==== [#impersonation] -[role=label--new-5.14] === Run queries as a different user You can execute a query through a different user by providing an link:https://neo4j.com/docs/api/dotnet-driver/5.28/api/Neo4j.Driver.IAuthToken.html[`IAuthToken`] to the `.WithAuthToken()` config method. Switching user at the session level is cheaper than creating a new `IDriver` 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. +Queries are then run within the security context of the given user (i.e., home database, permissions, etc.). [source, csharp] ---- @@ -419,8 +415,7 @@ using var session = driver.AsyncSession(conf => conf ); ---- -The method `.WithImpersonatedUser()` 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 `IDriver` 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 method `.WithImpersonatedUser()` 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 `IDriver` 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, csharp] ---- @@ -435,7 +430,7 @@ using var session = driver.AsyncSession(conf => conf == Transaction configuration You can exert further control on transactions by providing a `TransactionConfig` object as (optional) last parameter to `.ExecuteReadAsync()`, `.ExecuteWriteAsync()`, and `.BeginTransactionAsync()`. -You can create one via link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.TransactionConfigBuilder.html[`TransactionConfigBuilder`]. +You can create one via link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.TransactionConfigBuilder.html[`TransactionConfigBuilder`]. Use it to specify: - A transaction timeout. diff --git a/dotnet-manual/modules/ROOT/pages/upgrade.adoc b/dotnet-manual/modules/ROOT/pages/upgrade.adoc index 694067f6..325b5b3c 100644 --- a/dotnet-manual/modules/ROOT/pages/upgrade.adoc +++ b/dotnet-manual/modules/ROOT/pages/upgrade.adoc @@ -2,11 +2,10 @@ = Upgrade from older versions -This page contains the list of new features and breaking changes of the driver from version 4.4 to 5.x. +This page contains the list of new features and breaking changes of the driver from version 5.28 to 6.x. For a full list of changes, see the link:{driver-changelog-url}[driver changelog]. -The latest driver version of the 5.x series ({dotnet-driver-version}) is compatible with Neo4j server both 4.4 and 5.x, so you can upgrade the driver before you upgrade the server. -At the same time, the driver version 4.4 is forward compatible with Neo4j server 5.x, so you could also upgrade the server before the driver; however, given that it's easier to roll back an application upgrade than a server upgrade, it's recommended to start with the driver. +The latest driver version of the 6.x series is compatible with Neo4j server both 4.4, 5.x, and 202[56].x, so you can upgrade the driver before you upgrade the server. At the same time, the driver version 5.28 is forward compatible with Neo4j server 202[56].x, so you could also upgrade the server before the driver; however, given that it’s easier to roll back an application upgrade than a server upgrade, it’s recommended to start with the driver. [TIP] When upgrading the Neo4j server to a newer version, the Cypher queries in your application may also need updating. + @@ -20,104 +19,41 @@ See link:https://neo4j.com/docs/cypher-manual/current/deprecations-additions-rem |=== | -.Support for .NET 8 +.New type `Vector` [%collapsible] ==== -The driver supports the .NET Standard 2.0. For supported versions of the .NET Framework, see the link:https://dotnet.microsoft.com/en-us/platform/dotnet-standard[.NET Standard page]. +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. ==== | -.Run transactions with less knowledge of driver's internals +.GQL status objects in errors [%collapsible] ==== -The new method `Driver.ExecutableQuery()` is a wrapper for `Session.ExecuteRead/Write()`, but it abstracts away the result processing part and returns a list of records to the caller directly. - -For more information, see xref:query-simple.adoc[`ExecutableQuery`]. -==== - -| -.Record to object mapping -[%collapsible] -==== -Allows records to be mapped to objects simply and without endless boilerplate. - -For more information, see xref:object-mapping.adoc[Record to Object Mapping]. -==== -| -.Re-authentication -[%collapsible] -==== -Allows for handling xref:connect-advanced.adoc#rotating-tokens[expiring authentication (rotating tokens)] as well as xref:transactions.adoc#impersonation[session scoped authentication]. -==== - -| -.Mutual TLS (mTLS) as second authentication factor (2FA) -[%collapsible] -==== -Allows for configuring client side TLS certificates to authenticate against the server. - -For more information, see xref:connect-advanced.adoc#mtls[Mutual TLS]. -==== -| -.`BookmarkManager` support -[%collapsible] -==== -Bookmark managers make it easier to achieve causal chaining of sessions. - -See link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IBookmarkManager.html[interface `IBookmarkManager`]. +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]. ==== -| -.Notification filtering API -[%collapsible] -==== -Filtering allows to receive only a subset of notifications from the server, and to improve performance server-side. - -For more information, see xref:result-summary.adoc#filter-notifications[Explore the query execution summary -> Filter notifications]. -==== -| -.GQL statuses and errors -[%collapsible] -==== -The property link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IResultSummary.GqlStatusObjects.html[`IResultSummary.GqlStatusObjects`] contains a sequence of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IGqlStatusObject.html[`IGqlStatusObject`], holding information about the execution of the query. -This API is planned to supersede the current notifications API. - -For more information, 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 method `.WithTelemetryDisabled()` to opt out. - -See link:https://github.com/neo4j/neo4j-dotnet-driver/pull/735[API Telemetry]. -==== -| - |=== [[breaking-changes]] == Breaking changes and deprecations -Deprecated features are likely to be removed in version 6. +Deprecated features are likely to be removed in the next major version. + +MIN VERSION 8.0 [cols="1,5a,1"] |=== |Version |Message |Status -|5.0 -|link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IServerInfo.html[`IServerInfo`] -- Method `.Version()` has been removed. -Use `.Agent()`, `.ProtocolVersion()`, or call the link:https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_dbms_components[`dbms.components`] Cypher procedure instead. -|label:removed[] - -|5.0 +|6.0 |`ISession`, `IRxSession`, link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncSession.html[`IAsyncSession`] -- Methods `.ReadTransaction()` and `.WriteTransaction()` are deprecated in favor of `.ExecuteRead()` and `.ExecuteWrite()`. -The same holds for the async coounterparts `.ReadTransactionAsync()` and `.WriteTransactionAsync()`, replaced by `.ExecuteReadAsync()` and `.ExecuteWriteAsync()`. +The same holds for the async counterparts `.ReadTransactionAsync()` and `.WriteTransactionAsync()`, replaced by `.ExecuteReadAsync()` and `.ExecuteWriteAsync()`. The new methods behave similarly, but the argument types are different and the new methods do not support the `.commit()`, `.rollback()`, `.close()`, and `.closed()` methods. -|label:deprecated[] +|label:removed[] |5.0 |`ISession`, link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncSession.html[`IAsyncSession`] -- Method `.LastBookmark()` is deprecated in favor of `.LastBookmarks()`. diff --git a/dotnet-manual/modules/ROOT/partials/glossary.adoc b/dotnet-manual/modules/ROOT/partials/glossary.adoc index 9d3e146c..4eaddafd 100644 --- a/dotnet-manual/modules/ROOT/partials/glossary.adoc +++ b/dotnet-manual/modules/ROOT/partials/glossary.adoc @@ -1,2 +1,2 @@ [[transaction_function]]transaction function:: A transaction function is a callback executed by an `.ExecuteReadAsync()` or `.ExecuteWriteAsync()` call. The driver automatically re-executes the callback in case of server failure. -[[IDriver]]IDriver:: A link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IDriver.html[`IDriver`] object holds the details required to establish connections with a Neo4j database. +[[IDriver]]IDriver:: A link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IDriver.html[`IDriver`] object holds the details required to establish connections with a Neo4j database. diff --git a/dotnet-manual/modules/ROOT/partials/quickstart.adoc b/dotnet-manual/modules/ROOT/partials/quickstart.adoc index b769ffab..4b2e0cdb 100644 --- a/dotnet-manual/modules/ROOT/partials/quickstart.adoc +++ b/dotnet-manual/modules/ROOT/partials/quickstart.adoc @@ -20,7 +20,7 @@ Add the Neo4j .NET driver to your project: dotnet add package Neo4j.Driver ---- -The driver supports the .NET Standard 2.0. +The driver supports .NET 8.0 and 9.0. link:https://neo4j.com/docs/dotnet-manual/current/install/[More info on installing the driver] From efa602cd2192de32076d54648622fce78f0766c9 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 29 Oct 2025 11:56:59 +0000 Subject: [PATCH 3/8] changelog --- dotnet-manual/modules/ROOT/pages/connect.adoc | 2 +- dotnet-manual/modules/ROOT/pages/install.adoc | 5 +-- dotnet-manual/modules/ROOT/pages/upgrade.adoc | 44 ++++++++++--------- .../modules/ROOT/partials/quickstart.adoc | 4 +- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/dotnet-manual/modules/ROOT/pages/connect.adoc b/dotnet-manual/modules/ROOT/pages/connect.adoc index 39dd5bc4..5274d6eb 100644 --- a/dotnet-manual/modules/ROOT/pages/connect.adoc +++ b/dotnet-manual/modules/ROOT/pages/connect.adoc @@ -74,7 +74,7 @@ When interacting with a Neo4j database through the driver, it doesn't make a dif == Close connections Always close `IDriver` objects to free up all allocated resources, even upon unsuccessful connection or runtime errors. -Either create the `IDriver` object with the `using` keyword, or call the `Driver.CloseAsync()` method explicitly. +Either create the `IDriver` object with the `using` keyword, or call the `Driver.DisposeAsync()` method explicitly. == Further connection parameters diff --git a/dotnet-manual/modules/ROOT/pages/install.adoc b/dotnet-manual/modules/ROOT/pages/install.adoc index b67a2206..b1d09ad4 100644 --- a/dotnet-manual/modules/ROOT/pages/install.adoc +++ b/dotnet-manual/modules/ROOT/pages/install.adoc @@ -2,9 +2,8 @@ To start creating a Neo4j .NET application, you first need to install the .NET driver and get a Neo4j database instance to connect to. -[NOTE] -The driver supports the .NET Standard 2.0. -For supported versions of the .NET Framework, see the link:https://dotnet.microsoft.com/en-us/platform/dotnet-standard[.NET Standard page]. +[IMPORTANT] +The driver supports .NET 8.0 and 9.0. [#install-driver] diff --git a/dotnet-manual/modules/ROOT/pages/upgrade.adoc b/dotnet-manual/modules/ROOT/pages/upgrade.adoc index 325b5b3c..7dce99da 100644 --- a/dotnet-manual/modules/ROOT/pages/upgrade.adoc +++ b/dotnet-manual/modules/ROOT/pages/upgrade.adoc @@ -1,4 +1,4 @@ -:driver-changelog-url: https://github.com/neo4j/neo4j-dotnet-driver/wiki/5.X-Change-Log +:driver-changelog-url: https://github.com/neo4j/neo4j-dotnet-driver/wiki/6.X-Change-Log = Upgrade from older versions @@ -42,46 +42,48 @@ For more information, see xref:query-simple.adoc#error-handling[Error handling]. Deprecated features are likely to be removed in the next major version. -MIN VERSION 8.0 - [cols="1,5a,1"] |=== |Version |Message |Status |6.0 -|`ISession`, `IRxSession`, link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncSession.html[`IAsyncSession`] -- Methods `.ReadTransaction()` and `.WriteTransaction()` are deprecated in favor of `.ExecuteRead()` and `.ExecuteWrite()`. +|Supported .NET versions are 8.0 and 9.0. +|label:removed[Changed] + +|6.0 +|`ISession`, `IRxSession`, link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncSession.html[`IAsyncSession`] -- Methods `.ReadTransaction()` and `.WriteTransaction()` have been removed in favor of `.ExecuteRead()` and `.ExecuteWrite()`. The same holds for the async counterparts `.ReadTransactionAsync()` and `.WriteTransactionAsync()`, replaced by `.ExecuteReadAsync()` and `.ExecuteWriteAsync()`. The new methods behave similarly, but the argument types are different and the new methods do not support the `.commit()`, `.rollback()`, `.close()`, and `.closed()` methods. |label:removed[] -|5.0 -|`ISession`, link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IAsyncSession.html[`IAsyncSession`] -- Method `.LastBookmark()` is deprecated in favor of `.LastBookmarks()`. -|label:deprecated[] +|6.0 +|link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IDriver.html[`IDriver`] -- Method `.CloseAsync()` has been removed in favor of `.DisposeAsync()`. +|label:removed[] -|5.0 -|Class `Bookmark` is deprecated in favor of link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.Bookmarks.html[`Bookmarks`]. -|label:deprecated[] +|6.0 +|**Bookmarks** -|5.0 -|link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.SessionConfigBuilder.html[`SessionConfigBuilder`] -- Method link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.SessionConfigBuilder.WithBookmarks.html#Neo4j_Driver_SessionConfigBuilder_WithBookmarks_Neo4j_Driver_Bookmarks___[`.WithBookmarks()`] takes `Bookmarks[]` as argument rather than `Bookmark[]`. -|label:deprecated[] +- `ISession`, `IRxSession` link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncSession.html[`IAsyncSession`] -- Method `.LastBookmark()` has been removed in favor of `.LastBookmarks()`. +- Class `Bookmark` has been removed in favor of link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Bookmarks.html[`Bookmarks`]. +- link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.SessionConfigBuilder.html[`SessionConfigBuilder`] -- Method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.SessionConfigBuilder.WithBookmarks.html#Neo4j_Driver_SessionConfigBuilder_WithBookmarks_Neo4j_Driver_Bookmarks___[`.WithBookmarks()`] takes `Bookmarks[]` as argument rather than `Bookmark[]`. +|label:removed[] // a backslash is needed in first url to avoid it becoming a with the subsequent link __ -|5.0 +|6.0 |`ZonedDateTime` -- Two constructors not including `DateTimeKind` are deprecated (link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ZonedDateTime.-ctor.html#Neo4j_Driver_ZonedDateTime_\_ctor_System_Int32_System_Int32_System_Int32_System_Int32_System_Int32_System_Int32_Neo4j_Driver_Zone_[this] and link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.ZonedDateTime.-ctor.html#Neo4j_Driver_ZonedDateTime_\_ctor_System_Int32_System_Int32_System_Int32_System_Int32_System_Int32_System_Int32_System_Int32_Neo4j_Driver_Zone_[this]). -|label:deprecated[] +|label:removed[] -|5.0 -|link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IEntity.html[`IEntity`], link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.INode.html[`INode`], link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IRelationship.html[`IRelationship`] -- Method `.Id()` (`long`) is deprecated in favor of `.ElementId()` (`string`). -|label:deprecated[] +|6.0 +|link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.INotification.html[`INotification`] -- Property `Severity` has been removed in favor of `SeverityLevel`/`RawSeverityLevel`. +|label:removed[] |5.0 -|link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.IRelationship.html[`IRelationship`] -- Methods `.StartNodeId()` and `.EndNodeId()` are deprecated in favor of `.StartNodeElementId()` and `.EndNodeElementId()`. Old identifiers were `long`, whereas new ElementIds are `string`. +|link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IEntity.html[`IEntity`], link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.INode.html[`INode`], link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IRelationship.html[`IRelationship`] -- Method `.Id()` (`long`) is deprecated in favor of `.ElementId()` (`string`). |label:deprecated[] -|5.7 -|link:https://neo4j.com/docs/api/dotnet-driver/{dotnet-driver-version}/api/Neo4j.Driver.INotification.html[`INotification`] -- Property `Severity` is deprecated in favor of `SeverityLevel` or `RawSeverityLevel`. +|5.0 +|link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IRelationship.html[`IRelationship`] -- Methods `.StartNodeId()` and `.EndNodeId()` are deprecated in favor of `.StartNodeElementId()` and `.EndNodeElementId()`. Old identifiers were `long`, whereas new ElementIds are `string`. |label:deprecated[] |=== diff --git a/dotnet-manual/modules/ROOT/partials/quickstart.adoc b/dotnet-manual/modules/ROOT/partials/quickstart.adoc index 4b2e0cdb..3e768c10 100644 --- a/dotnet-manual/modules/ROOT/partials/quickstart.adoc +++ b/dotnet-manual/modules/ROOT/partials/quickstart.adoc @@ -109,6 +109,6 @@ Unless you created them with the `using` keyword, remember to close all `IDriver [source, csharp, test-skip, copy=true] ---- -await session.CloseAsync(); -await driver.CloseAsync(); +await session.DisposeAsync(); +await driver.DisposeAsync(); ---- From e579d2c77d05defc9ca680c98c062bb8d7e697e4 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 29 Oct 2025 11:57:09 +0000 Subject: [PATCH 4/8] v6 --- preview.yml | 2 +- publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/preview.yml b/preview.yml index 03bf262c..b400ee15 100644 --- a/preview.yml +++ b/preview.yml @@ -14,6 +14,7 @@ content: - javascript-manual - python-manual - java-manual + - dotnet-manual - common-content exclude: - '!**/_includes/*' @@ -25,7 +26,6 @@ content: worktrees: true start_paths: - go-manual - - dotnet-manual exclude: - '!**/_includes/*' - '!**/readme.adoc' diff --git a/publish.yml b/publish.yml index 16e3c2a8..76d6fa68 100644 --- a/publish.yml +++ b/publish.yml @@ -13,6 +13,7 @@ content: - java-manual - javascript-manual - python-manual + - dotnet-manual - common-content exclude: - '!**/_includes/*' @@ -23,7 +24,6 @@ content: edit_url: https://github.com/neo4j/docs-drivers/tree/{refname}/{path} worktrees: true start_paths: - - dotnet-manual - go-manual exclude: - '!**/_includes/*' From 3be87e4237302e8bf8d48f4a39ee84e09320e660 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 29 Oct 2025 12:13:24 +0000 Subject: [PATCH 5/8] new types --- .../modules/ROOT/pages/data-types.adoc | 59 ++++--------------- 1 file changed, 11 insertions(+), 48 deletions(-) diff --git a/dotnet-manual/modules/ROOT/pages/data-types.adoc b/dotnet-manual/modules/ROOT/pages/data-types.adoc index 9741be59..a649106e 100644 --- a/dotnet-manual/modules/ROOT/pages/data-types.adoc +++ b/dotnet-manual/modules/ROOT/pages/data-types.adoc @@ -286,62 +286,25 @@ async Task addFriend(IDriver driver, string name, string status, string friendNa For full documentation, see link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IPath.html[API documentation -> IPath]. -//// [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`]. +The type link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Vector.html[`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`. +You can create vectors from lists of `float`, `double`, `sbyte`, `short`, `int`, `long`. [IMPORTANT] Storing `VECTOR` objects in the database requires a server version >= 2025.10, Enterprise Edition. -.Create, store, and retrieve a vector -[source, go] +.Create and store a vector +[source, csharp] ---- -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) -} +var vector = new Vector([5, 6, 7]); +var result = await driver.ExecutableQuery("MERGE (d:Doc {embedding: $vector})") + .WithParameters(new { vector = vector }) + .WithConfig(new QueryConfig(database: "")) + .ExecuteAsync(); ---- @@ -349,9 +312,9 @@ func main() { [#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. +The type link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.UnsupportedType.html[`UnsupportedType`] is used for data types returned by Cypher queries that the driver doesn't recognize. This happens when the client version is too old with respect to the server version. -//// + ifndef::backend-pdf[] From 489a58ebebe470ab51478ee195b0825e56322b1e Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 29 Oct 2025 12:27:15 +0000 Subject: [PATCH 6/8] Dispose of sessions --- dotnet-manual/modules/ROOT/pages/transactions.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet-manual/modules/ROOT/pages/transactions.adoc b/dotnet-manual/modules/ROOT/pages/transactions.adoc index b09f4667..3b7bd320 100644 --- a/dotnet-manual/modules/ROOT/pages/transactions.adoc +++ b/dotnet-manual/modules/ROOT/pages/transactions.adoc @@ -466,7 +466,7 @@ Each connection pool has *a finite number of sessions*, so if you open sessions It is thus recommended to create sessions with the `using` keyword, which automatically closes them when the scope ends. When a session is closed, it is returned to the connection pool to be later reused. -If you do not open sessions with `using`, remember to call the `.CloseAsync()` method when you have finished using them. +If you do not open sessions with `using`, remember to call the `.DisposeAsync()` method when you have finished using them. [source, csharp] ---- @@ -474,7 +474,7 @@ var session = driver.AsyncSession(conf => conf.WithDatabase("")); // session usage -await session.CloseAsync(); +await session.DisposeAsync(); ---- From 0062a58ee86dd7f1c1f83101711d7942883efe39 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Fri, 31 Oct 2025 09:01:41 +0000 Subject: [PATCH 7/8] broken formatting --- dotnet-manual/modules/ROOT/pages/query-advanced.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet-manual/modules/ROOT/pages/query-advanced.adoc b/dotnet-manual/modules/ROOT/pages/query-advanced.adoc index df2cce76..ad86d3f8 100644 --- a/dotnet-manual/modules/ROOT/pages/query-advanced.adoc +++ b/dotnet-manual/modules/ROOT/pages/query-advanced.adoc @@ -7,7 +7,7 @@ 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[`.ExecutableQuery()`] 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. -You run an implicit transaction with the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncSession.RunAsync.html[`ISessionAsync.RunAsync()]`. +You run an implicit transaction with the method link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IAsyncSession.RunAsync.html[`ISessionAsync.RunAsync()`]. It returns a link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.IResultCursor.html[`IResultCursor`] object that needs to be xref:transactions#process-result[processed accordingly]. [source, csharp] From f5995b10ff145feccb9ddd483014cc3a71f8f0b2 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 5 Nov 2025 10:45:18 +0100 Subject: [PATCH 8/8] link cypher 25 --- dotnet-manual/modules/ROOT/pages/data-types.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet-manual/modules/ROOT/pages/data-types.adoc b/dotnet-manual/modules/ROOT/pages/data-types.adoc index a649106e..2ab070f2 100644 --- a/dotnet-manual/modules/ROOT/pages/data-types.adoc +++ b/dotnet-manual/modules/ROOT/pages/data-types.adoc @@ -290,7 +290,7 @@ For full documentation, see link:https://neo4j.com/docs/api/dotnet-driver/curren [#vector] == Vector -The type link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Vector.html[`Vector`] maps to the Cypher type link:https://neo4j.com/docs/cypher-manual/current/values-and-types/vector/[`VECTOR`]. +The type link:https://neo4j.com/docs/api/dotnet-driver/current/api/Neo4j.Driver.Vector.html[`Vector`] maps to the Cypher type link:https://neo4j.com/docs/cypher-manual/25/values-and-types/vector/[`VECTOR`]. Vector objects are stored as contiguous blocks of memory, containing homogeneous values. You can create vectors from lists of `float`, `double`, `sbyte`, `short`, `int`, `long`.