From 83c777deb9bcf52b8e488fc928f93df03dc0f4cd Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Mon, 21 Nov 2022 18:07:39 +0000 Subject: [PATCH 001/383] Update antora.yml --- antora.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/antora.yml b/antora.yml index 120627a40..4d35395cc 100644 --- a/antora.yml +++ b/antora.yml @@ -1,10 +1,11 @@ name: cypher-manual title: Cypher Manual -version: '5.0' +version: '5' start_page: ROOT:index.adoc nav: - modules/ROOT/content-nav.adoc asciidoc: attributes: - neo4j-version: '5.0' - neo4j-version-exact: '5.0.0' + neo4j-version-short: '5' + neo4j-version: '5.3' + neo4j-version-exact: '5.3.0' From 38b90f08f55a5d866c3cd201d7668912988192b2 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Mon, 21 Nov 2022 18:08:25 +0000 Subject: [PATCH 002/383] Update publish.yml --- publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/publish.yml b/publish.yml index c4ada0857..01aafd1d5 100644 --- a/publish.yml +++ b/publish.yml @@ -24,7 +24,7 @@ urls: antora: extensions: - require: "@neo4j-antora/antora-modify-sitemaps" - sitemap_version: '5.0' + sitemap_version: '5' sitemap_loc_version: 'current' move_sitemaps_to_components: true @@ -55,4 +55,4 @@ asciidoc: check-mark: icon:check[] cross-mark: icon:times[] neo4j-base-uri: '' - neo4j-docs-base-uri: /docs \ No newline at end of file + neo4j-docs-base-uri: /docs From a14584a74cf93d019bf7af0bc736df43ecdc229c Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Tue, 22 Nov 2022 09:35:40 +0000 Subject: [PATCH 003/383] Update publish branches --- publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/publish.yml b/publish.yml index 01aafd1d5..da7a6def3 100644 --- a/publish.yml +++ b/publish.yml @@ -6,7 +6,7 @@ site: content: sources: - url: ./ - branches: ['4.4','dev'] + branches: ['3.5', '4.0', '4.1', '4.2', '4.3', '4.4','dev'] exclude: - '!**/_includes/*' - '!**/readme.adoc' From 4dcae65ac4e5566e94d015dd85be3acc14b36195 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Tue, 22 Nov 2022 09:56:16 +0000 Subject: [PATCH 004/383] Update link to browser manual (#219) --- modules/ROOT/pages/introduction/transactions.adoc | 2 +- preview.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/introduction/transactions.adoc b/modules/ROOT/pages/introduction/transactions.adoc index 0f4fb971f..cfd319b47 100644 --- a/modules/ROOT/pages/introduction/transactions.adoc +++ b/modules/ROOT/pages/introduction/transactions.adoc @@ -40,7 +40,7 @@ For examples of the API, or the commands used to start and commit transactions, * For information on using transactions with a Neo4j driver, see _The session API_ in the link:{docs-base-uri}[Neo4j Driver manuals]. * For information on using transactions over the HTTP API, see the link:{neo4j-docs-base-uri}/http-api/{page-version}/actions#http-api-actions[HTTP API documentation -> Using the HTTP API]. * For information on using transactions within the embedded Core API, see the link:{neo4j-docs-base-uri}/java-reference/{page-version}/java-embedded/cypher-java#cypher-java[Java Reference -> Executing Cypher queries from Java]. -* For information on using transactions within the Neo4j Browser or Cypher-shell, see the link:{neo4j-docs-base-uri}/browser-manual/{current-version}/reference-commands/[Neo4j Browser documentation] or the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tools/cypher-shell/#cypher-shell-commands[Cypher-shell documentation]. +* For information on using transactions within the Neo4j Browser or Cypher-shell, see the link:{neo4j-docs-base-uri}/browser-manual/current/reference-commands/[Neo4j Browser documentation] or the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tools/cypher-shell/#cypher-shell-commands[Cypher-shell documentation]. When writing procedures or using Neo4j embedded, remember that all iterators returned from an execution result should be either fully exhausted or closed. This ensures that the resources bound to them are properly released. diff --git a/preview.yml b/preview.yml index 36da307be..41c1f9d44 100644 --- a/preview.yml +++ b/preview.yml @@ -24,7 +24,7 @@ urls: antora: extensions: - require: "@neo4j-antora/antora-modify-sitemaps" - sitemap_version: '5.0' + sitemap_version: '5' sitemap_loc_version: 'current' move_sitemaps_to_components: true From 97d220d3d720726808479797b0d6ed9fe19d084a Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Tue, 22 Nov 2022 10:13:47 +0000 Subject: [PATCH 005/383] Fail verification if log contains warnings (#220) --- .github/workflows/docs-pr.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-pr.yml b/.github/workflows/docs-pr.yml index 0c44b0159..64cfb0c90 100644 --- a/.github/workflows/docs-pr.yml +++ b/.github/workflows/docs-pr.yml @@ -7,7 +7,8 @@ name: "Verify PR" on: pull_request: branches: - - "4.4" + - "3.5" + - "4.[0-9]" - "5.0" - "5.x" - "dev" @@ -26,3 +27,5 @@ jobs: docs-verify-for-pr: needs: docs-build-for-pr uses: recrwplay/actions-demo/.github/workflows/reusable-docs-verify.yml@main + with: + failOnWarnings: true \ No newline at end of file From 6c12943027c24a2edd5cd386c323065092ab163e Mon Sep 17 00:00:00 2001 From: AlexicaWright <49636617+AlexicaWright@users.noreply.github.com> Date: Tue, 22 Nov 2022 14:12:02 +0100 Subject: [PATCH 006/383] added dryrun to list of additions and a note about 5.2 --- .../ROOT/pages/access-control/manage-servers.adoc | 5 +++++ ...eprecations-additions-removals-compatibility.adoc | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index 1692ba52a..8e8011769 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -352,6 +352,11 @@ Using `DRYRUN REALLOCATE DATABASE` returns a view of how the databases would hav | "db3" | "server-1" | "00000000-94ff-4ede-87be-3d741b795480" | "server-5" | "00000003-0df7-4057-81fd-1cf43c9ef5f7" | "primary" |=== +[NOTE] +==== +`DRYRUN` is introduced in Neo4j 5.2 and thus does not work in previous versions. +==== + [[server-management-deallocate]] == Deallocate databases diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index cbfe9834f..9e3460e10 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -1,5 +1,5 @@ [[cypher-deprecations-additions-removals-compatibility]] -= Deprecations, additions and compatibility += Deprecations, additions, and compatibility :description: all of the features that have been removed, deprecated, added, or extended in different Cypher versions. @@ -32,6 +32,16 @@ a| Creating composite databases now allows for an empty options clause. There are no applicable option values for composite databases. +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +DRYRUN +---- + +a| To preview of the result of a command without executing it, prepend the command with `DRYRUN`. + |=== [[cypher-deprecations-additions-removals-5.1]] From d9e1ff3668014492769d38d13f8fcea05be4898c Mon Sep 17 00:00:00 2001 From: AlexicaWright <49636617+AlexicaWright@users.noreply.github.com> Date: Tue, 22 Nov 2022 14:56:27 +0100 Subject: [PATCH 007/383] Update modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- .../pages/deprecations-additions-removals-compatibility.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 9e3460e10..c8b3897b7 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -40,7 +40,7 @@ label:updated[] DRYRUN ---- -a| To preview of the result of a command without executing it, prepend the command with `DRYRUN`. +a| To preview the result of a command without executing it, prepend the command with `DRYRUN`. |=== From 348cab703cba34c7930b2ad994a4677c8dd4c811 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Tue, 22 Nov 2022 14:26:27 +0000 Subject: [PATCH 008/383] Use short form of version attribute for index page (#221) --- antora.yml | 4 ++-- modules/ROOT/pages/index.adoc | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/antora.yml b/antora.yml index 4d35395cc..ac8acc752 100644 --- a/antora.yml +++ b/antora.yml @@ -6,6 +6,6 @@ nav: - modules/ROOT/content-nav.adoc asciidoc: attributes: - neo4j-version-short: '5' - neo4j-version: '5.3' + neo4j-version: '5' + neo4j-version-minor: '5.3' neo4j-version-exact: '5.3.0' diff --git a/modules/ROOT/pages/index.adoc b/modules/ROOT/pages/index.adoc index 0735a5dc9..4db5dac8b 100644 --- a/modules/ROOT/pages/index.adoc +++ b/modules/ROOT/pages/index.adoc @@ -2,7 +2,7 @@ [[cypher-manual]] = The Neo4j Cypher Manual v{neo4j-version} -:neo4j-buildnumber: {neo4j-version} +:neo4j-buildnumber: {neo4j-version-minor} Cypher is Neo4j's graph query language that allows users to store and retrieve data from the graph database. It is a declarative, SQL-inspired language for describing visual patterns in graphs. @@ -11,12 +11,12 @@ The syntax provides a visual and logical way to match patterns of nodes and rela == Documentation updates for Neo4j 5 -Neo4j v5 includes a number of new features and updates. +Neo4j {neo4j-version} includes a number of new features and updates. A highlight of these include: * Cypher syntax improvements with Graph Pattern Matching (relationships and labels): + -** In `MATCH` clauses, `WHERE` can be placed inside a relationship pattern to filter relationships. +** In `MATCH` clauses, `WHERE` can be placed inside a relationship pattern to filter relationships. ** In `MATCH` clauses, nodes and relationships can be filtered using more sophisticated label (type) expressions. ** Simpler alternative syntax to navigate and traverse graphs using the following operators: *** `&`: logical `AND` @@ -30,7 +30,7 @@ For more information, see the xref:syntax/expressions.adoc#label-expressions[sec * New `elementID` for graph objects: + New IDs are introduced to uniquely identify graph elements in Neo4j databases. -Node ID will exist with each release of Neo4j v5. +Node ID will exist with each release of Neo4j {neo4j-version}. + For more information, see xref:functions/scalar.adoc#functions-elementid[`elementId()`]. From 5a6d6bdc92613185b86846dfcf322e45967e7559 Mon Sep 17 00:00:00 2001 From: AlexicaWright <49636617+AlexicaWright@users.noreply.github.com> Date: Tue, 22 Nov 2022 15:42:56 +0100 Subject: [PATCH 009/383] addressed PR comments --- modules/ROOT/pages/access-control/manage-servers.adoc | 3 +-- .../pages/deprecations-additions-removals-compatibility.adoc | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index 8e8011769..8ab5044ef 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -354,7 +354,7 @@ Using `DRYRUN REALLOCATE DATABASE` returns a view of how the databases would hav [NOTE] ==== -`DRYRUN` is introduced in Neo4j 5.2 and thus does not work in previous versions. +`DRYRUN` is introduced in Neo4j 5.2, and thus is not available in earlier minor releases ov v5. ==== [[server-management-deallocate]] @@ -375,7 +375,6 @@ Using `DRYRUN DEALLOCATE DATABASES FROM 'server-1', 'server-2'` returns a view o [options="header,footer", width="100%", cols="m,m,m,m,m,m"] |=== |database|fromServerName|fromServerId|toServerName|toServerId|mode - | "db1" | "server-1" | "00000001-8c04-4731-a2fd-7b0289c511ce" | "server-4" | "00000002-5b91-43c1-8b25-5289f674563e" | "primary" | "db1" | "server-2" | "00000000-7e53-427c-a987-24634c4745f3" | "server-5" | "00000003-0e98-44c8-9844-f0a4eb95b0d8" | "primary" |=== diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 9e3460e10..856b81844 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -37,10 +37,10 @@ label:functionality[] label:updated[] [source, cypher, role="noheader"] ---- -DRYRUN +DRYRUN REALLOCATE\|DEALLOCATE DATABASES FROM ---- -a| To preview of the result of a command without executing it, prepend the command with `DRYRUN`. +a| To preview of the result of either `REALLOCATE` or `DEALLOCATE` without executing, prepend the command with `DRYRUN`. |=== From 881652fd2c3b931dd3c33e3f59cdecf0713a0062 Mon Sep 17 00:00:00 2001 From: David Oliver Date: Tue, 22 Nov 2022 15:44:17 +0000 Subject: [PATCH 010/383] Add deprecation for duplicated variable length relationships in 5.2 (#212) Adds the deprecation from https://github.com/neo4j/neo4j-documentation/pull/1625 --- ...ions-additions-removals-compatibility.adoc | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 32ecdbde2..91223d161 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -13,6 +13,33 @@ Replacement syntax for deprecated and removed features are also indicated. [[cypher-deprecations-additions-removals-5.2]] == Version 5.2 +=== Deprecated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:syntax[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +MATCH ()-[r*]-()-[r*]-() +---- +---- +MATCH p = ()-[r*]-(), q = ()-[r*]-() +---- + +---- +MATCH ()-[r*]-() MATCH ()-[r*]-() +---- +a| + +The use of the same relationship variable for multiple variable length relationships is deprecated. + +|=== + === Updated features [cols="2", options="header"] @@ -73,7 +100,7 @@ CREATE TEXT INDEX ... OPTIONS {indexProvider: `text-2.0`} ---- a| -A new text index provider is available, `text-2.0`. +A new text index provider is available, `text-2.0`. This is also the default provider if none is given. |=== From deb718bfcc6020776e0e63ed014f73bf10ae67ed Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Tue, 22 Nov 2022 17:04:53 +0000 Subject: [PATCH 011/383] Add kroki extension to playbook (#224) --- publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/publish.yml b/publish.yml index da7a6def3..da9776f8f 100644 --- a/publish.yml +++ b/publish.yml @@ -35,6 +35,7 @@ asciidoc: - "@neo4j-antora/antora-add-notes" - "@neo4j-antora/antora-page-roles" - "@neo4j-antora/antora-table-footnotes" + - asciidoctor-kroki attributes: page-theme: docs page-type: Docs From 9c042c0fef9e0fed78d0f5a1cff42d7a6db03dff Mon Sep 17 00:00:00 2001 From: AlexicaWright <49636617+AlexicaWright@users.noreply.github.com> Date: Wed, 23 Nov 2022 08:57:58 +0100 Subject: [PATCH 012/383] changed label to new --- .../pages/deprecations-additions-removals-compatibility.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 856b81844..b41bab7d5 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -34,7 +34,7 @@ There are no applicable option values for composite databases. a| label:functionality[] -label:updated[] +label:new[] [source, cypher, role="noheader"] ---- DRYRUN REALLOCATE\|DEALLOCATE DATABASES FROM From 4588dab3d3f59927cd56c965c67ed9b01ba2e295 Mon Sep 17 00:00:00 2001 From: Arne Fischereit Date: Thu, 10 Nov 2022 16:00:40 +0100 Subject: [PATCH 013/383] Mark legacy relationship types as deprecated. --- ...recations-additions-removals-compatibility.adoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 928eb7b7a..bb24b5b94 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -661,6 +661,20 @@ Creating a database with unescaped dots in the name has been deprecated, instead CREATE DATABASE `databaseName.withDot` ... ---- +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +()-[:A\|:B]->() +---- +a| +Replaced by: +[source, cypher, role="noheader"] +---- +()-[:A\|B]->() +---- + |=== === Updated features From f6d2fd2c70cae08f56984b5d3e03384f4eff8460 Mon Sep 17 00:00:00 2001 From: Tobias Johansson Date: Wed, 2 Nov 2022 10:59:16 +0100 Subject: [PATCH 014/383] Add note about the behavior of the id function on composite databases --- modules/ROOT/pages/functions/scalar.adoc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/ROOT/pages/functions/scalar.adoc b/modules/ROOT/pages/functions/scalar.adoc index dd957ae9b..a9adec0f4 100644 --- a/modules/ROOT/pages/functions/scalar.adoc +++ b/modules/ROOT/pages/functions/scalar.adoc @@ -372,6 +372,18 @@ Every relationship in a database has an identifier. The identifier for a relationship is guaranteed to be unique among other relationships' identifiers in the same database, within the scope of a single transaction. ==== +[NOTE] +==== +On <> the `id()` function should be used with caution. +It is recommended to use <> instead. + +When called in database-specific subqueries, the resulting id value for a node or relationship is local to that database. +The local id for nodes or relationships from different databases may be the same. + +When called from the root context of a query, the resulting value is an extended id for the node or relationship. +The extended id is likely different from the local id for the same node or relationship. +==== + *Syntax:* [source, syntax, role="noheader"] From 121a5db7baf41ba3abe2ea6ad645e7b5f13cbbef Mon Sep 17 00:00:00 2001 From: Tobias Johansson Date: Wed, 23 Nov 2022 16:37:53 +0100 Subject: [PATCH 015/383] Address review comment --- modules/ROOT/pages/functions/scalar.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/functions/scalar.adoc b/modules/ROOT/pages/functions/scalar.adoc index a9adec0f4..1e26a0db2 100644 --- a/modules/ROOT/pages/functions/scalar.adoc +++ b/modules/ROOT/pages/functions/scalar.adoc @@ -374,7 +374,7 @@ The identifier for a relationship is guaranteed to be unique among other relatio [NOTE] ==== -On <> the `id()` function should be used with caution. +On a <>, the `id()` function should be used with caution. It is recommended to use <> instead. When called in database-specific subqueries, the resulting id value for a node or relationship is local to that database. From d168ac073d1b7a485bba53d6fa987099269ef9f5 Mon Sep 17 00:00:00 2001 From: Gemma Lamont Date: Thu, 17 Nov 2022 15:22:42 +0100 Subject: [PATCH 016/383] Update documentation for Exists and Count subqueries --- .../images/graph_expression_subqueries.svg | 119 ++++++ ...ions-additions-removals-compatibility.adoc | 38 ++ modules/ROOT/pages/syntax/expressions.adoc | 346 ++++++++++++++---- 3 files changed, 425 insertions(+), 78 deletions(-) create mode 100644 modules/ROOT/images/graph_expression_subqueries.svg diff --git a/modules/ROOT/images/graph_expression_subqueries.svg b/modules/ROOT/images/graph_expression_subqueries.svg new file mode 100644 index 000000000..b037298a9 --- /dev/null +++ b/modules/ROOT/images/graph_expression_subqueries.svg @@ -0,0 +1,119 @@ + + + + + + +L + + + +Andy + +Swedish, Person + +age = 36 +name = 'Andy' + + + +DogAndy + +Dog + +name = 'Andy' + + + +Andy->DogAndy + + +  HAS_DOG +since = 2016 + + + +Timothy + +Person + +age = 25 +name = 'Timothy' + + + +Mittens + +Cat + +name = 'Mittens' + + + +Timothy->Mittens + + +  HAS_CAT +since = 2019 + + + +Peter + +Person + +age = 35 +name = 'Peter' + + + +Ozzy + +Dog + +name = 'Ozzy' + + + +Peter->Ozzy + + +  HAS_DOG +since = 2018 + + + +Fido + +Dog + +name = 'Fido' + + + +Peter->Fido + + +  HAS_DOG +since = 2010 + + + +Banana + +Toy + +name = 'Banana' + + + +Fido->Banana + + +  HAS_TOY + + + diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index bb24b5b94..407f5c7fd 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -9,6 +9,44 @@ New features are added to the language continuously, and occasionally, some feat This section lists all of the features that have been removed, deprecated, added, or extended in different Cypher versions. Replacement syntax for deprecated and removed features are also indicated. +[[cypher-deprecations-additions-removals-5.3]] +== Version 5.3 + +=== Updated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +EXISTS { + ... +} +---- +a| + +An `EXISTS` subquery now supports any non-writing query. For example, it now supports `UNION` and `CALL` clauses. + + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +COUNT { + ... +} +---- +a| + +A `COUNT` subquery now supports any non-writing query. For example, it now supports `UNION` and `CALL` clauses. + +|=== [[cypher-deprecations-additions-removals-5.2]] == Version 5.2 diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index fbcc73274..56c09f9f0 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -437,73 +437,32 @@ Variables introduced inside the subquery are not part of the outside scope and t The following graph is used for the examples below: -.Graph -["dot", "Subquery expressions-1.svg", "neoviz", ""] ----- - N0 [ - label = "{Swedish, Person|age = 36\lname = \'Andy\'\l}" - ] - N0 -> N3 [ - color = "#2e3436" - fontcolor = "#2e3436" - label = "HAS_DOG\nsince = 2016\l" - ] - N1 [ - label = "{Person|age = 25\lname = \'Timothy\'\l}" - ] - N2 [ - label = "{Person|age = 35\lname = \'Peter\'\l}" - ] - N2 -> N5 [ - color = "#2e3436" - fontcolor = "#2e3436" - label = "HAS_DOG\nsince = 2018\l" - ] - N2 -> N4 [ - color = "#2e3436" - fontcolor = "#2e3436" - label = "HAS_DOG\nsince = 2010\l" - ] - N3 [ - label = "{Dog|name = \'Andy\'\l}" - ] - N4 [ - label = "{Dog|name = \'Fido\'\l}" - ] - N4 -> N6 [ - color = "#4e9a06" - fontcolor = "#4e9a06" - label = "HAS_TOY\n" - ] - N5 [ - label = "{Dog|name = \'Ozzy\'\l}" - ] - N6 [ - label = "{Toy|name = \'Banana\'\l}" - ] - ----- - +//// +CREATE +(andy:Swedish:Person {name: 'Andy', age: 36}), +(timothy:Person {name: 'Timothy', age: 25}), +(peter:Person {name: 'Peter', age: 35}), +(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) +//// + +image:graph_expression_subqueries.svg[] [[existential-subqueries]] === `EXISTS` subqueries -An `EXISTS` subquery can be used to find out if a specified pattern exists at least once in the data. -It serves the same purpose as a <> but is more powerful because it allows you to use `MATCH` and `WHERE` clauses internally. -Moreover, it can appear in any expression position, unlike path patterns. +An `EXISTS` subquery can be used to find out if at least one row is returned by the given query. +It is more powerful than using a path pattern as it allows full queries to be executed. If the subquery evaluates to at least one row, the whole expression will become true. This also means that the system only needs to evaluate if there is at least one row and can skip the rest of the work. +Any non-updating query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +as any variable defined within the subquery will not be available outside of the expression, even if a final `RETURN` clause is used. -*Syntax:* -[source, cypher, role=noplay] -EXISTS { - MATCH [Pattern] - WHERE [Expression] -} - - -It is worth noting that the `MATCH` keyword can be omitted in such subqueries and that the `WHERE` clause is optional. +It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `EXISTS` consists of only +a Pattern and an optional `WHERE` clause. [[existential-subquery-simple-case]] ==== Simple `EXISTS` subquery @@ -518,7 +477,7 @@ The following example shows this: ---- MATCH (person:Person) WHERE EXISTS { - MATCH (person)-[:HAS_DOG]->(:Dog) + (person)-[:HAS_DOG]->(:Dog) } RETURN person.name AS name ---- @@ -543,13 +502,14 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) ]]>(:Dog) + (person)-[:HAS_DOG]->(:Dog) } RETURN person.name AS name ]]> @@ -593,6 +553,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -648,6 +609,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -702,6 +664,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -714,21 +677,126 @@ RETURN person.name AS name, EXISTS { ++++ endif::nonhtmloutput[] +[[existential-subquery-with-union]] +==== `EXISTS` subquery with a `UNION` +`Exists` can be used with a `UNION` clause, and the `RETURN` clauses are not required. +Here is the result that shows if at least one row is returned from the union of 2 queries. + +.Query +[source, cypher] +---- +MATCH (person:Person) +RETURN + person.name AS name, + EXISTS { + MATCH (person)-[:HAS_DOG]->(:Dog) + UNION + MATCH (person)-[:HAS_CAT]->(:Cat) + } AS hasPet +---- + +.Result +[role="queryresult",options="header,footer",cols="2* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(:Dog) + UNION + MATCH (person)-[:HAS_CAT]->(:Cat) + } AS hasPet +]]> +++++ +endif::nonhtmloutput[] + +[[existential-subquery-with-with]] +==== `EXISTS` subquery with `WITH` +Variables from the outside scope will not go out of scope when using a `WITH`, they are visible for the entirety of the subquery, +this means that shadowing of these variables is also not allowed. +Here is an example where a `WITH` clause introduces a new variable, and the outer scope variable person being referenced is still available after: + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE EXISTS { + WITH "Ozzy" AS dogName + MATCH (person)-[:HAS_DOG]->(d:Dog) + WHERE d.name = dogName +} +RETURN person.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(d:Dog) + WHERE d.name = dogName +} +RETURN person.name AS name +]]> +++++ +endif::nonhtmloutput[] + [[count-subqueries]] === `COUNT` subqueries A `COUNT` subquery expression can be used to count the number of results of the subquery. -*Syntax:* -[source, cypher, role=noplay] -COUNT { - MATCH [Pattern] - WHERE [Expression] -} - +Any non-updating query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +as any variable defined within the subquery will not be available outside of the expression, +even if a final `RETURN` clause is used. One exception to this is that for `DISTINCT UNIONS` the `RETURN` is still mandatory. -It is worth noting that the `MATCH` keyword can be omitted in such subqueries and that the `WHERE` clause is optional. +It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `COUNT` consists of only a Pattern and an optional `WHERE` clause. [[count-subquery-simple-case]] ==== Simple `COUNT` subquery @@ -765,6 +833,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -813,6 +882,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -827,6 +897,122 @@ RETURN person.name AS name ++++ endif::nonhtmloutput[] +[[count-subquery-with-union]] +==== `COUNT` subquery with a `UNION` + +`COUNT` can be used with a `UNION` clause, if the `UNION` clause is distinct, the `RETURN` clause is required. +Here is the result that shows the count of pets each person has by using a `UNION`: + +.Query +[source, cypher] +---- +MATCH (person:Person) +RETURN + person.name AS name, + COUNT { + MATCH (person)-[:HAS_DOG]->(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS numPets +---- + +.Result +[role="queryresult",options="header,footer",cols="2* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS numPets +]]> +++++ +endif::nonhtmloutput[] + +[[count-subquery-with-with]] +==== `COUNT` subquery with `WITH` + +Variables from the outside scope will not go out of scope when using a `WITH`, +they are visible for the entirety of the subquery, this means that shadowing of these variables is also not allowed. +Here is an example where a `WITH` clause introduces a new variable, +and the outer scope variable person being referenced is still available after: + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE COUNT { + WITH "Ozzy" AS dogName + MATCH (person)-[:HAS_DOG]->(d:Dog) + WHERE d.name = dogName +} = 1 +RETURN person.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(d:Dog) + WHERE d.name = dogName +} = 1 +RETURN person.name AS name +]]> +++++ +endif::nonhtmloutput[] + [[count-subqueries-other-clauses]] ==== Using `COUNT` subqueries inside other clauses @@ -842,7 +1028,7 @@ See a few examples below: ---- MATCH (person:Person) RETURN person.name, COUNT { (person)-[:HAS_DOG]->(:Dog) } as howManyDogs - + ---- .Result @@ -866,13 +1052,14 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) ]]>(:Dog) } as howManyDogs - + ]]> ++++ endif::nonhtmloutput[] @@ -887,7 +1074,7 @@ endif::nonhtmloutput[] MATCH (person:Person) WHERE person.name ="Andy" SET person.howManyDogs = COUNT { (person)-[:HAS_DOG]->(:Dog) } RETURN person.howManyDogs as howManyDogs - + ---- .Result @@ -910,6 +1097,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -917,7 +1105,7 @@ CREATE MATCH (person:Person) WHERE person.name ="Andy" SET person.howManyDogs = COUNT { (person)-[:HAS_DOG]->(:Dog) } RETURN person.howManyDogs as howManyDogs - + ]]> ++++ endif::nonhtmloutput[] @@ -935,7 +1123,7 @@ RETURN WHEN COUNT { (person)-[:HAS_DOG]->(:Dog) } > 1 THEN "Doglover " + person.name ELSE person.name END AS result - + ---- .Result @@ -959,6 +1147,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -969,7 +1158,7 @@ RETURN WHEN COUNT { (person)-[:HAS_DOG]->(:Dog) } > 1 THEN "Doglover " + person.name ELSE person.name END AS result - + ]]> ++++ endif::nonhtmloutput[] @@ -988,7 +1177,7 @@ MATCH (person:Person) RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs, avg(person.age) AS averageAge ORDER BY numDogs - + ---- .Result @@ -1012,6 +1201,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -1020,7 +1210,7 @@ MATCH (person:Person) RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs, avg(person.age) AS averageAge ORDER BY numDogs - + ]]> ++++ endif::nonhtmloutput[] @@ -1685,7 +1875,7 @@ RETURN n:A&B == Relationship type expressions Relationship type expressions evaluate to `true` or `false` when applied to the type of a relationship. - + Assuming no other filters are applied, then a relationship type expression evaluating to `true` means the relationship is matched. [IMPORTANT] From c43af34c8df5756ab4fb998ec9c7590d608e80e0 Mon Sep 17 00:00:00 2001 From: Gemma Lamont Date: Fri, 18 Nov 2022 11:36:17 +0100 Subject: [PATCH 017/383] PR Review updates --- modules/ROOT/pages/syntax/expressions.adoc | 129 +++++++++++++++++++-- modules/ROOT/pages/syntax/index.adoc | 3 + 2 files changed, 122 insertions(+), 10 deletions(-) diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index 56c09f9f0..0eebfdd24 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -454,21 +454,21 @@ image:graph_expression_subqueries.svg[] === `EXISTS` subqueries An `EXISTS` subquery can be used to find out if at least one row is returned by the given query. -It is more powerful than using a path pattern as it allows full queries to be executed. +It is more powerful than using a xref::clauses/where.adoc#filter-on-patterns[path pattern] as it allows full queries to be executed. If the subquery evaluates to at least one row, the whole expression will become true. This also means that the system only needs to evaluate if there is at least one row and can skip the rest of the work. -Any non-updating query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +Any non-writing query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted as any variable defined within the subquery will not be available outside of the expression, even if a final `RETURN` clause is used. It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `EXISTS` consists of only -a Pattern and an optional `WHERE` clause. +a pattern and an optional `WHERE` clause. [[existential-subquery-simple-case]] ==== Simple `EXISTS` subquery Variables introduced by the outside scope can be used in the `EXISTS` subquery without importing them, -unlike the case for `CALL` subqueries, <>. +unlike the case for `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[where they require importing]. The following example shows this: @@ -679,7 +679,9 @@ endif::nonhtmloutput[] [[existential-subquery-with-union]] ==== `EXISTS` subquery with a `UNION` + `Exists` can be used with a `UNION` clause, and the `RETURN` clauses are not required. +It is worth noting that if one branch has a `RETURN` clause, then all branches require one. Here is the result that shows if at least one row is returned from the union of 2 queries. .Query @@ -735,8 +737,10 @@ endif::nonhtmloutput[] [[existential-subquery-with-with]] ==== `EXISTS` subquery with `WITH` + Variables from the outside scope will not go out of scope when using a `WITH`, they are visible for the entirety of the subquery, this means that shadowing of these variables is also not allowed. +Shadowing of an outside scope variable is when a newly introduced variable inside the inner scope is defined with the same name as one from the outside scope. Here is an example where a `WITH` clause introduces a new variable, and the outer scope variable person being referenced is still available after: .Query @@ -786,23 +790,75 @@ RETURN person.name AS name ++++ endif::nonhtmloutput[] +[[existential-subquery-with-return]] +==== `EXISTS` subquery with `RETURN` + +`EXISTS` subqueries do not require a `RETURN` clause at the end of the subquery, but if one is present, it does not +need to be aliased, a difference from xref::clauses/call-subquery.adoc[`CALL` subqueries]. +Any variables returned in an `EXISTS` subquery will not be available after the subquery. + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE EXISTS { + MATCH (person)-[:HAS_DOG]->(:Dog) + RETURN person.name +} +RETURN person.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(:Dog) + RETURN person.name +} +RETURN person.name AS name +]]> +++++ +endif::nonhtmloutput[] + [[count-subqueries]] === `COUNT` subqueries -A `COUNT` subquery expression can be used to count the number of results of the subquery. - +A `COUNT` subquery expression can be used to count the number of rows returned by the subquery. -Any non-updating query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +Any non-writing query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted as any variable defined within the subquery will not be available outside of the expression, -even if a final `RETURN` clause is used. One exception to this is that for `DISTINCT UNIONS` the `RETURN` is still mandatory. +even if a final `RETURN` clause is used. One exception to this is that for a `DISTINCT UNION` the `RETURN` is still mandatory. -It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `COUNT` consists of only a Pattern and an optional `WHERE` clause. +It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `COUNT` consists of only a pattern and an optional `WHERE` clause. [[count-subquery-simple-case]] ==== Simple `COUNT` subquery Variables introduced by the outside scope can be used in the `COUNT` subquery without importing them, -unlike the case for `CALL` subqueries, <>. +unlike the case for `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[where they require importing]. The following query exemplifies this and outputs the owners of more than one dog: @@ -901,6 +957,7 @@ endif::nonhtmloutput[] ==== `COUNT` subquery with a `UNION` `COUNT` can be used with a `UNION` clause, if the `UNION` clause is distinct, the `RETURN` clause is required. +`UNION ALL` clauses do not require the `RETURN` clause, however, it is worth noting that if one branch has a `RETURN` clause, then all require one. Here is the result that shows the count of pets each person has by using a `UNION`: .Query @@ -963,6 +1020,7 @@ endif::nonhtmloutput[] Variables from the outside scope will not go out of scope when using a `WITH`, they are visible for the entirety of the subquery, this means that shadowing of these variables is also not allowed. +Shadowing of an outside scope variable is when a newly introduced variable inside the inner scope is defined with the same name as one from the outside scope. Here is an example where a `WITH` clause introduces a new variable, and the outer scope variable person being referenced is still available after: @@ -1215,6 +1273,57 @@ RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs, ++++ endif::nonhtmloutput[] +[[count-subquery-with-return]] +==== `COUNT` subquery with `RETURN` + +`COUNT` subqueries do not require a `RETURN` clause at the end of the subquery, but if one is present, it does not +need to be aliased, a difference from xref::clauses/call-subquery.adoc[`CALL` subqueries]. +Any variables returned in a `COUNT` subquery will not be available after the subquery. + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE COUNT { + MATCH (person)-[:HAS_DOG]->(:Dog) + RETURN person.name +} = 1 +RETURN person.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(:Dog) + RETURN person.name +} = 1 +RETURN person.name AS name +]]> +++++ +endif::nonhtmloutput[] [[label-expressions]] == Label expressions diff --git a/modules/ROOT/pages/syntax/index.adoc b/modules/ROOT/pages/syntax/index.adoc index 4a6b2dc25..e0a0e5255 100644 --- a/modules/ROOT/pages/syntax/index.adoc +++ b/modules/ROOT/pages/syntax/index.adoc @@ -8,8 +8,11 @@ * xref::syntax/expressions.adoc[Expressions] ** xref::syntax/expressions.adoc#cypher-expressions-general[Expressions in general] ** xref::syntax/expressions.adoc#cypher-expressions-string-literals[Note on string literals] + ** xref::syntax/expressions.adoc#cypher-expressions-number-literals[Note on number literals] ** xref::syntax/expressions.adoc#query-syntax-case[`CASE` Expressions] + ** xref::syntax/expressions.adoc#cypher-subquery-expressions[Subquery expressions] ** xref::syntax/expressions.adoc#label-expressions[Label expressions] + ** xref::syntax/expressions.adoc#relationship-type-expressions[Relationship type expressions] * xref::syntax/variables.adoc[Variables] * xref::syntax/reserved.adoc[Reserved keywords] * xref::syntax/parameters.adoc[Parameters] From 66d5e40e6dd74bed0b50752ecd73aaf4671516ff Mon Sep 17 00:00:00 2001 From: Gemma Lamont Date: Mon, 21 Nov 2022 09:53:12 +0100 Subject: [PATCH 018/383] Add COUNT and EXISTS as submenus in menu --- modules/ROOT/pages/syntax/index.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ROOT/pages/syntax/index.adoc b/modules/ROOT/pages/syntax/index.adoc index e0a0e5255..19e536fb7 100644 --- a/modules/ROOT/pages/syntax/index.adoc +++ b/modules/ROOT/pages/syntax/index.adoc @@ -11,6 +11,8 @@ ** xref::syntax/expressions.adoc#cypher-expressions-number-literals[Note on number literals] ** xref::syntax/expressions.adoc#query-syntax-case[`CASE` Expressions] ** xref::syntax/expressions.adoc#cypher-subquery-expressions[Subquery expressions] + *** xref::syntax/expressions.adoc#existential-subqueries[`EXISTS` subqueries] + *** xref::syntax/expressions.adoc#count-subqueries[`COUNT` subqueries] ** xref::syntax/expressions.adoc#label-expressions[Label expressions] ** xref::syntax/expressions.adoc#relationship-type-expressions[Relationship type expressions] * xref::syntax/variables.adoc[Variables] From 34fd359c6a29c6b6c92f36fecb94267cc0c01826 Mon Sep 17 00:00:00 2001 From: Gemma Lamont Date: Fri, 25 Nov 2022 10:32:14 +0100 Subject: [PATCH 019/383] PR review updates --- modules/ROOT/pages/syntax/expressions.adoc | 53 +++++++++++----------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index 0eebfdd24..5ca7b3329 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -458,17 +458,17 @@ It is more powerful than using a xref::clauses/where.adoc#filter-on-patterns[pat If the subquery evaluates to at least one row, the whole expression will become true. This also means that the system only needs to evaluate if there is at least one row and can skip the rest of the work. -Any non-writing query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +Any non-writing query is allowed. `EXISTS` subqueries differ from regular queries in that the final `RETURN` clause may be omitted, as any variable defined within the subquery will not be available outside of the expression, even if a final `RETURN` clause is used. -It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `EXISTS` consists of only +It is worth noting that the `MATCH` keyword can be omitted in subqueries in cases where the `EXISTS` consists of only a pattern and an optional `WHERE` clause. [[existential-subquery-simple-case]] ==== Simple `EXISTS` subquery -Variables introduced by the outside scope can be used in the `EXISTS` subquery without importing them, -unlike the case for `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[where they require importing]. +Variables introduced by the outside scope can be used in the `EXISTS` subquery without importing them. +In this regard, `EXISTS` subqueries are different from `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[which do require importing]. The following example shows this: @@ -682,7 +682,7 @@ endif::nonhtmloutput[] `Exists` can be used with a `UNION` clause, and the `RETURN` clauses are not required. It is worth noting that if one branch has a `RETURN` clause, then all branches require one. -Here is the result that shows if at least one row is returned from the union of 2 queries. +The below example demonstrates that if one of the `UNION` branches was to return at least one row, the entire `EXISTS` expression will evaluate to true. .Query [source, cypher] @@ -738,10 +738,11 @@ endif::nonhtmloutput[] [[existential-subquery-with-with]] ==== `EXISTS` subquery with `WITH` -Variables from the outside scope will not go out of scope when using a `WITH`, they are visible for the entirety of the subquery, -this means that shadowing of these variables is also not allowed. -Shadowing of an outside scope variable is when a newly introduced variable inside the inner scope is defined with the same name as one from the outside scope. -Here is an example where a `WITH` clause introduces a new variable, and the outer scope variable person being referenced is still available after: +Variables from the outside scope are visible for the entire subquery, even when using a `WITH` clause. +This means that shadowing of these variables is not allowed. +An outside scope variable is shadowed when a newly introduced variable within the inner scope is defined with the same variable. +In the below example, a `WITH` clause introduces a new variable. +Note that the outer scope variable `person` referenced in the main query is still available after the `WITH` clause. .Query [source, cypher] @@ -793,8 +794,8 @@ endif::nonhtmloutput[] [[existential-subquery-with-return]] ==== `EXISTS` subquery with `RETURN` -`EXISTS` subqueries do not require a `RETURN` clause at the end of the subquery, but if one is present, it does not -need to be aliased, a difference from xref::clauses/call-subquery.adoc[`CALL` subqueries]. +`EXISTS` subqueries do not require a `RETURN` clause at the end of the subquery. If one is present, it does not +need to be aliased, which is different compared to xref::clauses/call-subquery.adoc[`CALL` subqueries]. Any variables returned in an `EXISTS` subquery will not be available after the subquery. .Query @@ -848,17 +849,17 @@ endif::nonhtmloutput[] A `COUNT` subquery expression can be used to count the number of rows returned by the subquery. -Any non-writing query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +Any non-writing query is allowed. `COUNT` subqueries differ from regular queries in that the final `RETURN` clause may be omitted, as any variable defined within the subquery will not be available outside of the expression, -even if a final `RETURN` clause is used. One exception to this is that for a `DISTINCT UNION` the `RETURN` is still mandatory. +even if a final `RETURN` clause is used. One exception to this is that for a `DISTINCT UNION` clause, the `RETURN` clause is still mandatory. -It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `COUNT` consists of only a pattern and an optional `WHERE` clause. +It is worth noting that the `MATCH` keyword can be omitted in subqueries in cases where the `COUNT` consists of only a pattern and an optional `WHERE` clause. [[count-subquery-simple-case]] ==== Simple `COUNT` subquery -Variables introduced by the outside scope can be used in the `COUNT` subquery without importing them, -unlike the case for `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[where they require importing]. +Variables introduced by the outside scope can be used in the `COUNT` subquery without importing them. +In this regard, `COUNT` subqueries are different from `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[which do require importing]. The following query exemplifies this and outputs the owners of more than one dog: @@ -956,9 +957,9 @@ endif::nonhtmloutput[] [[count-subquery-with-union]] ==== `COUNT` subquery with a `UNION` -`COUNT` can be used with a `UNION` clause, if the `UNION` clause is distinct, the `RETURN` clause is required. -`UNION ALL` clauses do not require the `RETURN` clause, however, it is worth noting that if one branch has a `RETURN` clause, then all require one. -Here is the result that shows the count of pets each person has by using a `UNION`: +`COUNT` can be used with a `UNION` clause. If the `UNION` clause is distinct, the `RETURN` clause is required. +`UNION ALL` clauses do not require the `RETURN` clause. However, it is worth noting that if one branch has a `RETURN` clause, then all require one. +The below example shows the count of pets each person has by using a `UNION` clause: .Query [source, cypher] @@ -1018,11 +1019,11 @@ endif::nonhtmloutput[] [[count-subquery-with-with]] ==== `COUNT` subquery with `WITH` -Variables from the outside scope will not go out of scope when using a `WITH`, -they are visible for the entirety of the subquery, this means that shadowing of these variables is also not allowed. -Shadowing of an outside scope variable is when a newly introduced variable inside the inner scope is defined with the same name as one from the outside scope. -Here is an example where a `WITH` clause introduces a new variable, -and the outer scope variable person being referenced is still available after: +Variables from the outside scope are visible for the entire subquery, even when using a `WITH` clause. +This means that shadowing of these variables is not allowed. +An outside scope variable is shadowed when a newly introduced variable within the inner scope is defined with the same variable. +In the below example, a `WITH` clause introduces a new variable. +Note that the outer scope variable `person` referenced in the main query is still available after the `WITH` clause. .Query [source, cypher] @@ -1276,8 +1277,8 @@ endif::nonhtmloutput[] [[count-subquery-with-return]] ==== `COUNT` subquery with `RETURN` -`COUNT` subqueries do not require a `RETURN` clause at the end of the subquery, but if one is present, it does not -need to be aliased, a difference from xref::clauses/call-subquery.adoc[`CALL` subqueries]. +`COUNT` subqueries do not require a `RETURN` clause at the end of the subquery. If one is present, it does not need to be aliased. +This is a difference compared to from xref::clauses/call-subquery.adoc[`CALL` subqueries]. Any variables returned in a `COUNT` subquery will not be available after the subquery. .Query From 2e77a19e7b11f205f12f6c7184280a9b05407dd6 Mon Sep 17 00:00:00 2001 From: David Oliver Date: Wed, 30 Nov 2022 09:46:07 +0000 Subject: [PATCH 020/383] Update explanation for predicate functions when used with null values (#226) Replaces https://github.com/neo4j/neo4j-documentation/pull/1633 @gem-neo4j please could you check that I moved these over into the right place. --- modules/ROOT/pages/functions/predicate.adoc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 3254355c1..ff8f713d6 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -41,7 +41,8 @@ CREATE == all() The function `all()` returns `true` if the predicate holds for all elements in the given list. -`null` is returned if the list is `null` or all of its elements are `null`. + +`null` is returned if the list is `null` or if the predicate evaluates to `null` for at least one element and does not evaluate to `false` for any other element. *Syntax:* @@ -113,7 +114,8 @@ All nodes in the returned paths will have a property `age` with a value larger t == any() The function `any()` returns `true` if the predicate holds for at least one element in the given list. -`null` is returned if the list is `null` or all of its elements are `null`. + +`null` is returned if the list is `null`, or if the predicate evaluates to `null` for at least one element and does not evaluate to `true` for any other element. *Syntax:* @@ -429,7 +431,8 @@ Thus, `isEmpty()` is not suited to test for `null`-values. == none() The function `none()` returns `true` if the predicate does _not_ hold for any element in the given list. -`null` is returned if the list is `null` or all of its elements are `null`. + +`null` is returned if the list is `null`, or if the predicate evaluates to `null` for at least one element and does not evaluate to `true` for any other element. *Syntax:* @@ -499,7 +502,8 @@ No node in the returned paths has a property `age` with the value `25`. == single() The function `single()` returns `true` if the predicate holds for exactly _one_ of the elements in the given list. -`null` is returned if the list is `null` or all of its elements are `null`. + +`null` is returned if the list is `null`, or if the predicate evaluates to `null` for at least one element and `true` for max one element. *Syntax:* @@ -558,4 +562,3 @@ In every returned path there is exactly one node that has a property `eyes` with |=== ====== - From 503949f0db9091234fbd9c10919078d2506285f1 Mon Sep 17 00:00:00 2001 From: lidiazuin <102308961+lidiazuin@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:39:24 +0100 Subject: [PATCH 021/383] Improving description of `access` on SHOW DATABASES (#231) (#236) Original PR: https://github.com/neo4j/docs-cypher/pull/231#pullrequestreview-1199287768 --- modules/ROOT/pages/databases.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 02920499b..6ccea090b 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -134,6 +134,11 @@ These commands return the following columns: | access | The database access mode, either `read-write` or `read-only`. label:default-output[] +[NOTE] +==== +A database may be described as read-only when using `ALTER DATABASE ... SET ACCESS READ ONLY`. +==== + | databaseID | The database unique ID. From b28ccc7ea3fac2bc9c9cb4e7ff5ec7f6da5a7b3b Mon Sep 17 00:00:00 2001 From: lidiazuin <102308961+lidiazuin@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:44:04 +0100 Subject: [PATCH 022/383] Edition of the Expressions section after review (#235) (#238) Original PR https://github.com/neo4j/docs-cypher/pull/235 --- modules/ROOT/pages/clauses/match.adoc | 1 + modules/ROOT/pages/clauses/where.adoc | 106 +++++++-------- modules/ROOT/pages/syntax/expressions.adoc | 151 +++++++++++---------- modules/ROOT/pages/syntax/patterns.adoc | 2 + 4 files changed, 135 insertions(+), 125 deletions(-) diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 9235d9601..5df105c50 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -311,6 +311,7 @@ Returns all actors that `ACTED_IN` *'Wall Street'*. 1+d|Rows: 3 |=== +Read more about xref:/syntax/expressions.adoc#relationship-type-expressions[relationship type expressions]. [[match-on-multiple-rel-types]] === Match on multiple relationship types diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index 1a062b4d8..5174d8b58 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -10,6 +10,7 @@ * xref::clauses/where.adoc#where-introduction[Introduction] * xref::clauses/where.adoc#query-where-basic[Basic usage] + ** xref::clauses/where.adoc#node-pattern-predicates[Node pattern predicates] ** xref::clauses/where.adoc#boolean-operations[Boolean operations] ** xref::clauses/where.adoc#filter-on-node-label[Filter on node label] ** xref::clauses/where.adoc#filter-on-node-property[Filter on node property] @@ -40,7 +41,6 @@ ** xref::clauses/where.adoc#simple-range[Simple range] ** xref::clauses/where.adoc#composite-range[Composite range] * xref::clauses/where.adoc#pattern-element-predicates[Pattern element predicates] - ** xref::clauses/where.adoc#node-pattern-predicates[Node pattern predicates] ** xref::clauses/where.adoc#relationship-pattern-predicates[Relationship pattern predicates] @@ -84,6 +84,57 @@ CREATE (andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), [[query-where-basic]] == Basic usage +[[node-pattern-predicates]] +=== Node pattern predicates + +`WHERE` can appear inside a node pattern in a `MATCH` clause or a pattern comprehension: + + +.+WHERE+ +====== + +.Query +[source, cypher, indent=0] +---- +WITH 30 AS minAge +MATCH (a:Person WHERE a.name = 'Andy')-[:KNOWS]->(b:Person WHERE b.age > minAge) +RETURN b.name +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(b WHERE b:Person) | b.name] AS friends +---- + +.Result +[role="queryresult",options="header,footer",cols="1*> in order to specify additional constraints. - -[[node-pattern-predicates]] -=== Node pattern predicates - -`WHERE` can appear inside a node pattern in a `MATCH` clause or a pattern comprehension: - - -.+WHERE+ -====== - -.Query -[source, cypher, indent=0] ----- -WITH 30 AS minAge -MATCH (a:Person WHERE a.name = 'Andy')-[:KNOWS]->(b:Person WHERE b.age > minAge) -RETURN b.name ----- - -.Result -[role="queryresult",options="header,footer",cols="1*(b WHERE b:Person) | b.name] AS friends ----- - -.Result -[role="queryresult",options="header,footer",cols="1* Date: Wed, 30 Nov 2022 09:32:08 +0100 Subject: [PATCH 023/383] =?UTF-8?q?Document=20=C2=B4Allow=20both=20plural?= =?UTF-8?q?=20and=20singular=20form=20in=20SHOW=20commands`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `DATABASE` or `DATABASES` in show database commands (excluding show home/default database) - `SERVER` or `SERVERS` in show sever command - `USER` or `USERS` in show users command (excluding show current user) - `ROLE` or `ROLES` in show role command - `WITH USER` or `WITH USERS` in show role command We still only allow singular form for: `SHOW HOME DATABASE` `SHOW DEFAULT DATABASE` `SHOW CURRENT USER` as those shouldn't be able to imply there could be more than one. --- modules/ROOT/pages/access-control/manage-roles.adoc | 5 ++--- modules/ROOT/pages/access-control/manage-servers.adoc | 2 +- modules/ROOT/pages/access-control/manage-users.adoc | 4 ++-- modules/ROOT/pages/databases.adoc | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/ROOT/pages/access-control/manage-roles.adoc b/modules/ROOT/pages/access-control/manage-roles.adoc index ffc006fb3..f303b1305 100644 --- a/modules/ROOT/pages/access-control/manage-roles.adoc +++ b/modules/ROOT/pages/access-control/manage-roles.adoc @@ -27,7 +27,7 @@ m| SHOW ROLES a| [source, syntax, role="noheader"] ---- -SHOW [ALL\|POPULATED] ROLES +SHOW [ALL\|POPULATED] ROLE[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -63,7 +63,7 @@ m| SHOW ROLES WITH USERS a| [source, syntax, role="noheader"] ---- -SHOW [ALL\|POPULATED] ROLES WITH USERS +SHOW [ALL\|POPULATED] ROLE[S] WITH USER[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -84,7 +84,6 @@ a| GRANT SHOW ROLE ---- -[source, privilege, role="noheader"] (see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index fef8dbabc..2942260d5 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -153,7 +153,7 @@ m| SHOW SERVERS a| [source, cyper, role=noplay] ---- -SHOW SERVERS +SHOW SERVER[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/access-control/manage-users.adoc index 12d2c9fbc..2005fa7cb 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/access-control/manage-users.adoc @@ -55,7 +55,7 @@ m| SHOW USERS a| [source, syntax, role="noheader"] ---- -SHOW USERS +SHOW USER[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -477,7 +477,7 @@ This example shows how to: [source, cypher, role=noplay] ---- -SHOW USERS YIELD user, suspended, passwordChangeRequired, roles, home +SHOW USER YIELD user, suspended, passwordChangeRequired, roles, home WHERE user = 'jake' ---- ====== diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 6ccea090b..4b8be5845 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -24,13 +24,13 @@ The syntax of the database management commands is as follows: | [source, syntax, role="noheader"] ---- -SHOW { DATABASE name \| DATABASES \| DEFAULT DATABASE \| HOME DATABASE } +SHOW { DATABASE[S] name \| DATABASE[S] \| DEFAULT DATABASE \| HOME DATABASE } [WHERE expression] ---- [source, syntax, role="noheader"] ---- -SHOW { DATABASE name \| DATABASES \| DEFAULT DATABASE \| HOME DATABASE } +SHOW { DATABASE[S] name \| DATABASE[S] \| DEFAULT DATABASE \| HOME DATABASE } YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] From 090ad303452c6c4fc49dd17c39552ea1e9ac7a75 Mon Sep 17 00:00:00 2001 From: JPryce-Aklundh Date: Thu, 1 Dec 2022 16:41:19 +0100 Subject: [PATCH 024/383] fix.execution-plan.xref --- modules/ROOT/pages/execution-plans/index.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/execution-plans/index.adoc b/modules/ROOT/pages/execution-plans/index.adoc index 3bd3ae73f..5002f82f9 100644 --- a/modules/ROOT/pages/execution-plans/index.adoc +++ b/modules/ROOT/pages/execution-plans/index.adoc @@ -10,7 +10,7 @@ This section describes the characteristics of query execution plans and provides [NOTE] ==== -For information on replanning, see xref::query-tuning/query-options.adoc#cypher-replanning[Cypher replanning]. +For information on replanning, see xref:query-tuning/query-options.adoc#cypher-replanning[Cypher replanning]. ==== [[execution-plan-introduction]] From 7070e1f5e420ae69571fdebdc948438617b56203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 6 Dec 2022 16:36:01 +0100 Subject: [PATCH 025/383] Update restrictions when using WITH clause in a CALL subquery (#245) --- modules/ROOT/pages/clauses/call-subquery.adoc | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/clauses/call-subquery.adoc index 0d7bb5c77..8debd0706 100644 --- a/modules/ROOT/pages/clauses/call-subquery.adoc +++ b/modules/ROOT/pages/clauses/call-subquery.adoc @@ -675,3 +675,48 @@ These are the restrictions on queries that use `+CALL { ... } IN TRANSACTIONS+`: * A `+CALL { ... } IN TRANSACTIONS+` in a `UNION` is not supported. * A `+CALL { ... } IN TRANSACTIONS+` after a write clause is not supported, unless that write clause is inside a `+CALL { ... } IN TRANSACTIONS+`. +Additionally, there are some restrictions that apply when using an importing `WITH` clause in a `CALL` subquery: + +* Only variables imported with the importing `WITH` clause can be used. +* No expressions or aliasing are allowed within the importing `WITH` clause. +* It is not possible to follow an importing `WITH` clause with any of the following clauses: `DISTINCT`, `ORDER BY`, `WHERE`, `SKIP`, and `LIMIT`. + +Attempting any of the above, will throw an error. +For example, the following query using a `WHERE` clause after an importing `WITH` clause will throw an error: + +.Query +[source, cypher, indent=0] +---- +UNWIND [[1,2],[1,2,3,4],[1,2,3,4,5]] AS l +CALL { + WITH l + WHERE size(l) > 2 + RETURN l AS largeLists +} +RETURN largeLists +---- + +.Error message: +[source, output, role="noheader", indent=0] +---- +Importing WITH should consist only of simple references to outside variables. +WHERE is not allowed. +---- + +A solution to this restriction, necessary for any filtering or ordering of an importing `WITH` clause, is to declare a second `WITH` clause after the importing `WITH` clause. +This second `WITH` clause will act as a regular `WITH` clause. +For example, the following query will not throw an error: + +.Query +[source, cypher, indent=0] +---- +UNWIND [[1,2],[1,2,3,4],[1,2,3,4,5]] AS l +CALL { + WITH l + WITH size(l) AS size, l AS l + WHERE size > 2 + RETURN l AS largeLists +} +RETURN largeLists +---- + From ae345d0aa141b69477e01abe92e5d982eb18ffda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louise=20S=C3=B6derstr=C3=B6m?= Date: Thu, 8 Dec 2022 15:12:09 +0100 Subject: [PATCH 026/383] Update docs about round() function. (#230) We follow the behaviour of Java, i.e. rounding towards positive infinity for ties when we have no precision or mode or rounding according to https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/math/RoundingMode.html for rounding modes. The current description was wrong in some cases and missleading in others. Also added more examples. Fixes #https://github.com/neo4j/neo4j/issues/12972 and https://github.com/neo4j/neo4j/issues/12963 --- .../pages/functions/mathematical-numeric.adoc | 274 ++++++++++++++++-- 1 file changed, 251 insertions(+), 23 deletions(-) diff --git a/modules/ROOT/pages/functions/mathematical-numeric.adoc b/modules/ROOT/pages/functions/mathematical-numeric.adoc index adfee7017..728126905 100644 --- a/modules/ROOT/pages/functions/mathematical-numeric.adoc +++ b/modules/ROOT/pages/functions/mathematical-numeric.adoc @@ -342,7 +342,7 @@ A random number is returned. [[functions-round]] == round() -`round()` returns the value of the given number rounded to the nearest integer, with half-way values always rounded up. +`round()` returns the value of the given number rounded to the nearest integer, with ties always rounded towards positive infinity. *Syntax:* @@ -400,11 +400,31 @@ RETURN round(3.141592) ====== +.+round() of negative number with tie+ +====== + +.Query +[source, cypher, indent=0] +---- +RETURN round(-1.5) +---- + +Ties are rounded towards positive infinity, therfore `-1.0` is returned. + +.Result +[role="queryresult",options="header,footer",cols="1*> without precision. *Syntax:* @@ -438,12 +458,12 @@ round(expression, precision) |=== -| `round(null)` returns `null`. +| `round()` returns `null` if any of its input parameters are `null`. |=== -.+round()+ +.+round() with precision+ ====== .Query @@ -466,6 +486,52 @@ RETURN round(3.141592, 3) ====== +.+round() with precision 0 and tie+ +====== + +.Query +[source, cypher, indent=0] +---- +RETURN round(-1.5, 0) +---- + +To align with `round(-1.5)`, `-1.0` is returned. + +.Result +[role="queryresult",options="header,footer",cols="1* Date: Mon, 12 Dec 2022 10:25:56 +0100 Subject: [PATCH 027/383] Fix Runtime version in Query Plan examples (#249) --- .../ROOT/pages/execution-plans/operators.adoc | 448 +++++++++--------- .../shortestpath-planning.adoc | 12 +- modules/ROOT/pages/query-tuning/indexes.adoc | 90 ++-- modules/ROOT/pages/query-tuning/using.adoc | 52 +- 4 files changed, 301 insertions(+), 301 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 1dfcc6344..83b4cbf57 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -35,13 +35,13 @@ RETURN n ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -78,13 +78,13 @@ RETURN r ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -121,13 +121,13 @@ RETURN r ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -165,13 +165,13 @@ RETURN candidate ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -209,13 +209,13 @@ RETURN candidate ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -252,13 +252,13 @@ RETURN r, n1 ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -296,13 +296,13 @@ RETURN r, n1 ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -340,13 +340,13 @@ RETURN r ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -384,13 +384,13 @@ RETURN r ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -428,13 +428,13 @@ RETURN r ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -472,13 +472,13 @@ RETURN r ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -516,13 +516,13 @@ RETURN candidate ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -562,13 +562,13 @@ RETURN candidate ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -607,13 +607,13 @@ RETURN countryOrLocation ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -640,13 +640,13 @@ MATCH ()-[r]->() RETURN r ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -674,13 +674,13 @@ MATCH ()-[r]-() RETURN r ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -715,13 +715,13 @@ RETURN r ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -755,13 +755,13 @@ RETURN r ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -797,13 +797,13 @@ RETURN friendOrFoe ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -839,13 +839,13 @@ RETURN friendOrFoe ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -882,13 +882,13 @@ RETURN n ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -924,13 +924,13 @@ RETURN person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -968,13 +968,13 @@ RETURN location ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1014,13 +1014,13 @@ RETURN t ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1064,13 +1064,13 @@ RETURN location, person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1108,13 +1108,13 @@ MERGE (t:Team {name: 'Engineering', id: 42}) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1157,13 +1157,13 @@ RETURN l ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1202,13 +1202,13 @@ RETURN t ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1246,13 +1246,13 @@ RETURN l ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1290,13 +1290,13 @@ RETURN l ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1333,13 +1333,13 @@ RETURN l ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1381,13 +1381,13 @@ RETURN p, q ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1431,13 +1431,13 @@ RETURN p.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------+-------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -1488,13 +1488,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | @@ -1548,13 +1548,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1613,13 +1613,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +--------------------+-----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -1678,13 +1678,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +--------------------+-----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -1742,13 +1742,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1801,13 +1801,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1862,13 +1862,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------------+-----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -1925,13 +1925,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +---------------------------+-----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -1991,13 +1991,13 @@ ON CREATE SET p.existed = false ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2038,13 +2038,13 @@ MERGE (s)-[:FRIENDS_WITH]->(s) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2092,13 +2092,13 @@ RETURN p.name, [(p)-[:WORKS_IN]->(location) | location.name] AS cities ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------+-----------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -2144,13 +2144,13 @@ MERGE (s)-[:FRIENDS_WITH]->(s) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2199,13 +2199,13 @@ RETURN fof ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2245,13 +2245,13 @@ RETURN fof ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2296,13 +2296,13 @@ RETURN p, l ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2346,13 +2346,13 @@ RETURN p ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2394,13 +2394,13 @@ RETURN p, q ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2442,13 +2442,13 @@ RETURN p ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2496,13 +2496,13 @@ RETURN DISTINCT p, q ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2553,13 +2553,13 @@ RETURN DISTINCT p, q ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2603,13 +2603,13 @@ MERGE (t:Team {name: 'Engineering', id: 42}) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -2653,13 +2653,13 @@ CREATE (:Person) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2698,13 +2698,13 @@ RETURN n ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2741,13 +2741,13 @@ RETURN line ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2803,13 +2803,13 @@ RETURN loc.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2862,13 +2862,13 @@ RETURN p, q ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2919,13 +2919,13 @@ RETURN a.name, b.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -2977,13 +2977,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-------------------+----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -3036,13 +3036,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3101,13 +3101,13 @@ RETURN other.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3164,13 +3164,13 @@ RETURN p, t ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3216,13 +3216,13 @@ FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -3269,13 +3269,13 @@ MERGE () ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 1024 @@ -3337,13 +3337,13 @@ RETURN ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3391,13 +3391,13 @@ RETURN p.name, count(*) AS count ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3438,13 +3438,13 @@ RETURN count(p) AS people ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3483,13 +3483,13 @@ RETURN count(r) AS jobs ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3527,13 +3527,13 @@ RETURN DISTINCT l ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3579,13 +3579,13 @@ RETURN DISTINCT p.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3624,13 +3624,13 @@ RETURN p ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3669,13 +3669,13 @@ LIMIT 3 ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3717,13 +3717,13 @@ SKIP 1 ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3769,13 +3769,13 @@ ORDER BY p.name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3821,13 +3821,13 @@ ORDER BY p.name, p.age ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3870,13 +3870,13 @@ LIMIT 2 ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3923,13 +3923,13 @@ LIMIT 2 ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -3972,13 +3972,13 @@ MATCH (p:Country) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4030,13 +4030,13 @@ RETURN value ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4076,13 +4076,13 @@ LIMIT 3 ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4127,13 +4127,13 @@ RETURN p, q ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4177,13 +4177,13 @@ RETURN u, v ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4226,13 +4226,13 @@ RETURN 'hello' AS greeting ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4271,13 +4271,13 @@ RETURN p ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4316,13 +4316,13 @@ FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------+--------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -4367,13 +4367,13 @@ ORDER BY label ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4415,13 +4415,13 @@ RETURN ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4469,13 +4469,13 @@ CREATE (max)-[:FRIENDS_WITH]->(chris) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4514,13 +4514,13 @@ DELETE w ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4566,13 +4566,13 @@ DETACH DELETE p ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4619,13 +4619,13 @@ SET n:Person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4665,13 +4665,13 @@ REMOVE n:Person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4711,13 +4711,13 @@ SET n = {weekday: 'Monday', meal: 'Lunch'} ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4757,13 +4757,13 @@ SET r = {weight: 5, unit: 'kg'} ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4803,13 +4803,13 @@ SET n.checked = true ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -4850,13 +4850,13 @@ FOR (c:Country) REQUIRE c.name is UNIQUE ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-------------------+------------------------------------------------------------------+ | Operator | Details | @@ -4891,13 +4891,13 @@ FOR (c:Country) REQUIRE c.name is UNIQUE ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +--------------------------------+------------------------------------------------------------------+ | Operator | Details | @@ -4932,13 +4932,13 @@ FOR (p:Person) REQUIRE p.name IS NOT NULL ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-------------------+------------------------------------------------------------------+ | Operator | Details | @@ -4972,13 +4972,13 @@ FOR (e:Employee) REQUIRE (e.firstname, e.surname) IS NODE KEY ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-------------------+-----------------------------------------------------------------------------------+ | Operator | Details | @@ -5011,13 +5011,13 @@ FOR ()-[l:LIKED]-() REQUIRE l.when IS NOT NULL ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-------------------+-----------------------------------------------------------------------+ | Operator | Details | @@ -5048,13 +5048,13 @@ DROP CONSTRAINT name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------+-----------------+ | Operator | Details | @@ -5086,13 +5086,13 @@ SHOW CONSTRAINTS ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +------------------+-------------------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -5129,13 +5129,13 @@ FOR (c:Country) ON (c.name) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +--------------+-----------------------------------------------+ | Operator | Details | @@ -5170,13 +5170,13 @@ FOR ()-[k:KNOWS]-() ON (k.since) ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +---------------------------+----------------------------------------------------+ | Operator | Details | @@ -5209,13 +5209,13 @@ DROP INDEX name ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner ADMINISTRATION Runtime SCHEMA -Runtime version 5.0 +Runtime version {neo4j-version-minor} +------------+------------+ | Operator | Details | @@ -5247,13 +5247,13 @@ SHOW INDEXES ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------+-------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -5289,13 +5289,13 @@ SHOW FUNCTIONS ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------+-----------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -5329,13 +5329,13 @@ SHOW PROCEDURES ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-----------------+----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -5369,13 +5369,13 @@ SHOW TRANSACTIONS ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +-------------------+-----------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | @@ -5409,13 +5409,13 @@ TERMINATE TRANSACTIONS 'database-transaction-123' ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime SLOTTED -Runtime version 5.0 +Runtime version {neo4j-version-minor} +------------------------+--------------------------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | diff --git a/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc b/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc index 06f075e2a..6ccee84fd 100644 --- a/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc +++ b/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc @@ -74,13 +74,13 @@ RETURN p ---- .Query plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -151,13 +151,13 @@ RETURN p This query, in contrast with the one above, needs to check that the whole path follows the predicate before we know if it is valid or not, and so the query plan will also include the fallback to the slower exhaustive search algorithm. .Query plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 1024 @@ -254,13 +254,13 @@ However, the inclusion of the `WITH` clause means that the query plan will not i Instead, any paths found by the fast algorithm will subsequently be filtered, which may result in no answers being returned. .Query plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index d346ba864..85ad880be 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -309,13 +309,13 @@ RETURN person ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -349,13 +349,13 @@ RETURN r ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -389,13 +389,13 @@ RETURN person ---- .Query Plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -429,13 +429,13 @@ RETURN person, friend ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -464,13 +464,13 @@ RETURN person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -500,13 +500,13 @@ RETURN person, friend ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -537,13 +537,13 @@ RETURN person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -569,7 +569,7 @@ For example, if indexes exist on both `:Label(p1)` and `:Label(p2)`, `MATCH (n:L .Query -[source, cypher, indent=0] +[source, query plan, subs="attributes+", role="noheader"] ---- MATCH (person:Person) WHERE person.firstname = 'Andy' @@ -577,13 +577,13 @@ RETURN person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -654,13 +654,13 @@ RETURN friend, person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -693,13 +693,13 @@ RETURN r.since ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -735,13 +735,13 @@ RETURN person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -781,13 +781,13 @@ RETURN person .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -819,13 +819,13 @@ RETURN person, friend ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -856,13 +856,13 @@ RETURN person, friend ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -894,13 +894,13 @@ RETURN person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -934,13 +934,13 @@ RETURN person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1016,13 +1016,13 @@ RETURN person, friend ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1093,13 +1093,13 @@ RETURN person ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1133,13 +1133,13 @@ RETURN person, friend ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1171,13 +1171,13 @@ RETURN p ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1213,13 +1213,13 @@ RETURN r.lastMetPoint ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -1256,13 +1256,13 @@ RETURN person.firstname ---- .Query Plan -[source, query plan, role="noheader"] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 diff --git a/modules/ROOT/pages/query-tuning/using.adoc b/modules/ROOT/pages/query-tuning/using.adoc index ad72da885..579d28580 100644 --- a/modules/ROOT/pages/query-tuning/using.adoc +++ b/modules/ROOT/pages/query-tuning/using.adoc @@ -66,13 +66,13 @@ The query above will be used in some of the examples on this page. Without any hints, one index and no join is used. .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -186,13 +186,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -258,13 +258,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -318,13 +318,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -388,13 +388,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -451,13 +451,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -527,13 +527,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -604,13 +604,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -680,13 +680,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -752,13 +752,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -829,13 +829,13 @@ RETURN * ---- .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -907,13 +907,13 @@ RETURN * Without any hint, the planner did not use a join to solve the `OPTIONAL MATCH`. .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 @@ -963,13 +963,13 @@ RETURN * Now the planner uses a join to solve the `OPTIONAL MATCH`. .Query plan -[source] +[source, query plan, subs="attributes+", role="noheader"] ---- Planner COST Runtime PIPELINED -Runtime version 5.0 +Runtime version {neo4j-version-minor} Batch size 128 From fd715a24774a3e2e456903c9b4e2aace5e69c0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:46:51 +0100 Subject: [PATCH 028/383] Remove old note about variable length pattern matching in versions 2.x (#255) Currently only removing note, pending restructuring of the page on patterns in collaboration with the Langstar team (set for q1 of 2023). --- modules/ROOT/pages/syntax/patterns.adoc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/modules/ROOT/pages/syntax/patterns.adoc b/modules/ROOT/pages/syntax/patterns.adoc index 378d470fd..572759cf8 100644 --- a/modules/ROOT/pages/syntax/patterns.adoc +++ b/modules/ROOT/pages/syntax/patterns.adoc @@ -202,15 +202,6 @@ You can specify additional constraints by introducing a xref::clauses/where.adoc [[cypher-pattern-varlength]] == Variable-length pattern matching -[CAUTION] -==== -Variable length pattern matching in versions 2.1.x and earlier does not enforce relationship uniqueness for patterns described within a single `MATCH` clause. -This means that a query such as the following: `MATCH (a)-[r]\->(b), p = (a)-[\*]\->(c) RETURN *, relationships(p) AS rs` may include `r` as part of the `rs` set. -This behavior has changed in versions 2.2.0 and later, in such a way that `r` will be excluded from the result set, as this better adheres to the rules of relationship uniqueness as documented here xref::introduction/uniqueness.adoc[]. -If you have a query pattern that needs to retrace relationships rather than ignoring them as the relationship uniqueness rules normally dictate, you can accomplish this using multiple match clauses, as follows: `MATCH (a)-[r]\->(b) MATCH p = (a)-[*]\->(c) RETURN *, relationships(p)`. -This will work in all versions of Neo4j that support the `MATCH` clause, namely 2.0.0 and later. -==== - Rather than describing a long path using a sequence of many node and relationship descriptions in a pattern, many relationships (and the intermediate nodes) can be described by specifying a length in the relationship description of a pattern. For example: From d9ff885df3044b3d93734d49e4b329e3fbc7e008 Mon Sep 17 00:00:00 2001 From: Jack Waudby <33488812+jackwaudby@users.noreply.github.com> Date: Wed, 14 Dec 2022 13:11:56 +0000 Subject: [PATCH 029/383] Add documentation on updated `SHOW DATABASES` behaviour (#246) In 5.3 the behaviour `SHOW DATABASES` was changed to now additionally display databases hosted on offline servers. This PR updates the documentation to reflect this. --- modules/ROOT/pages/databases.adoc | 5 +++++ ...cations-additions-removals-compatibility.adoc | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 4b8be5845..f46d26bd6 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -258,6 +258,11 @@ However, some privileges enable users to see additional databases regardless of If a user has not been granted `ACCESS` privilege to any databases nor any of the above special cases, the command can still be executed but will only return the `system` database, which is always visible. ==== +[NOTE] +==== +Databases hosted on servers that are offline are also returned by the `SHOW DATABASES` command. +For such databases, the `address` column displays `NULL`, the `currentStatus` column displays `unknown`, and the `statusMessage` displays `Server is unavailable`. +==== ====== diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 407f5c7fd..649634b25 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -19,6 +19,22 @@ Replacement syntax for deprecated and removed features are also indicated. | Feature | Details +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +SHOW DATABASES +---- +a| +Changes to the visibility of databases hosted on offline servers. + +For such databases: + +* The `address` column will return `NULL`. +* The `currentStatus` column will return `unknown`. +* The `statusMessage` will return `Server is unavailable`. + a| label:functionality[] label:updated[] From c1f64e5eb69e1788b1a32dfd6ba0f708b74f3799 Mon Sep 17 00:00:00 2001 From: Satia Herfert Date: Tue, 13 Dec 2022 09:42:41 +0100 Subject: [PATCH 030/383] Clarify that the order of UNION branches matter if there are updates. --- modules/ROOT/pages/clauses/union.adoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/ROOT/pages/clauses/union.adoc b/modules/ROOT/pages/clauses/union.adoc index 7bb7fc475..44eb23a49 100644 --- a/modules/ROOT/pages/clauses/union.adoc +++ b/modules/ROOT/pages/clauses/union.adoc @@ -15,6 +15,16 @@ The number and the names of the columns must be identical in all queries combine To keep all the result rows, use `UNION ALL`. Using just `UNION` will combine and remove duplicates from the result set. +[NOTE] +==== +If any of the queries in a UNION contain updates, the order if queries in the UNION is relevant. + +Any clause before the UNION cannot observe writes made by a clause after the UNION. +Any clause after UNION can observe all writes made by a clause before the UNION. + +For details see xref::introduction/clause_composition.adoc#cypher-clause-composition-union-queries[clause composition in queries with `UNION`] for details. +==== + image:graph_union_clause.svg[] //// From b85523e29b3f8bddfc62cb276256e4eab5a47921 Mon Sep 17 00:00:00 2001 From: Satia Herfert Date: Wed, 14 Dec 2022 13:48:06 +0100 Subject: [PATCH 031/383] typo Co-authored-by: Gustav Hedengran --- modules/ROOT/pages/clauses/union.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/clauses/union.adoc b/modules/ROOT/pages/clauses/union.adoc index 44eb23a49..25221e6aa 100644 --- a/modules/ROOT/pages/clauses/union.adoc +++ b/modules/ROOT/pages/clauses/union.adoc @@ -17,7 +17,7 @@ Using just `UNION` will combine and remove duplicates from the result set. [NOTE] ==== -If any of the queries in a UNION contain updates, the order if queries in the UNION is relevant. +If any of the queries in a UNION contain updates, the order of queries in the UNION is relevant. Any clause before the UNION cannot observe writes made by a clause after the UNION. Any clause after UNION can observe all writes made by a clause before the UNION. From ec519cb0269c795a983a69ab1e70ce1e802b07e7 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 15 Dec 2022 16:45:32 +0000 Subject: [PATCH 032/383] Update Neo4j version attributes --- antora.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antora.yml b/antora.yml index ac8acc752..08df6a409 100644 --- a/antora.yml +++ b/antora.yml @@ -7,5 +7,5 @@ nav: asciidoc: attributes: neo4j-version: '5' - neo4j-version-minor: '5.3' - neo4j-version-exact: '5.3.0' + neo4j-version-minor: '5.4' + neo4j-version-exact: '5.4.0' From ad68b7181a71e5d81e7d8bdba65c0d328e1d899f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:33:43 +0100 Subject: [PATCH 033/383] fix index preference list (#265) --- modules/ROOT/pages/query-tuning/indexes.adoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index 85ad880be..ce3038366 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -410,10 +410,9 @@ Batch size 128 Total database accesses: 2, total allocated memory: 184 ---- - [discrete] -[[administration-indexes-relationship-btree-index-example]] -=== Relationship RANGE index +[[administration-indexes-relationship-range-index-example]] +=== Relationship RANGE index example In this example, a `KNOWS(since)` relationship `RANGE` index is available. From fdd763d7a39ce7b9d514bfcb41c822c7739f87ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:52:57 +0100 Subject: [PATCH 034/383] Update default password length from 3 to 8 characters (#266) --- modules/ROOT/pages/access-control/manage-users.adoc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/access-control/manage-users.adoc index 2005fa7cb..8692c4869 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/access-control/manage-users.adoc @@ -528,6 +528,7 @@ CREATE OR REPLACE USER name * For `SET PASSWORD`: ** The `password` can either be a string value or a string parameter. +** The default Neo4j password length is at least 8 characters. ** All passwords are encrypted (hashed) when stored in the Neo4j `system` database. `PLAINTEXT` and `ENCRYPTED` just refer to the format of the password in the Cypher command, i.e. whether Neo4j needs to hash it or it has already been hashed. Consequently, it is never possible to get the plaintext of a password back out of the database. @@ -563,7 +564,7 @@ For example, you can create the user `jake` in a suspended state, with the home [source,cypher,role=noplay] ---- CREATE USER jake -SET PASSWORD 'abc' CHANGE REQUIRED +SET PASSWORD 'abcd1234' CHANGE REQUIRED SET STATUS SUSPENDED SET HOME DATABASE anotherDb ---- @@ -598,7 +599,7 @@ Appending `IF NOT EXISTS` to the `CREATE USER` command will ensure that no excep [source,cypher,role=noplay] ---- CREATE USER jake IF NOT EXISTS -SET PLAINTEXT PASSWORD 'xyz' +SET PLAINTEXT PASSWORD 'abcd1234' ---- ====== @@ -611,7 +612,7 @@ The `CREATE OR REPLACE USER` command will result in any existing user being dele [source,cypher,role=noplay] ---- CREATE OR REPLACE USER jake -SET PLAINTEXT PASSWORD 'xyz' +SET PLAINTEXT PASSWORD 'abcd1234' ---- This is equivalent to running `DROP USER jake IF EXISTS` followed by `CREATE USER jake SET PASSWORD 'xyz'`. @@ -711,7 +712,7 @@ For example, you can modify the user `bob` with a new password and active status [source, cypher, role=noplay] ---- ALTER USER bob -SET PASSWORD 'abc123' CHANGE NOT REQUIRED +SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED SET STATUS ACTIVE ---- @@ -776,7 +777,7 @@ Nothing happens should the user not exist. [source, cypher, role=noplay] ---- -ALTER USER nonExistingUser IF EXISTS SET PASSWORD 'abc123' +ALTER USER nonExistingUser IF EXISTS SET PASSWORD 'abcd1234' ---- @@ -790,7 +791,7 @@ When a user executes this command it will change their password as well as set t [source, cypher, role=noplay] ---- ALTER CURRENT USER -SET PASSWORD FROM 'abc123' TO '123xyz' +SET PASSWORD FROM 'password1' TO 'password2' ---- [NOTE] From 4e0acdb268ee15cfa017780425b7f9462456adc5 Mon Sep 17 00:00:00 2001 From: Lidia Zuin <102308961+lidiazuin@users.noreply.github.com> Date: Fri, 16 Dec 2022 15:11:02 +0100 Subject: [PATCH 035/383] Adding notes about syntax to explain the use of quotation marks (#252) (#269) Cherry-pick of https://github.com/neo4j/docs-cypher/pull/252#pullrequestreview-1220888011 --- .../database-administration.adoc | 4 ++ .../access-control/dbms-administration.adoc | 45 +++++++++++++++++++ .../access-control/manage-privileges.adoc | 5 +++ .../pages/access-control/manage-roles.adoc | 5 +++ .../pages/access-control/manage-servers.adoc | 5 +++ .../pages/access-control/manage-users.adoc | 5 +++ .../access-control/privileges-reads.adoc | 4 ++ .../access-control/privileges-writes.adoc | 4 ++ modules/ROOT/pages/aliases.adoc | 6 ++- .../ROOT/pages/clauses/listing-functions.adoc | 5 +++ .../pages/clauses/listing-procedures.adoc | 5 +++ .../pages/clauses/transaction-clauses.adoc | 10 +++++ modules/ROOT/pages/constraints/syntax.adoc | 5 ++- modules/ROOT/pages/databases.adoc | 6 ++- .../pages/indexes-for-full-text-search.adoc | 5 +++ .../pages/indexes-for-search-performance.adoc | 6 ++- 16 files changed, 121 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/access-control/database-administration.adoc b/modules/ROOT/pages/access-control/database-administration.adoc index bde850bf7..e44d30f3c 100644 --- a/modules/ROOT/pages/access-control/database-administration.adoc +++ b/modules/ROOT/pages/access-control/database-administration.adoc @@ -61,6 +61,10 @@ This can be quite powerful as it allows permissions to be switched from one data * _role[, ...]_ ** The role or roles to associate the privilege with, comma-separated. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== .General grant +ON DATABASE+ privilege syntax [cols="<15s,<85"] diff --git a/modules/ROOT/pages/access-control/dbms-administration.adoc b/modules/ROOT/pages/access-control/dbms-administration.adoc index 331a4dfa2..c34c8092c 100644 --- a/modules/ROOT/pages/access-control/dbms-administration.adoc +++ b/modules/ROOT/pages/access-control/dbms-administration.adoc @@ -141,6 +141,11 @@ a|Rows: 3 The DBMS privileges for role management are assignable using Cypher administrative commands. They can be granted, denied and revoked like other privileges. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Role management privileges command syntax [options="header", width="100%", cols="3a,2"] |=== @@ -370,6 +375,11 @@ a|Rows: 1 The DBMS privileges for user management can be assigned using Cypher administrative commands. They can be granted, denied and revoked like other privileges. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .User management privileges command syntax [options="header", width="100%", cols="3a,2"] |=== @@ -697,6 +707,11 @@ Impersonation is the ability of a user to assume another user's roles (and there The ability to impersonate users can be granted via the `IMPERSONATE` privilege. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Impersonation privileges command syntax [options="header", width="100%", cols="3a,2"] |=== @@ -765,6 +780,11 @@ GRANT IMPERSONATE (alice, bob) ON DBMS TO userImpersonator The DBMS privileges for database management can be assigned by using Cypher administrative commands. They can be granted, denied and revoked like other privileges. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Database management privileges command syntax [options="header", width="100%", cols="3a,2"] |=== @@ -1020,6 +1040,11 @@ The DBMS privileges for alias management can be assigned by using Cypher adminis They can be granted, denied and revoked like other privileges. It is also possible to manage aliases with xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[database management commands]. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Alias management privileges command syntax [options="header", width="100%", cols="3a,2"] |=== @@ -1183,6 +1208,11 @@ a|Rows: 1 The DBMS privileges for server management can be assigned using Cypher administrative commands. They can be granted, denied, and revoked like other privileges. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Server management privileges command syntax [options="header", width="100%", cols="3a,2"] |=== @@ -1208,6 +1238,11 @@ GRANT SHOW SERVERS The DBMS privileges for privilege management can be assigned by using Cypher administrative commands. They can be granted, denied and revoked like other privileges. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Privilege management privileges command syntax [options="header", width="100%", cols="3a,2"] |=== @@ -1355,6 +1390,11 @@ a|Rows: 1 The DBMS privileges for procedure and user defined function execution can be assigned by using Cypher administrative commands. They can be granted, denied and revoked like other privileges. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Execute privileges command syntax [options="header", width="100%", cols="3a,2"] |=== @@ -1946,6 +1986,11 @@ The right to perform the following privileges can be achieved with a single comm * Execute all procedures with elevated privileges. * Execute all user defined functions with elevated privileges. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + [source, cypher, role=noplay] ---- GRANT [IMMUTABLE] ALL [[DBMS] PRIVILEGES] diff --git a/modules/ROOT/pages/access-control/manage-privileges.adoc b/modules/ROOT/pages/access-control/manage-privileges.adoc index 36ad8a575..eae6b3aaa 100644 --- a/modules/ROOT/pages/access-control/manage-privileges.adoc +++ b/modules/ROOT/pages/access-control/manage-privileges.adoc @@ -31,6 +31,11 @@ If a user was not also provided with the database `ACCESS` privilege, then acces Information about the database access privilege can be found in xref::access-control/database-administration.adoc#access-control-database-administration-access[The ACCESS privilege]. ==== +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + [role=enterprise-edition] [[access-control-graph-privileges]] diff --git a/modules/ROOT/pages/access-control/manage-roles.adoc b/modules/ROOT/pages/access-control/manage-roles.adoc index f303b1305..e23581161 100644 --- a/modules/ROOT/pages/access-control/manage-roles.adoc +++ b/modules/ROOT/pages/access-control/manage-roles.adoc @@ -17,6 +17,11 @@ When connected to the DBMS over `bolt`, administration commands are automaticall [[access-control-role-syntax]] == Role management command syntax +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + [cols="<15s,<85"] |=== diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index 2942260d5..b892fd9eb 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -12,6 +12,11 @@ When connected to the DBMS over `bolt`, administration commands are automaticall [[server-management-syntax]] == Server management command syntax +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + [cols="<15s,<85"] |=== | Command diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/access-control/manage-users.adoc index 8692c4869..b2d4109d2 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/access-control/manage-users.adoc @@ -15,6 +15,11 @@ When connected to the DBMS over `bolt`, administration commands are automaticall [[access-control-user-syntax]] == User management command syntax +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + [cols="<15s,<85"] |=== diff --git a/modules/ROOT/pages/access-control/privileges-reads.adoc b/modules/ROOT/pages/access-control/privileges-reads.adoc index cae6246fd..1e0007415 100644 --- a/modules/ROOT/pages/access-control/privileges-reads.adoc +++ b/modules/ROOT/pages/access-control/privileges-reads.adoc @@ -15,6 +15,10 @@ There are three separate read privileges: * xref::access-control/privileges-reads.adoc#access-control-privileges-reads-read[`READ`] - enables the specified properties of the found entities to be read. * xref::access-control/privileges-reads.adoc#access-control-privileges-reads-match[`MATCH`] - combines both `TRAVERSE` and `READ`, enabling an entity to be found and its properties read. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== [[access-control-privileges-reads-traverse]] == The `TRAVERSE` privilege diff --git a/modules/ROOT/pages/access-control/privileges-writes.adoc b/modules/ROOT/pages/access-control/privileges-writes.adoc index 263f3ede4..90a16c0ba 100644 --- a/modules/ROOT/pages/access-control/privileges-writes.adoc +++ b/modules/ROOT/pages/access-control/privileges-writes.adoc @@ -23,6 +23,10 @@ There are also compound privileges which combine the above specific privileges: * xref::access-control/privileges-writes.adoc#access-control-privileges-writes-write[`WRITE`] - allows all `WRITE` operations on an entire graph. * xref::access-control/privileges-writes.adoc#access-control-privileges-writes-all[`ALL GRAPH PRIVILEGES`] - allows all `READ` and `WRITE` operations on an entire graph. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== [[access-control-privileges-writes-create]] == The `CREATE` privilege diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 1e352be53..f798c3d5f 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -28,6 +28,11 @@ When connected to the DBMS over Bolt, administration commands are automatically The syntax of the alias management commands is as follows: +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Alias management command syntax [options="header", width="100%", cols="1,5a"] |=== @@ -109,7 +114,6 @@ Drop either a local or remote database alias. |=== - This is the list of the allowed driver settings for remote aliases. [[remote-alias-driver-settings]] diff --git a/modules/ROOT/pages/clauses/listing-functions.adoc b/modules/ROOT/pages/clauses/listing-functions.adoc index dfbb4ce70..6963594fb 100644 --- a/modules/ROOT/pages/clauses/listing-functions.adoc +++ b/modules/ROOT/pages/clauses/listing-functions.adoc @@ -65,6 +65,11 @@ Is `null` without the xref::access-control/dbms-administration.adoc#access-contr == Syntax +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + List functions, either all or only built-in or user-defined:: [source, syntax, role="noheader", indent=0] diff --git a/modules/ROOT/pages/clauses/listing-procedures.adoc b/modules/ROOT/pages/clauses/listing-procedures.adoc index 5f2db572f..b5fbb906c 100644 --- a/modules/ROOT/pages/clauses/listing-procedures.adoc +++ b/modules/ROOT/pages/clauses/listing-procedures.adoc @@ -65,6 +65,11 @@ a| Map of extra output, e.g. if the procedure is deprecated. == Syntax +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + List all procedures:: [source, syntax, role="noheader", indent=0] diff --git a/modules/ROOT/pages/clauses/transaction-clauses.adoc b/modules/ROOT/pages/clauses/transaction-clauses.adoc index 3e61f5448..20aa69a7c 100644 --- a/modules/ROOT/pages/clauses/transaction-clauses.adoc +++ b/modules/ROOT/pages/clauses/transaction-clauses.adoc @@ -209,6 +209,11 @@ The `SHOW TRANSACTIONS` command can be combined with multiple `SHOW TRANSACTIONS === Syntax +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + List transactions on the current server:: [source, cypher, role="noheader", indent=0] @@ -366,6 +371,11 @@ The `TERMINATE TRANSACTIONS` command can be combined with multiple `SHOW TRANSAC === Syntax +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + Terminate transactions by ID on the current server:: [source, cypher, role="noheader", indent=0] diff --git a/modules/ROOT/pages/constraints/syntax.adoc b/modules/ROOT/pages/constraints/syntax.adoc index c2b9fe0e7..5a759d93f 100644 --- a/modules/ROOT/pages/constraints/syntax.adoc +++ b/modules/ROOT/pages/constraints/syntax.adoc @@ -4,6 +4,10 @@ = Syntax :check-mark: icon:check[] +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== [[administration-constraints-syntax-create]] == Syntax for creating constraints @@ -26,7 +30,6 @@ There is no supported index configuration for range indexes. Creating a constraint requires the xref::access-control/database-administration.adoc#access-control-database-administration-constraints[`CREATE CONSTRAINT` privilege]. ==== - [[administration-constraints-syntax-create-unique]] [discrete] === Create a unique node property constraint diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index f46d26bd6..0b42f4e7d 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -15,6 +15,11 @@ These administrative commands are automatically routed to the `system` database The syntax of the database management commands is as follows: +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Database management command syntax [options="header", width="100%", cols="1m,5a"] |=== @@ -104,7 +109,6 @@ DROP [COMPOSITE] DATABASE name [IF EXISTS] [{DUMP\|DESTROY} [DATA]] [WAIT [n [SE |=== - [[administration-databases-show-databases]] == Listing databases diff --git a/modules/ROOT/pages/indexes-for-full-text-search.adoc b/modules/ROOT/pages/indexes-for-full-text-search.adoc index 73c7ba1e5..7c28e512b 100644 --- a/modules/ROOT/pages/indexes-for-full-text-search.adoc +++ b/modules/ROOT/pages/indexes-for-full-text-search.adoc @@ -101,6 +101,11 @@ Full-text indexes are created with the `CREATE FULLTEXT INDEX` command. An index can be given a unique name when created (or get a generated one), which is used to reference the specific index when querying or dropping it. A full-text index applies to a list of labels or a list of relationship types, for node and relationship indexes respectively, and then a list of property names. +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .Syntax for creating full-text indexes [options="header", width="100%", cols="5a, 3"] |=== diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index aa974c871..bea730a12 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -78,6 +78,11 @@ With `IF NOT EXISTS`, no error is thrown and nothing happens should an index wit It may still throw an error if conflicting constraints exist, such as constraints with the same name or schema and backing index type. ==== +[NOTE] +==== +The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +==== + .+Create a range index on nodes+ [options="noheader", width="100%", cols="2, 8a"] @@ -1670,4 +1675,3 @@ DROP INDEX missing_index_name IF EXISTS ====== - From 82e44c971208d09a086998c967ec2641208b6694 Mon Sep 17 00:00:00 2001 From: Arne Fischereit <79841228+arnefischereit@users.noreply.github.com> Date: Mon, 19 Dec 2022 10:59:14 +0100 Subject: [PATCH 036/383] Pointing the link to index-backed order-by to the right location (#259) The link at the end of the introduction of https://neo4j-docs-cypher-259.surge.sh/cypher-manual/5/clauses/order-by/ pointed to a general page about indexes instead of the specialised page on index-backed ORDER BY. --- modules/ROOT/pages/clauses/order-by.adoc | 2 +- .../ROOT/pages/query-tuning/advanced-example.adoc | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/ROOT/pages/clauses/order-by.adoc b/modules/ROOT/pages/clauses/order-by.adoc index 348ec7989..15e033981 100644 --- a/modules/ROOT/pages/clauses/order-by.adoc +++ b/modules/ROOT/pages/clauses/order-by.adoc @@ -22,7 +22,7 @@ This last rule is to make sure that `ORDER BY` does not change the results, only The performance of Cypher queries using `ORDER BY` on node properties can be influenced by the existence and use of an index for finding the nodes. If the index can provide the nodes in the order requested in the query, Cypher can avoid the use of an expensive `Sort` operation. -Read more about this capability in xref::query-tuning/indexes.adoc[The use of indexes]. +Read more about this capability in xref::query-tuning/advanced-example.adoc#advanced-query-tuning-example-index-backed-order-by[Index-backed ORDER BY]. The following graph is used for the examples below: diff --git a/modules/ROOT/pages/query-tuning/advanced-example.adoc b/modules/ROOT/pages/query-tuning/advanced-example.adoc index a86e28f28..94e185d45 100644 --- a/modules/ROOT/pages/query-tuning/advanced-example.adoc +++ b/modules/ROOT/pages/query-tuning/advanced-example.adoc @@ -14,7 +14,7 @@ In summary, an index will be based on the combination of a `Label` and a `proper Any Cypher query that searches for nodes with a specific label and some predicate on the property (equality, range or existence) will be planned to use the index if the cost planner deems that to be the most efficient solution. -In order to benefit from enhancements provided by native indexes, it is useful to understand when _index-backed property lookup_ and _index-backed order by_ will come into play. +In order to benefit from enhancements provided by native indexes, it is useful to understand when _index-backed property lookup_ and _index-backed ORDER BY_ will come into play. Let's explain how to use these features with a more advanced query tuning example. [NOTE] @@ -994,7 +994,7 @@ In this case, the semantics of aggregating functions works like an implicit exis [[advanced-query-tuning-example-index-backed-order-by]] -== Index-backed order by +== Index-backed ORDER BY Now consider the following refinement to the query: @@ -1064,7 +1064,7 @@ In Neo4j 3.5 and later, the Cypher planner will recognize that the index already The `Order by` column describes the order of rows after each operator. We see that the `Order by` column contains `p.name ASC` from the index seek operation, meaning that the rows are ordered by `p.name` in ascending order. -_Index-backed order by_ can also be used for queries that expect their results is descending order, but with slightly lower performance. +_Index-backed ORDER BY_ can also be used for queries that expect their results is descending order, but with slightly lower performance. [NOTE] ==== @@ -1075,7 +1075,7 @@ In cases where the Cypher planner is unable to remove the `Sort` operator, the p [[advanced-query-tuning-example-indexed-backed-order-by-min-and-max]] === `min()` and `max()` -For the `min` and `max` functions, the _index-backed order by_ optimization can be used to avoid aggregation and instead utilize the fact that the minimum/maximum value is the first/last one in a sorted index. +For the `min` and `max` functions, the _index-backed ORDER BY_ optimization can be used to avoid aggregation and instead utilize the fact that the minimum/maximum value is the first/last one in a sorted index. Consider the following query which returns the fist actor in alphabetical order: [source, cypher, indent=0] @@ -1134,7 +1134,7 @@ This will simply pick the first value from the index. For large datasets, this can improve performance dramatically. -_Index-backed order by_ can also be used for corresponding queries with the `max` function, but with slightly lower performance. +_Index-backed ORDER BY_ can also be used for corresponding queries with the `max` function, but with slightly lower performance. [[advanced-query-tuning-example-indexed-backed-order-by-restrictions]] @@ -1165,7 +1165,7 @@ Predicates that will not work: If there is an existence constraint on the property, no predicate is required to trigger the optimization. For example, `CREATE CONSTRAINT constraint_name FOR (p:Person) REQUIRE p.name IS NOT NULL` -As of Neo4j {neo4j-version-exact}, predicates with parameters, such as `WHERE n.prop > $param`, can trigger _index-backed order by_. +As of Neo4j {neo4j-version-exact}, predicates with parameters, such as `WHERE n.prop > $param`, can trigger _index-backed ORDER BY_. The only exception are queries with parameters of type `Point`. ==== From ff1b62698553a0c080949ff74a16df2ee21f18d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 19 Dec 2022 11:08:02 +0100 Subject: [PATCH 037/383] Correct password example (#271) --- modules/ROOT/pages/access-control/manage-users.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/access-control/manage-users.adoc index b2d4109d2..30e800d27 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/access-control/manage-users.adoc @@ -620,7 +620,7 @@ CREATE OR REPLACE USER jake SET PLAINTEXT PASSWORD 'abcd1234' ---- -This is equivalent to running `DROP USER jake IF EXISTS` followed by `CREATE USER jake SET PASSWORD 'xyz'`. +This is equivalent to running `DROP USER jake IF EXISTS` followed by `CREATE USER jake SET PASSWORD 'abcd1234'`. ====== From 450666d382efdab1fff8ebb6a7fc8c6ab7378465 Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Mon, 19 Dec 2022 11:35:28 +0100 Subject: [PATCH 038/383] Add docs for new logical operator IntersectionNodeByLabelsScan (#243) --- .../execution-plans/operator-summary.adoc | 6 ++ .../ROOT/pages/execution-plans/operators.adoc | 56 +++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operator-summary.adoc b/modules/ROOT/pages/execution-plans/operator-summary.adoc index a6ec01d5a..23a525efd 100644 --- a/modules/ROOT/pages/execution-plans/operator-summary.adoc +++ b/modules/ROOT/pages/execution-plans/operator-summary.adoc @@ -266,6 +266,12 @@ Yields rows from the left-hand operator and discards rows from the right-hand op | | +| xref::execution-plans/operators.adoc#query-plan-intersection-node-by-labels-scan[IntersectionNodeByLabelsScan] +| Fetches all nodes that have all of the provided labels from the node label index. +| +| +| + | xref::execution-plans/operators.adoc#query-plan-let-anti-semi-apply[LetAntiSemiApply] a| Performs a nested loop. diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 83b4cbf57..9dcfaf2d2 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -5,7 +5,7 @@ [abstract] -- -All executin plan operators are listed here, grouped by the similarity of their characteristics. +All executing plan operators are listed here, grouped by the similarity of their characteristics. -- Certain operators are only used by a subset of the xref::query-tuning/index.adoc#cypher-runtime[runtimes] that Cypher can choose from. @@ -594,6 +594,7 @@ Total database accesses: 76, total allocated memory: 184 // New in 5.0 The `UnionNodeByLabelsScan` operator fetches all nodes that have at least one of the provided labels from the node label index. +====== //// CREATE LOOKUP INDEX lookup_index_name FOR (n) ON EACH labels(n) @@ -628,10 +629,57 @@ Batch size 128 Total database accesses: 13, total allocated memory: 184 ---- +====== + + +[[query-plan-intersection-node-by-labels-scan]] +== Intersection Node By Labels Scan +// IntersectionNodeByLabelsScan +// New in 5.4 + +The `IntersectionNodeByLabelsScan` operator fetches all nodes that have all of the provided labels from the node label index. +====== +//// +CREATE LOOKUP INDEX lookup_index_name FOR (n) ON EACH labels(n) +//// + +.Query +[source,cypher] +---- +MATCH (countryAndLocation:Country&Location) +RETURN countryAndLocation +---- + +.Query Plan +[source, query plan, subs="attributes+", role="noheader"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | countryAndLocation | 10 | 0 | 0 | | | | | +| | +----+-------------------------------------+----------------+------+---------+----------------+ | | | +| +IntersectionNodeByLabelsScan | 1 | countryAndLocation:Country&Location | 10 | 0 | 0 | 120 | 0/0 | 1.011 | Fused in Pipeline 0 | ++-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + + +Total database accesses: 13, total allocated memory: 184 +---- + +====== + [[query-plan-directed-all-relationships-scan]] == Directed All Relationships Scan == The `DirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. +====== .Query [source,cypher] @@ -661,12 +709,12 @@ Batch size 128 Total database accesses: 28, total allocated memory: 184 ---- - +====== [[query-plan-undirected-all-relationships-scan]] == Undirected All Relationships Scan == The `UndirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. - +====== .Query [source,cypher] ---- @@ -695,7 +743,7 @@ Batch size 128 Total database accesses: 28, total allocated memory: 184 ---- - +====== [[query-plan-directed-relationship-type-scan]] == Directed Relationship Type Scan From 1a7c022f938b2ad83438abfaf3da9b18b3aa774c Mon Sep 17 00:00:00 2001 From: AlexicaWright <49636617+AlexicaWright@users.noreply.github.com> Date: Tue, 20 Dec 2022 09:51:22 +0100 Subject: [PATCH 039/383] fix broken link --- modules/ROOT/pages/clauses/use.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index 9bef73ab3..681446a1d 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -24,7 +24,7 @@ Where `` refers to the name or alias of a database in the DBMS. [[query-use-syntax-composite]] === Composite database syntax -When running queries against a link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-database[composite database], the `USE` clause can also appear as the first clause of: +When running queries against a link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases[composite databases], the `USE` clause can also appear as the first clause of: * Union parts: + From 3b012f5eb9b8a7ab89086f385d7177053f5f7ca3 Mon Sep 17 00:00:00 2001 From: AlexicaWright <49636617+AlexicaWright@users.noreply.github.com> Date: Tue, 20 Dec 2022 09:54:13 +0100 Subject: [PATCH 040/383] typo --- modules/ROOT/pages/clauses/use.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index 681446a1d..edfd5ce1d 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -24,7 +24,7 @@ Where `` refers to the name or alias of a database in the DBMS. [[query-use-syntax-composite]] === Composite database syntax -When running queries against a link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases[composite databases], the `USE` clause can also appear as the first clause of: +When running queries against a link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases[composite database], the `USE` clause can also appear as the first clause of: * Union parts: + From 54ba664540364d5f4a9fd3f8190b6aa0ba094b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 21 Dec 2022 11:59:01 +0100 Subject: [PATCH 041/383] Update Show Procedures table (#264) --- .../pages/clauses/listing-procedures.adoc | 251 +++++++++++------- 1 file changed, 148 insertions(+), 103 deletions(-) diff --git a/modules/ROOT/pages/clauses/listing-procedures.adoc b/modules/ROOT/pages/clauses/listing-procedures.adoc index b5fbb906c..5be57ef6d 100644 --- a/modules/ROOT/pages/clauses/listing-procedures.adoc +++ b/modules/ROOT/pages/clauses/listing-procedures.adoc @@ -131,7 +131,7 @@ SHOW PROCEDURES ---- .Result -[role="queryresult",options="header,footer",cols="4* List of procedures]. == Listing procedures with filtering on output columns The listed procedures can be filtered in multiple ways, one way is to use the `WHERE` clause. -For example, returning the names of all admin procedures: +For example, returning the names of all `admin` procedures: .Query [source, cypher, indent=0] @@ -238,10 +240,20 @@ WHERE admin | +"db.stats.collect"+ | +true+ | +"db.stats.retrieve"+ | +true+ | +"db.stats.retrieveAllAnonymized"+ | +true+ - -2+d|Rows: 7 +| +"db.stats.status"+ | +true+ +| +"db.stats.stop"+ | +true+ +| +"dbms.checkConfigValue" | +true+ +| +"dbms.cluster.checkConnectivity"+ | +true+ +| +"dbms.cluster.cordonServer"+ | +true+ +| +"dbms.cluster.readReplicaToggle"+ | +true+ +| +"dbms.cluster.uncordonServer"+ | +true+ +| +"dbms.listConfig"+ | +true+ + +2+d|Rows: 15 |=== +The above table only displays the first 15 results of the query. +For a full list of all procedures which require `admin` privileges in Neo4j, visit the {neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures#/#_list_of_procedures[Operations Manual -> List of procedures]. == Listing procedures with other filtering @@ -249,8 +261,8 @@ The listed procedures can also be filtered by whether a user can execute them. This filtering is only available through the `EXECUTABLE` clause and not through the `WHERE` clause. This is due to using the user's privileges instead of filtering on the available output columns. -There are two options, how to use the `EXECUTABLE` clause. -The first option, is to filter for the current user: +There are two options for using the `EXECUTABLE` clause. +The first option is to filter for the current user: .Query [source, cypher, indent=0] @@ -259,77 +271,95 @@ SHOW PROCEDURES EXECUTABLE BY CURRENT USER YIELD * ---- .Result -[role="queryresult",options="header,footer",cols="5*+ | ++ -| | +"db.awaitIndexes"+ | +"Wait for all indexes to come online (for example: CALL db.awaitIndexes(300))."+ | ++ | ++ -| | +"db.checkpoint"+ | +"Initiate and wait for a new check point, or wait any already on-going check point to complete. Note that this temporarily disables the `dbms.checkpoint.iops.limit` setting in order to make the check point complete faster. This might cause transaction throughput to degrade slightly, due to increased IO load."+ | ++ | ++ -| -| +"db.constraints"+ -| +"List all constraints in the database."+ -| ++ -| ++ -| - -| +"db.createIndex"+ -| +"Create a named schema index with specified index provider and configuration (optional). Yield: name, labels, properties, providerName, status"+ +| +"db.clearQueryCaches"+ +| +"Clears all query caches."+ | ++ | ++ -| | +"db.createLabel"+ | +"Create a label"+ | ++ | ++ -| - -| +"db.createNodeKey"+ -| +"Create a named node key constraint. Backing index will use specified index provider and configuration (optional). Yield: name, labels, properties, providerName, status"+ -| ++ -| ++ -| | +"db.createProperty"+ | +"Create a Property"+ | ++ | ++ -| | +"db.createRelationshipType"+ | +"Create a RelationshipType"+ | ++ | ++ -| -| +"db.createUniquePropertyConstraint"+ -| +"Create a named unique property constraint. Backing index will use specified index provider and configuration (optional). Yield: name, labels, properties, providerName, status"+ +| +"db.index.fulltext.awaitEventuallyConsistentIndexRefresh"+ +| +"Wait for the updates from recently committed transactions to be applied to any eventually-consistent full-text indexes."+ +| ++ +| ++ + +| +"db.index.fulltext.listAvailableAnalyzers"+ +| +"List the available analyzers that the full-text indexes can be configured with."+ +| ++ +| ++ + +| +"db.index.fulltext.queryNodes"+ +| +"Query the given full-text index. Returns the matching nodes, and their Lucene query score, ordered by score. Valid keys for the options map are: 'skip' to skip the top N results; 'limit' to limit the number of results returned; 'analyzer' to use the specified analyzer as search analyzer for this query."+ +| ++ +| ++ + +| +"db.index.fulltext.queryRelationships"+ +| +"Query the given full-text index. Returns the matching relationships, and their Lucene query score, ordered by score. Valid keys for the options map are: 'skip' to skip the top N results; 'limit' to limit the number of results returned; 'analyzer' to use the specified analyzer as search analyzer for this query."+ +| ++ +| ++ + +| +"db.info"+ +| +"Provides information regarding the database."+ +| ++ +| ++ + +| +"db.labels"+ +| +"List all available labels in the database."+ +| ++ +| ++ + +| +"db.listLocks"+ +| +"List all locks in the database."+ +| ++ +| ++ + +| +"db.ping"+ +| +"This procedure can be used by client side tooling to test whether they are correctly connected to a database. The procedure is available in all databases and always returns true. A faulty connection can be detected by not being able to call this procedure."+ | ++ | ++ -| -5+d|Rows: 10 +4+d|Rows: 15 |=== +The above table only displays the first 15 results of the query. Note that the two `roles` columns are empty due to missing the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Also note that the following columns are not present in the table: `mode`, `worksOnSystem`, `signature`, `argumentDescription`, `returnDescription`, `admin`, and `options`. -The second option, filters the list to only contain procedures executable by a specific user: - +The second option for using the `EXECUTABLE` clause is to filter the list to only contain procedures executable by a specific user. +The below example shows the procedures available to the user `jake`, who has been granted the `EXECUTE PROCEDURE dbms.*` privilege by the `admin` of the database. +(More information about `DBMS EXECUTE` privilege administration can be found xref::access-control/dbms-administration.adoc#access-control-dbms-administration-execute[here]). .Query [source, cypher, indent=0] @@ -338,60 +368,75 @@ SHOW PROCEDURES EXECUTABLE BY jake ---- .Result -[role="queryresult",options="header,footer",cols="4* Date: Mon, 2 Jan 2023 09:04:18 +0100 Subject: [PATCH 042/383] Fix small syntax error in clauses folder (#280) Here are some small mistype we notice during the work for test the cypher query under /clauses/*adoc We put some issue we notice in here: https://docs.google.com/document/d/1YzMD5kkfDnMS9WEhk4PaAxZTVqIsZVteS_Heye_c8G4/edit Some of they we are trying to fix in this PR --- modules/ROOT/pages/clauses/call-subquery.adoc | 2 +- modules/ROOT/pages/clauses/match.adoc | 4 ++-- modules/ROOT/pages/clauses/merge.adoc | 10 +++++----- modules/ROOT/pages/clauses/return.adoc | 2 +- modules/ROOT/pages/clauses/use.adoc | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/clauses/call-subquery.adoc index 8debd0706..3e15b6631 100644 --- a/modules/ROOT/pages/clauses/call-subquery.adoc +++ b/modules/ROOT/pages/clauses/call-subquery.adoc @@ -696,7 +696,7 @@ CALL { RETURN largeLists ---- -.Error message: +.Error message [source, output, role="noheader", indent=0] ---- Importing WITH should consist only of simple references to outside variables. diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 5df105c50..3bca6f825 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -674,11 +674,11 @@ CREATE (thePresident:Movie {title: 'The American President'}), (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) + (rob)-[:DIRECTED]->(thePresident); MATCH (charlie:Person {name: 'Charlie Sheen'}), (martin:Person {name: 'Martin Sheen'}) -CREATE (charlie)-[:X {blocked: false}]->(:UNBLOCKED)<-[:X {blocked: false}]-(martin) +CREATE (charlie)-[:X {blocked: false}]->(:UNBLOCKED)<-[:X {blocked: false}]-(martin); CREATE (charlie)-[:X {blocked: true}]->(:BLOCKED)<-[:X {blocked: false}]-(martin) //// diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index f85978153..5a5f4d0e1 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -70,8 +70,8 @@ The following graph is used for the examples below: image:graph_merge_clause.svg[] //// -CREATE CONSTRAINT FOR (person:Person) REQUIRE person.name IS UNIQUE -CREATE CONSTRAINT FOR (movie:Movie) REQUIRE movie.title IS UNIQUE +CREATE CONSTRAINT FOR (person:Person) REQUIRE person.name IS UNIQUE; +CREATE CONSTRAINT FOR (movie:Movie) REQUIRE movie.title IS UNIQUE; CREATE (charlie:Person {name: 'Charlie Sheen', bornIn: 'New York', chauffeurName: 'John Brown'}), (martin:Person {name: 'Martin Sheen', bornIn: 'Ohio', chauffeurName: 'Bob Brown'}), @@ -171,9 +171,9 @@ In this example, the following would significantly improve the performance of th .Query [source, cypher, role="noheader"] ------ +---- CREATE INDEX PersonIndex FOR (n:Person) ON (n.name) ------ +---- [[merge-merge-single-node-derived-from-an-existing-node-property]] @@ -565,7 +565,7 @@ Merge using unique constraints fails when finding partial matches. [source, cypher, indent=0] ---- MERGE (michael:Person {name: 'Michael Douglas', role: 'Gordon Gekko'}) -#RETURN michael +RETURN michael ---- While there is a matching unique `'michael'` node with the name `'Michael Douglas'`, there is no unique node with the role of `'Gordon Gekko'` and `MERGE` fails to match. diff --git a/modules/ROOT/pages/clauses/return.adoc b/modules/ROOT/pages/clauses/return.adoc index e2c2e9825..22ebe051f 100644 --- a/modules/ROOT/pages/clauses/return.adoc +++ b/modules/ROOT/pages/clauses/return.adoc @@ -202,7 +202,7 @@ Any expression can be used as a return item -- literals, predicates, properties, [source, cypher, indent=0] ---- MATCH (a {name: 'A'}) -RETURN a.age > 30, "I'm a literal", [p=(a)-->() | p] AS `(a)-->()` AS `(a)-->()` +RETURN a.age > 30, "I'm a literal", [p=(a)-->() | p] AS `(a)-->()` ---- Returns a predicate, a literal and function call with a pattern expression parameter. diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index edfd5ce1d..5ca0d2169 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -61,7 +61,7 @@ When executing queries against a composite database, the `USE` clause must only In this example it is assumed that the DBMS contains a database named `myDatabase`: -.Query. +.Query [source, cypher, indent=0] ---- USE myDatabase @@ -74,7 +74,7 @@ MATCH (n) RETURN n In this example it is assumed that the DBMS contains a composite database named `myComposite`, which includes an alias named `myConstituent`: -.Query. +.Query [source, cypher, indent=0] ---- USE myComposite.myConstituent @@ -89,7 +89,7 @@ The built-in function `graph.byName()` can be used in the `USE` clause to resolv This example uses a composite database named `myComposite` that includes an alias named `myConstituent`: -.Query. +.Query [source, cypher, indent=0] ---- USE graph.byName('myComposite.myConstituent') @@ -98,7 +98,7 @@ MATCH (n) RETURN n The argument can be any expression that evaluates to the name of a constituent graph - for example a parameter: -.Query. +.Query [source, cypher] ---- USE graph.byName($graphName) From dd6fb314d87cfdb281897c9cb083bffb01764522 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Wed, 4 Jan 2023 13:48:34 +0100 Subject: [PATCH 043/383] Document relationship key and uniqueness constraints (#225) Feature merged in 5.3 but hidden behind feature flag, the flag was turned true by default in 5.4. --- .../database-administration.adoc | 4 - .../pages/clauses/listing-procedures.adoc | 6 +- modules/ROOT/pages/clauses/merge.adoc | 38 +- modules/ROOT/pages/constraints/examples.adoc | 1021 ++++++++++++++--- modules/ROOT/pages/constraints/index.adoc | 39 +- modules/ROOT/pages/constraints/syntax.adoc | 162 ++- ...ions-additions-removals-compatibility.adoc | 85 +- .../execution-plans/operator-summary.adoc | 26 +- .../ROOT/pages/execution-plans/operators.adoc | 149 +-- .../pages/indexes-for-search-performance.adoc | 20 + modules/ROOT/pages/introduction/index.adoc | 3 +- .../introduction/neo4j-databases-graphs.adoc | 10 +- modules/ROOT/pages/keyword-glossary.adoc | 20 +- 13 files changed, 1185 insertions(+), 398 deletions(-) diff --git a/modules/ROOT/pages/access-control/database-administration.adoc b/modules/ROOT/pages/access-control/database-administration.adoc index e44d30f3c..3f4e6447e 100644 --- a/modules/ROOT/pages/access-control/database-administration.adoc +++ b/modules/ROOT/pages/access-control/database-administration.adoc @@ -681,8 +681,6 @@ For example, to grant the role `regularUsers` the ability to create indexes on t GRANT CREATE INDEX ON DATABASE neo4j TO regularUsers ---- -The `SHOW INDEXES` privilege only affects the xref::indexes-for-search-performance.adoc#administration-indexes-list-indexes[`SHOW INDEXES` command], and not the older procedures for listing indexes, such as `db.indexes`. - [[access-control-database-administration-constraints]] == The `CONSTRAINT MANAGEMENT` privileges @@ -743,8 +741,6 @@ For example, to grant the role `regularUsers` the ability to create constraints GRANT CREATE CONSTRAINT ON DATABASE neo4j TO regularUsers ---- -The `SHOW CONSTRAINTS` privilege only affects the xref::constraints/syntax.adoc#administration-constraints-syntax-list[`SHOW CONSTRAINTS` command], and not the older procedures for listing constraints, such as `db.constraints`. - [[access-control-database-administration-tokens]] == The `NAME MANAGEMENT` privileges diff --git a/modules/ROOT/pages/clauses/listing-procedures.adoc b/modules/ROOT/pages/clauses/listing-procedures.adoc index 5be57ef6d..f3540f541 100644 --- a/modules/ROOT/pages/clauses/listing-procedures.adoc +++ b/modules/ROOT/pages/clauses/listing-procedures.adoc @@ -213,7 +213,7 @@ SHOW PROCEDURES 4+d|Rows: 15 |=== -The above table only displays the first 15 results of the query. +The above table only displays the first 15 results of the query. For a full list of all built-in procedures in Neo4j, visit the {neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures#/#_list_of_procedures[Operations Manual -> List of procedures]. == Listing procedures with filtering on output columns @@ -252,7 +252,7 @@ WHERE admin 2+d|Rows: 15 |=== -The above table only displays the first 15 results of the query. +The above table only displays the first 15 results of the query. For a full list of all procedures which require `admin` privileges in Neo4j, visit the {neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures#/#_list_of_procedures[Operations Manual -> List of procedures]. == Listing procedures with other filtering @@ -353,7 +353,7 @@ SHOW PROCEDURES EXECUTABLE BY CURRENT USER YIELD * 4+d|Rows: 15 |=== -The above table only displays the first 15 results of the query. +The above table only displays the first 15 results of the query. Note that the two `roles` columns are empty due to missing the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. Also note that the following columns are not present in the table: `mode`, `worksOnSystem`, `signature`, `argumentDescription`, `returnDescription`, `admin`, and `options`. diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index 5a5f4d0e1..751bb34f7 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -26,11 +26,11 @@ Either the pattern already exists, or it needs to be created. ** xref::clauses/merge.adoc#merge-merge-on-an-undirected-relationship[Merge on an undirected relationship] ** xref::clauses/merge.adoc#merge-merge-on-a-relationship-between-two-existing-nodes[Merge on a relationship between two existing nodes] ** xref::clauses/merge.adoc#merge-merge-on-a-relationship-between-an-existing-node-and-a-merged-node-derived-from-a-node-property[Merge on a relationship between an existing node and a merged node derived from a node property] -* xref::clauses/merge.adoc#query-merge-using-unique-constraints[Using unique constraints with `MERGE`] -** xref::clauses/merge.adoc#merge-merge-using-unique-constraints-creates-a-new-node-if-no-node-is-found[Merge using unique constraints creates a new node if no node is found] -** xref::clauses/merge.adoc#merge-merge-using-unique-constraints-matches-an-existing-node[Merge using unique constraints matches an existing node] -** xref::clauses/merge.adoc#merge-merge-with-unique-constraints-and-partial-matches[Merge with unique constraints and partial matches] -** xref::clauses/merge.adoc#merge-merge-with-unique-constraints-and-conflicting-matches[Merge with unique constraints and conflicting matches] +* xref::clauses/merge.adoc#query-merge-using-unique-constraints[Using property uniqueness constraints with `MERGE`] +** xref::clauses/merge.adoc#merge-merge-using-unique-constraints-creates-a-new-node-if-no-node-is-found[Merge using property uniqueness constraints creates a new node if no node is found] +** xref::clauses/merge.adoc#merge-merge-using-unique-constraints-matches-an-existing-node[Merge using property uniqueness constraints matches an existing node] +** xref::clauses/merge.adoc#merge-merge-with-unique-constraints-and-partial-matches[Merge with property uniqueness constraints and partial matches] +** xref::clauses/merge.adoc#merge-merge-with-unique-constraints-and-conflicting-matches[Merge with property uniqueness constraints and conflicting matches] * xref::clauses/merge.adoc#merge-using-map-parameters-with-merge[Using map parameters with `MERGE`] [[query-merge-introduction]] @@ -55,8 +55,8 @@ If partial matches are needed, this can be accomplished by splitting a pattern u [IMPORTANT] ==== Under concurrent updates, `MERGE` only guarantees existence of the `MERGE` pattern, but not uniqueness. -To guarantee uniqueness of nodes with certain properties, a xref::constraints/index.adoc[unique constraint] should be used. -See xref::clauses/merge.adoc#query-merge-using-unique-constraints[Using unique constraints with `MERGE`] to see how `MERGE` can be used in combination with a unique constraint. +To guarantee uniqueness of nodes with certain properties, a xref::constraints/index.adoc[property uniqueness constraint] should be used. +See xref::clauses/merge.adoc#query-merge-using-unique-constraints[Using property uniqueness constraints with `MERGE`] to see how `MERGE` can be used in combination with a property uniqueness constraint. ==== As with `MATCH`, `MERGE` can match multiple occurrences of a pattern. @@ -489,15 +489,15 @@ Labels added: 5 [[query-merge-using-unique-constraints]] -== Using unique constraints with `MERGE` +== Using property uniqueness constraints with `MERGE` -Cypher prevents getting conflicting results from `MERGE` when using patterns that involve unique constraints. +Cypher prevents getting conflicting results from `MERGE` when using patterns that involve property uniqueness constraints. In this case, there must be at most one node that matches that pattern. -For example, given two unique constraints on `:Person(id)` and `:Person(ssn)`, a query such as `MERGE (n:Person {id: 12, ssn: 437})` will fail, if there are two different nodes (one with `id` 12 and one with `ssn` 437) or if there is only one node with only one of the properties. +For example, given two property uniqueness constraints on `:Person(id)` and `:Person(ssn)`, a query such as `MERGE (n:Person {id: 12, ssn: 437})` will fail, if there are two different nodes (one with `id` 12 and one with `ssn` 437), or if there is only one node with only one of the properties. In other words, there must be exactly one node that matches the pattern, or no matching nodes. -Note that the following examples assume the existence of unique constraints that have been created using: +Note that the following examples assume the existence of property uniqueness constraints that have been created using: [source, cypher, indent=0] ---- @@ -507,9 +507,9 @@ CREATE CONSTRAINT FOR (n:Person) REQUIRE n.role IS UNIQUE; [[merge-merge-using-unique-constraints-creates-a-new-node-if-no-node-is-found]] -=== Merge using unique constraints creates a new node if no node is found +=== Merge using property uniqueness constraints creates a new node if no node is found -Merge using unique constraints creates a new node if no node is found. +Merge using property uniqueness constraints creates a new node if no node is found. .Query [source, cypher, indent=0] @@ -534,9 +534,9 @@ Labels added: 1 [[merge-merge-using-unique-constraints-matches-an-existing-node]] -=== Merge using unique constraints matches an existing node +=== Merge using property uniqueness constraints matches an existing node -Merge using unique constraints matches an existing node. +Merge using property uniqueness constraints matches an existing node. .Query [source, cypher, indent=0] @@ -557,9 +557,9 @@ The `'oliver'` node already exists, so `MERGE` just matches it. [[merge-merge-with-unique-constraints-and-partial-matches]] -=== Merge with unique constraints and partial matches +=== Merge with property uniqueness constraints and partial matches -Merge using unique constraints fails when finding partial matches. +Merge using property uniqueness constraints fails when finding partial matches. .Query [source, cypher, indent=0] @@ -588,9 +588,9 @@ SET michael.role = 'Gordon Gekko' [[merge-merge-with-unique-constraints-and-conflicting-matches]] -=== Merge with unique constraints and conflicting matches +=== Merge with property uniqueness constraints and conflicting matches -Merge using unique constraints fails when finding conflicting matches. +Merge using property uniqueness constraints fails when finding conflicting matches. .Query [source, cypher, indent=0] diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc index 74fed31b0..0a440f566 100644 --- a/modules/ROOT/pages/constraints/examples.adoc +++ b/modules/ROOT/pages/constraints/examples.adoc @@ -1,6 +1,6 @@ :description: Examples of how to manage constraints used for ensuring data integrity. -[[administration-constraints-examples]] +[[constraints-examples]] = Examples [abstract] @@ -9,25 +9,26 @@ Examples of how to manage constraints used for ensuring data integrity. -- -[[administration-constraints-unique-nodes]] -== Unique node property constraints +[[constraints-examples-node-uniqueness]] +== Node property uniqueness constraints -* xref::constraints/examples.adoc#administration-constraints-create-a-unique-constraint[] -* xref::constraints/examples.adoc#administration-constraints-create-a-unique-constraint-only-if-it-does-not-already-exist[] -* xref::constraints/examples.adoc#administration-constraints-create-a-unique-constraint-with-specified-index-provider[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-an-already-existing-unique-property-constraint[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-a-unique-property-constraint-on-same-schema-as-existing-index[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-that-complies-with-unique-property-constraints[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-that-violates-a-unique-property-constraint[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-a-unique-property-constraint-due-to-conflicting-nodes[] +A node property uniqueness constraint ensures that all nodes with a particular label have a set of defined properties whose combined value is unique when existing. + +* xref::constraints/examples.adoc#constraints-create-a-node-uniqueness-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-node-uniqueness-constraint-if-not-exist[] +* xref::constraints/examples.adoc#constraints-create-a-node-uniqueness-constraint-with-index-provider[] +* xref::constraints/examples.adoc#constraints-create-an-already-existing-node-uniqueness-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-node-uniqueness-constraint-on-same-schema-as-existing-index[] +* xref::constraints/examples.adoc#constraints-create-a-node-that-complies-with-a-uniqueness-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-node-that-violates-a-uniqueness-constraint[] +* xref::constraints/examples.adoc#constraints-fail-to-create-a-uniqueness-constraint-due-to-conflicting-nodes[] [discrete] -[[administration-constraints-create-a-unique-constraint]] -=== Create a unique constraint +[[constraints-create-a-node-uniqueness-constraint]] +=== Create a node property uniqueness constraint -When creating a unique constraint, a name can be provided. -The constraint ensures that your database will never contain more than one node with a specific label and one property value. +When creating a property uniqueness constraint, a name can be provided. .+CREATE CONSTRAINT+ @@ -49,20 +50,20 @@ FOR (book:Book) REQUIRE book.isbn IS UNIQUE Unique constraints added: 1 ---- +[NOTE] +==== +The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. +==== + ====== [discrete] -[[administration-constraints-create-a-unique-constraint-only-if-it-does-not-already-exist]] -=== Create a unique constraint only if it does not already exist - -If it is not known whether a constraint exists or not, add `IF NOT EXISTS` to ensure it does. -The uniqueness constraint ensures that your database will never contain more than one node with a specific label and one property value. +[[constraints-create-a-node-uniqueness-constraint-if-not-exist]] +=== Handling existing constraints when creating a constraint -[NOTE] -==== -No constraint will be created if any other constraint with the given name or another uniqueness constraint on the same schema already exists. -==== +Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. +This will ensure that no error is thrown and no constraint is created if any other constraint with the given name or another node property uniqueness constraint on the same schema already exists. .+CREATE CONSTRAINT+ @@ -75,7 +76,7 @@ CREATE CONSTRAINT constraint_name IF NOT EXISTS FOR (book:Book) REQUIRE book.isbn IS UNIQUE ---- -Assuming no constraint with the given name or other uniqueness constraint on the same schema exists: +Assuming no constraint with the given name or other node property uniqueness constraint on the same schema exists: .Result [queryresult] @@ -86,18 +87,23 @@ Assuming no constraint with the given name or other uniqueness constraint on the Unique constraints added: 1 ---- +[NOTE] +==== +The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. +==== + ====== [discrete] -[[administration-constraints-create-a-unique-constraint-with-specified-index-provider]] -=== Create a unique constraint with specified index provider +[[constraints-create-a-node-uniqueness-constraint-with-index-provider]] +=== Specifying an index provider when creating a constraint -To create a unique constraint with a specific index provider for the backing index, the `OPTIONS` clause is used. +To create a property uniqueness constraint with a specific index provider for the backing index, the `OPTIONS` clause is used. The index type of the backing index is set with the `indexProvider` option. -Valid values for the index provider are: +The only valid value for the index provider is: * `range-1.0` label:default[] @@ -126,20 +132,28 @@ OPTIONS { Unique constraints added: 1 ---- +[NOTE] +==== +The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. +==== + ====== +There is no valid index configuration values for the constraint-backing range indexes. + [discrete] -[[administration-constraints-failure-to-create-an-already-existing-unique-property-constraint]] -=== Failure to create an already existing unique property constraint +[[constraints-create-an-already-existing-node-uniqueness-constraint]] +=== Creating an already existing constraint will fail .+CREATE CONSTRAINT+ ====== -Create a unique property constraint on the property `title` on nodes with the `Book` label, when that constraint already exists. +Create a property uniqueness constraint on the property `title` on nodes with the `Book` label, when that constraint already exists. //// +Set-up to get expected behavior: CREATE CONSTRAINT preExistingUnique FOR (book:Book) REQUIRE book.title IS UNIQUE //// @@ -158,20 +172,26 @@ Constraint already exists: Constraint( id=4, name='preExistingUnique', type='UNIQUENESS', schema=(:Book {title}), ownedIndex=3 ) ---- +[NOTE] +==== +The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6.0. +==== + ====== [discrete] -[[administration-constraints-failure-to-create-a-unique-property-constraint-on-same-schema-as-existing-index]] -=== Failure to create a unique property constraint on same schema as existing index +[[constraints-create-a-node-uniqueness-constraint-on-same-schema-as-existing-index]] +=== Creating a constraint on the same schema as an existing index will fail .+CREATE CONSTRAINT+ ====== -Create a unique property constraint on the property `wordCount` on nodes with the `Book` label, when an index already exists on that label and property combination. +Create a property uniqueness constraint on the property `wordCount` on nodes with the `Book` label, when an index already exists on that label and property combination. //// +Set-up to get expected behavior: CREATE INDEX FOR (book:Book) ON (book.wordCount) //// @@ -194,8 +214,8 @@ A constraint cannot be created until the index has been dropped. [discrete] -[[administration-constraints-create-a-node-that-complies-with-unique-property-constraints]] -=== Create a node that complies with unique property constraints +[[constraints-create-a-node-that-complies-with-a-uniqueness-constraint]] +=== Creating a node that complies with an existing constraint .+CREATE NODE+ @@ -204,6 +224,7 @@ A constraint cannot be created until the index has been dropped. Create a `Book` node with an `isbn` that is not already in the database. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS UNIQUE //// @@ -228,8 +249,8 @@ Labels added: 1 [discrete] -[[administration-constraints-create-a-node-that-violates-a-unique-property-constraint]] -=== Create a node that violates a unique property constraint +[[constraints-create-a-node-that-violates-a-uniqueness-constraint]] +=== Creating a node that violates an existing constraint will fail .+CREATE NODE+ @@ -238,6 +259,7 @@ Labels added: 1 Create a `Book` node with an `isbn` that is already used in the database. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS UNIQUE CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) //// @@ -260,16 +282,17 @@ Node(0) already exists with label `Book` and property `isbn` = '1449356265' [discrete] -[[administration-constraints-failure-to-create-a-unique-property-constraint-due-to-conflicting-nodes]] -=== Failure to create a unique property constraint due to conflicting nodes +[[constraints-fail-to-create-a-uniqueness-constraint-due-to-conflicting-nodes]] +=== Creating a constraint when there exist conflicting nodes will fail .+CREATE CONSTRAINT+ ====== -Create a unique property constraint on the property `isbn` on nodes with the `Book` label when there are two nodes with the same `isbn`. +Create a property uniqueness constraint on the property `isbn` on nodes with the `Book` label when there are two nodes with the same `isbn`. //// +Set-up to get expected behavior: CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) //// @@ -281,39 +304,333 @@ CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS UNIQUE ---- In this case the constraint can not be created because it is violated by existing data. -You may choose to use xref::indexes-for-search-performance.adoc[] instead or remove the offending nodes and then re-apply the constraint. +Either use xref::indexes-for-search-performance.adoc[] instead, or remove the offending nodes and then re-apply the constraint. .Error message [source, "error message", role="noheader"] ---- -Unable to create Constraint( name='constraint_62365a16', type='UNIQUENESS', -schema=(:Book {isbn}) ): +Unable to create Constraint( name='constraint_62365a16', type='UNIQUENESS', schema=(:Book {isbn}) ): Both Node(0) and Node(1) have the label `Book` and property `isbn` = '1449356265' ---- +[NOTE] +==== +The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6.0. +==== + +====== + + +[[constraints-examples-relationship-uniqueness]] +== Relationship property uniqueness constraints + +A relationship property uniqueness constraint ensures that all relationships with a particular relationship type have a set of defined properties whose combined value is unique when existing. + +* xref::constraints/examples.adoc#constraints-create-a-relationship-uniqueness-constraints[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-uniqueness-constraints-if-not-exist[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-uniqueness-constraints-with-index-provider[] +* xref::constraints/examples.adoc#constraints-create-an-already-existing-relationship-uniqueness-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-uniqueness-constraint-on-same-schema-as-existing-index[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-that-complies-with-a-uniqueness-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-that-violates-a-uniqueness-constraint[] +* xref::constraints/examples.adoc#constraints-fail-to-create-a-uniqueness-constraint-due-to-conflicting-relationships[] + + +[discrete] +[[constraints-create-a-relationship-uniqueness-constraints]] +=== Create a relationship property uniqueness constraint + +When creating a property uniqueness constraint, a name can be provided. + + +.+CREATE CONSTRAINT+ +====== + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT constraint_name +FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +---- + +.Result +[queryresult] +---- ++-------------------+ +| No data returned. | ++-------------------+ +Relationship uniqueness constraints added: 1 +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-uniqueness-constraints-if-not-exist]] +=== Handling existing constraints when creating a constraint + +Creating an already existing constraint will fail. +To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. +This will ensure that no error is thrown and no constraint is created if any other constraint with the given name or another relationship property uniqueness constraint on the same schema already exists. + + +.+CREATE CONSTRAINT+ +====== + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT constraint_name IF NOT EXISTS +FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +---- + +Assuming no constraint with the given name or other relationship property uniqueness constraint on the same schema exists: + +.Result +[queryresult] +---- ++-------------------+ +| No data returned. | ++-------------------+ +Relationship uniqueness constraints added: 1 +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-uniqueness-constraints-with-index-provider]] +=== Specifying an index provider when creating a constraint + +To create a property uniqueness constraint with a specific index provider for the backing index, the `OPTIONS` clause is used. + +The index type of the backing index is set with the `indexProvider` option. + +The only valid value for the index provider is: + +* `range-1.0` label:default[] + +// Only one valid value exists for the index provider in Neo4j 5.0 + + +.+CREATE CONSTRAINT+ +====== + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT constraint_with_options +FOR ()-[friend:FRIENDS_WITH]-() REQUIRE (friend.nickname, friend.since) IS UNIQUE +OPTIONS { + indexProvider: 'range-1.0', +} +---- + +.Result +[queryresult] +---- ++-------------------+ +| No data returned. | ++-------------------+ +Relationship uniqueness constraints added: 1 +---- + +====== + +There are no valid index configuration values for the constraint-backing range indexes. + + +[discrete] +[[constraints-create-an-already-existing-relationship-uniqueness-constraint]] +=== Creating an already existing constraint will fail + + +.+CREATE CONSTRAINT+ +====== + +Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type, when that constraint already exists. + +//// +Set-up to get expected behavior: +CREATE CONSTRAINT preExistingUnique FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +//// + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +---- + +In this case, the constraint cannot be created because it already exists. + +.Error message +[source, "error message", role="noheader"] +---- +Constraint already exists: +Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', schema=()-[:FRIENDS_WITH {nickname}]-(), ownedIndex=3 ) +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-uniqueness-constraint-on-same-schema-as-existing-index]] +=== Creating a constraint on the same schema as an existing index will fail + + +.+CREATE CONSTRAINT+ +====== + +Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type, when an index already exists on that relationship type and property combination. + +//// +Set-up to get expected behavior: +CREATE INDEX FOR ()-[friend:FRIENDS_WITH]-() ON (friend.nickname) +//// + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +---- + +In this case, the constraint cannot be created because there already exists an index covering that schema. + +.Error message +[source, "error message", role="noheader"] +---- +There already exists an index ()-[:FRIENDS_WITH {nickname}]-(). +A constraint cannot be created until the index has been dropped. +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-that-complies-with-a-uniqueness-constraint]] +=== Creating a relationship that complies with an existing constraint + + +.+CREATE RELATIONSHIP+ +====== + +Create a `FRIENDS_WITH` relationship with an `nickname` that is not already in the database. + +//// +Set-up to get expected behavior: +CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +//// + +.Query +[source, cypher, indent=0] +---- +CREATE (:Person {name: 'Josefin'})-[:FRIENDS_WITH {nickname: 'Mimi'}]->(:Person {name: 'Emilia'}) +---- + +.Result +[queryresult] +---- ++-------------------+ +| No data returned. | ++-------------------+ +Nodes created: 2 +Relationships created: 1 +Properties set: 3 +Labels added: 2 +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-that-violates-a-uniqueness-constraint]] +=== Creating a relationship that violates an existing constraint will fail + + +.+CREATE RELATIONSHIP+ +====== + +Create a `FRIENDS_WITH` relationship with an `nickname` that is already used in the database. + +//// +Set-up to get expected behavior: +CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +CREATE (:Person {name: 'Emma'}), (:Person {name: 'Josefin'})-[:FRIENDS_WITH {nickname: 'Mimi'}]->(:Person {name: 'Emilia'}) +//// + +.Query +[source, cypher, indent=0] +---- +MATCH (emma:Person {name: 'Emma'}), (emilia:Person {name: 'Emilia'}) +CREATE (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) +---- + +In this case, the relationship is not created in the graph. + +.Error message +[source, "error message", role="noheader"] +---- +Relationship(0) already exists with type `FRIENDS_WITH` and property `nickname` = 'Mimi' +---- + +====== + + +[discrete] +[[constraints-fail-to-create-a-uniqueness-constraint-due-to-conflicting-relationships]] +=== Creating a constraint when there exist conflicting relationships will fail + + +.+CREATE CONSTRAINT+ +====== + +Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type when there are two relationships with the same `nickname`. + +//// +Set-up to get expected behavior: +CREATE (emma:Person {name: 'Emma'}), (josefin:Person {name: 'Josefin'}), (emilia:Person {name: 'Emilia'}) +CREATE (josefin)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia), (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) +//// + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT friends FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +---- + +In this case, the constraint cannot be created because it is violated by existing data. +Either use xref::indexes-for-search-performance.adoc[] instead, or remove the offending relationships and then re-apply the constraint. + +.Error message +[source, "error message", role="noheader"] +---- +Unable to create Constraint( name='friends', type='RELATIONSHIP_UNIQUENESS', schema=()-[:FRIENDS_WITH {nickname}]-() ): +Both Relationship(0) and Relationship(1) have the type `FRIENDS_WITH` and property `nickname` = 'Mimi' +---- + ====== [role=enterprise-edition] -[[administration-constraints-prop-exist-nodes]] +[[constraints-examples-node-property-existence]] == Node property existence constraints -* xref::constraints/examples.adoc#administration-constraints-create-a-node-property-existence-constraint[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-property-existence-constraint-only-if-it-does-not-already-exist[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-an-already-existing-node-property-existence-constraint[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-that-complies-with-property-existence-constraints[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-that-violates-a-property-existence-constraint[] -* xref::constraints/examples.adoc#administration-constraints-removing-an-existence-constrained-node-property[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-a-node-property-existence-constraint-due-to-existing-node[] -//* xref::constraints/examples.adoc# +A node property existence constraint ensures that all nodes with a certain label have a certain property. + +* xref::constraints/examples.adoc#constraints-create-a-node-property-existence-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-node-property-existence-constraint-if-not-exist[] +* xref::constraints/examples.adoc#constraints-create-an-already-existing-node-property-existence-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-node-that-complies-with-a-property-existence-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-node-that-violates-a-property-existence-constraint[] +* xref::constraints/examples.adoc#constraints-removing-an-existence-constrained-node-property[] +* xref::constraints/examples.adoc#constraints-fail-to-create-a-property-existence-constraint-due-to-existing-node[] [discrete] -[[administration-constraints-create-a-node-property-existence-constraint]] +[[constraints-create-a-node-property-existence-constraint]] === Create a node property existence constraint When creating a node property existence constraint, a name can be provided. -The constraint ensures that all nodes with a certain label have a certain property. .+CREATE CONSTRAINT+ @@ -335,21 +652,29 @@ FOR (book:Book) REQUIRE book.isbn IS NOT NULL Property existence constraints added: 1 ---- +[NOTE] +==== +The statistics for property existence constraints will be split between nodes and relationships in Neo4j version 6.0. +For the node property existence constraints, they will say `Node property existence constraints`. +==== + ====== + [discrete] -[[administration-constraints-create-a-node-property-existence-constraint-only-if-it-does-not-already-exist]] -=== Create a node property existence constraint only if it does not already exist +[[constraints-create-a-node-property-existence-constraint-if-not-exist]] +=== Handling existing constraints when creating a constraint -If it is not known whether a constraint exists or not, add `IF NOT EXISTS` to ensure it does. -The node property existence constraint ensures that all nodes with a certain label have a certain property. -No constraint will be created if any other constraint with the given name or another node property existence constraint on the same schema already exists. +Creating an already existing constraint will fail. +To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. +This will ensure that no error is thrown and no constraint is created if any other constraint with the given name or another node property existence constraint on the same schema already existed. .+CREATE CONSTRAINT+ ====== //// +Set-up to get expected behavior: CREATE CONSTRAINT constraint_name FOR (book:Book) REQUIRE book.isbn IS UNIQUE //// @@ -374,8 +699,8 @@ Assuming a constraint with the name `constraint_name` already existed: [discrete] -[[administration-constraints-failure-to-create-an-already-existing-node-property-existence-constraint]] -=== Failure to create an already existing node property existence constraint +[[constraints-create-an-already-existing-node-property-existence-constraint]] +=== Creating an already existing constraint will fail .+CREATE CONSTRAINT+ @@ -384,6 +709,7 @@ Assuming a constraint with the name `constraint_name` already existed: Create a node property existence constraint on the property `title` on nodes with the `Book` label, when that constraint already exists. //// +Set-up to get expected behavior: CREATE CONSTRAINT preExistingNodePropExist FOR (book:Book) REQUIRE book.title IS NOT NULL //// @@ -407,8 +733,8 @@ Constraint( id=3, name='preExistingNodePropExist', type='NODE PROPERTY EXISTENCE [discrete] -[[administration-constraints-create-a-node-that-complies-with-property-existence-constraints]] -=== Create a node that complies with property existence constraints +[[constraints-create-a-node-that-complies-with-a-property-existence-constraint]] +=== Creating a node that complies with an existing constraint .+CREATE NODE+ @@ -417,6 +743,7 @@ Constraint( id=3, name='preExistingNodePropExist', type='NODE PROPERTY EXISTENCE Create a `Book` node with an `isbn` property. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS NOT NULL //// @@ -441,8 +768,8 @@ Labels added: 1 [discrete] -[[administration-constraints-create-a-node-that-violates-a-property-existence-constraint]] -=== Create a node that violates a property existence constraint +[[constraints-create-a-node-that-violates-a-property-existence-constraint]] +=== Creating a node that violates an existing constraint will fail .+CREATE NODE+ @@ -451,6 +778,7 @@ Labels added: 1 Trying to create a `Book` node without an `isbn` property, given a property existence constraint on `:Book(isbn)`. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS NOT NULL //// @@ -472,8 +800,8 @@ Node(0) with label `Book` must have the property `isbn` [discrete] -[[administration-constraints-removing-an-existence-constrained-node-property]] -=== Removing an existence constrained node property +[[constraints-removing-an-existence-constrained-node-property]] +=== Removing an existence constrained node property will fail .+REMOVE PROPERTY+ @@ -482,6 +810,7 @@ Node(0) with label `Book` must have the property `isbn` Trying to remove the `isbn` property from an existing node `book`, given a property existence constraint on `:Book(isbn)`. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS NOT NULL CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) //// @@ -505,8 +834,8 @@ Node(0) with label `Book` must have the property `isbn` [discrete] -[[administration-constraints-failure-to-create-a-node-property-existence-constraint-due-to-existing-node]] -=== Failure to create a node property existence constraint due to existing node +[[constraints-fail-to-create-a-property-existence-constraint-due-to-existing-node]] +=== Creating a constraint when there exist conflicting nodes will fail .+CREATE CONSTRAINT+ @@ -515,6 +844,7 @@ Node(0) with label `Book` must have the property `isbn` Create a constraint on the property `isbn` on nodes with the `Book` label when there already exists a node without an `isbn`. //// +Set-up to get expected behavior: CREATE (book:Book {title: 'Graph Databases'}) //// @@ -524,13 +854,13 @@ CREATE (book:Book {title: 'Graph Databases'}) CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS NOT NULL ---- -In this case the constraint can't be created because it is violated by existing data. We may choose to remove the offending nodes and then re-apply the constraint. +In this case the constraint can't be created because it is violated by existing data. +Remove the offending nodes and then re-apply the constraint. .Error message [source, "error message", role="noheader"] ---- -Unable to create Constraint( type='NODE PROPERTY EXISTENCE', schema=(:Book -{isbn}) ): +Unable to create Constraint( type='NODE PROPERTY EXISTENCE', schema=(:Book {isbn}) ): Node(0) with label `Book` must have the property `isbn` ---- @@ -538,24 +868,25 @@ Node(0) with label `Book` must have the property `isbn` [role=enterprise-edition] -[[administration-constraints-prop-exist-rels]] +[[constraints-examples-relationship-property-existence]] == Relationship property existence constraints -* xref::constraints/examples.adoc#administration-constraints-create-a-relationship-property-existence-constraint[] -* xref::constraints/examples.adoc#administration-constraints-create-a-relationship-property-existence-constraint-only-if-it-does-not-already-exist[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-an-already-existing-relationship-property-existence-constraint[] -* xref::constraints/examples.adoc#administration-constraints-create-a-relationship-that-complies-with-property-existence-constraints[] -* xref::constraints/examples.adoc#administration-constraints-create-a-relationship-that-violates-a-property-existence-constraint[] -* xref::constraints/examples.adoc#administration-constraints-removing-an-existence-constrained-relationship-property[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-a-relationship-property-existence-constraint-due-to-existing-relationship[] +A relationship property existence constraint ensures that all relationships with a certain type have a certain property. + +* xref::constraints/examples.adoc#constraints-create-a-relationship-property-existence-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-property-existence-constraint-if-not-exist[] +* xref::constraints/examples.adoc#constraints-create-an-already-existing-relationship-property-existence-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-that-complies-with-a-property-existence-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-that-violates-a-property-existence-constraint[] +* xref::constraints/examples.adoc#constraints-removing-an-existence-constrained-relationship-property[] +* xref::constraints/examples.adoc#constraints-fail-to-create-a-property-existence-constraint-due-to-existing-relationship[] [discrete] -[[administration-constraints-create-a-relationship-property-existence-constraint]] +[[constraints-create-a-relationship-property-existence-constraint]] === Create a relationship property existence constraint When creating a relationship property existence constraint, a name can be provided. -The constraint ensures all relationships with a certain type have a certain property. .+CREATE CONSTRAINT+ @@ -577,22 +908,29 @@ FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL Property existence constraints added: 1 ---- +[NOTE] +==== +The statistics for property existence constraints will be split between nodes and relationships in Neo4j version 6.0. +For the relationship property existence constraints, they will say `Relationship property existence constraints`. +==== + ====== [discrete] -[[administration-constraints-create-a-relationship-property-existence-constraint-only-if-it-does-not-already-exist]] -=== Create a relationship property existence constraint only if it does not already exist +[[constraints-create-a-relationship-property-existence-constraint-if-not-exist]] +=== Handling existing constraints when creating a constraint -If it is not known whether a constraint exists or not, add `IF NOT EXISTS` to ensure it does. -The relationship property existence constraint ensures all relationships with a certain type have a certain property. -No constraint will be created if any other constraint with the given name or another relationship property existence constraint on the same schema already exists. +Creating an already existing constraint will fail. +To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. +This will ensure that no error is thrown and no constraint is created if any other constraint with the given name or another relationship property existence constraint on the same schema already existed. .+CREATE CONSTRAINT+ ====== //// +Set-up to get expected behavior: CREATE CONSTRAINT constraint_name FOR (book:Book) REQUIRE book.isbn IS NOT NULL //// @@ -617,8 +955,8 @@ Assuming a constraint with the name `constraint_name` already existed: [discrete] -[[administration-constraints-failure-to-create-an-already-existing-relationship-property-existence-constraint]] -=== Failure to create an already existing relationship property existence constraint +[[constraints-create-an-already-existing-relationship-property-existence-constraint]] +=== Creating an already existing constraint will fail .+CREATE CONSTRAINT+ @@ -627,6 +965,7 @@ Assuming a constraint with the name `constraint_name` already existed: Create a named relationship property existence constraint on the property `week` on relationships with the `LIKED` type, when a constraint with the given name already exists. //// +Set-up to get expected behavior: CREATE CONSTRAINT relPropExist FOR ()-[like:LIKED]-() REQUIRE like.since IS NOT NULL //// @@ -649,8 +988,8 @@ There already exists a constraint called 'relPropExist'. [discrete] -[[administration-constraints-create-a-relationship-that-complies-with-property-existence-constraints]] -=== Create a relationship that complies with property existence constraints +[[constraints-create-a-relationship-that-complies-with-a-property-existence-constraint]] +=== Creating a relationship that complies with an existing constraint .+CREATE RELATIONSHIP+ @@ -659,6 +998,7 @@ There already exists a constraint called 'relPropExist'. Create a `LIKED` relationship with a `day` property. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL //// @@ -684,8 +1024,8 @@ Labels added: 2 [discrete] -[[administration-constraints-create-a-relationship-that-violates-a-property-existence-constraint]] -=== Create a relationship that violates a property existence constraint +[[constraints-create-a-relationship-that-violates-a-property-existence-constraint]] +=== Creating a relationship that violates an existing constraint will fail .+CREATE RELATIONSHIP+ @@ -694,6 +1034,7 @@ Labels added: 2 Trying to create a `LIKED` relationship without a `day` property, given a property existence constraint `:LIKED(day)`. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL //// @@ -715,8 +1056,8 @@ Relationship(0) with type `LIKED` must have the property `day` [discrete] -[[administration-constraints-removing-an-existence-constrained-relationship-property]] -=== Removing an existence constrained relationship property +[[constraints-removing-an-existence-constrained-relationship-property]] +=== Removing an existence constrained relationship property will fail .+REMOVE PROPERTY+ @@ -725,6 +1066,7 @@ Relationship(0) with type `LIKED` must have the property `day` Trying to remove the `day` property from an existing relationship `like` of type `LIKED`, given a property existence constraint `:LIKED(day)`. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL CREATE (user:User)-[like:LIKED {day: 'yesterday'}]->(book:Book) //// @@ -747,8 +1089,8 @@ Relationship(0) with type `LIKED` must have the property `day` [discrete] -[[administration-constraints-failure-to-create-a-relationship-property-existence-constraint-due-to-existing-relationship]] -=== Failure to create a relationship property existence constraint due to existing relationship +[[constraints-fail-to-create-a-property-existence-constraint-due-to-existing-relationship]] +=== Creating a constraint when there exist conflicting relationships will fail .+CREATE CONSTRAINT+ @@ -757,6 +1099,7 @@ Relationship(0) with type `LIKED` must have the property `day` Create a constraint on the property `day` on relationships with the `LIKED` type when there already exists a relationship without a property named `day`. //// +Set-up to get expected behavior: CREATE (user:User)-[like:LIKED]->(book:Book) //// @@ -766,13 +1109,13 @@ CREATE (user:User)-[like:LIKED]->(book:Book) CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL ---- -In this case the constraint can not be created because it is violated by existing data. We may choose to remove the offending relationships and then re-apply the constraint. +In this case the constraint can not be created because it is violated by existing data. +Remove the offending relationships and then re-apply the constraint. .Error message [source, "error message", role="noheader"] ---- -Unable to create Constraint( type='RELATIONSHIP PROPERTY EXISTENCE', -schema=-[:LIKED {day}]- ): +Unable to create Constraint( type='RELATIONSHIP PROPERTY EXISTENCE', schema=-[:LIKED {day}]- ): Relationship(0) with type `LIKED` must have the property `day` ---- @@ -780,26 +1123,27 @@ Relationship(0) with type `LIKED` must have the property `day` [role=enterprise-edition] -[[administration-constraints-node-key]] +[[constraints-examples-node-key]] == Node key constraints -* xref::constraints/examples.adoc#administration-constraints-create-a-node-key-constraint[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-key-constraint-only-if-it-does-not-already-exist[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-key-constraint-with-specified-index-provider[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-a-node-key-constraint-when-a-unique-property-constraint-exists-on-the-same-schema[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-a-node-key-constraint-with-the-same-name-as-existing-index[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-that-complies-with-node-key-constraints[] -* xref::constraints/examples.adoc#administration-constraints-create-a-node-that-violates-a-node-key-constraint[] -* xref::constraints/examples.adoc#administration-constraints-removing-a-node-key-constrained-property[] -* xref::constraints/examples.adoc#administration-constraints-failure-to-create-a-node-key-constraint-due-to-existing-node[] +A node key constraint ensures that all nodes with a particular label have a set of defined properties whose combined value is unique and all properties in the set are present. + +* xref::constraints/examples.adoc#constraints-create-a-node-key-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-node-key-constraint-if-not-exist[] +* xref::constraints/examples.adoc#constraints-create-a-node-key-constraint-with-index-provider[] +* xref::constraints/examples.adoc#constraints-node-key-and-uniqueness-constraint-on-the-same-schema[] +* xref::constraints/examples.adoc#constraints-create-a-node-key-constraint-with-the-same-name-as-existing-index[] +* xref::constraints/examples.adoc#constraints-create-a-node-that-complies-with-a-node-key-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-node-that-violates-a-node-key-constraint[] +* xref::constraints/examples.adoc#constraints-removing-a-node-key-constrained-property[] +* xref::constraints/examples.adoc#constraints-fail-to-create-a-node-key-constraint-due-to-existing-node[] [discrete] -[[administration-constraints-create-a-node-key-constraint]] +[[constraints-create-a-node-key-constraint]] === Create a node key constraint When creating a node key constraint, a name can be provided. -The constraint ensures that all nodes with a particular label have a set of defined properties whose combined value is unique and all properties in the set are present. .+CREATE CONSTRAINT+ @@ -825,18 +1169,19 @@ Node key constraints added: 1 [discrete] -[[administration-constraints-create-a-node-key-constraint-only-if-it-does-not-already-exist]] -=== Create a node key constraint only if it does not already exist +[[constraints-create-a-node-key-constraint-if-not-exist]] +=== Handling existing constraints when creating a constraint -If it is not known whether a constraint exists or not, add `IF NOT EXISTS` to ensure it does. -The node key constraint ensures that all nodes with a particular label have a set of defined properties whose combined value is unique and all properties in the set are present. -No constraint will be created if any other constraint with the given name or another node key constraint on the same schema already exists. +Creating an already existing constraint will fail. +To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. +This will ensure that no error is thrown and no constraint is created if any other constraint with the given name or another node key constraint on the same schema already exists. .+CREATE CONSTRAINT+ ====== //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY //// @@ -861,14 +1206,14 @@ Assuming a node key constraint on `(:Person {firstname, surname})` already exist [discrete] -[[administration-constraints-create-a-node-key-constraint-with-specified-index-provider]] -=== Create a node key constraint with specified index provider +[[constraints-create-a-node-key-constraint-with-index-provider]] +=== Specifying an index provider when creating a constraint To create a node key constraint with a specific index provider for the backing index, the `OPTIONS` clause is used. The index type of the backing index is set with the `indexProvider` option. -Valid values for the index provider are: +The only valid value for the index provider is: * `range-1.0` label:default[] @@ -897,18 +1242,21 @@ Node key constraints added: 1 ====== +There is no valid index configuration values for the constraint-backing range indexes. + [discrete] -[[administration-constraints-failure-to-create-a-node-key-constraint-when-a-unique-property-constraint-exists-on-the-same-schema]] -=== Failure to create a node key constraint when a unique property constraint exists on the same schema +[[constraints-node-key-and-uniqueness-constraint-on-the-same-schema]] +=== Node key and property uniqueness constraints are not allowed on the same schema .+CREATE CONSTRAINT+ ====== -Create a node key constraint on the properties `firstname` and `age` on nodes with the `Person` label, when a unique property constraint already exists on the same label and property combination. +Create a node key constraint on the properties `firstname` and `age` on nodes with the `Person` label, when a property uniqueness constraint already exists on the same label and property combination. //// +Set-up to get expected behavior: CREATE CONSTRAINT preExistingUnique FOR (p:Person) REQUIRE (p.firstname, p.age) IS UNIQUE //// @@ -931,8 +1279,8 @@ Constraint( id=4, name='preExistingUnique', type='UNIQUENESS', schema=(:Person { [discrete] -[[administration-constraints-failure-to-create-a-node-key-constraint-with-the-same-name-as-existing-index]] -=== Failure to create a node key constraint with the same name as existing index +[[constraints-create-a-node-key-constraint-with-the-same-name-as-existing-index]] +=== Creating a constraint on same name as an existing index will fail .+CREATE CONSTRAINT+ @@ -941,6 +1289,7 @@ Constraint( id=4, name='preExistingUnique', type='UNIQUENESS', schema=(:Person { Create a named node key constraint on the property `title` on nodes with the `Book` label, when an index already exists with the given name. //// +Set-up to get expected behavior: CREATE INDEX bookTitle FOR (book:ComicBook) ON (book.title) //// @@ -963,8 +1312,8 @@ There already exists an index called 'bookTitle'. [discrete] -[[administration-constraints-create-a-node-that-complies-with-node-key-constraints]] -=== Create a node that complies with node key constraints +[[constraints-create-a-node-that-complies-with-a-node-key-constraint]] +=== Creating a node that complies with an existing constraint .+CREATE NODE+ @@ -973,6 +1322,7 @@ There already exists an index called 'bookTitle'. Create a `Person` node with both a `firstname` and `surname` property. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY //// @@ -997,8 +1347,8 @@ Labels added: 1 [discrete] -[[administration-constraints-create-a-node-that-violates-a-node-key-constraint]] -=== Create a node that violates a node key constraint +[[constraints-create-a-node-that-violates-a-node-key-constraint]] +=== Creating a node that violates an existing constraint will fail .+CREATE NODE+ @@ -1007,6 +1357,7 @@ Labels added: 1 Trying to create a `Person` node without a `surname` property, given a node key constraint on `:Person(firstname, surname)`, will fail. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY //// @@ -1021,15 +1372,15 @@ In this case the node is not created in the graph. .Error message [source, "error message", role="noheader"] ---- -Node(0) with label `Person` must have the properties (firstname, surname) +Node(0) with label `Person` must have the properties (`firstname`, `surname`) ---- ====== [discrete] -[[administration-constraints-removing-a-node-key-constrained-property]] -=== Removing a +NODE KEY+-constrained property +[[constraints-removing-a-node-key-constrained-property]] +=== Removing a +NODE KEY+-constrained property will fail .+REMOVE PROPERTY+ @@ -1038,6 +1389,7 @@ Node(0) with label `Person` must have the properties (firstname, surname) Trying to remove the `surname` property from an existing node `Person`, given a `NODE KEY` constraint on `:Person(firstname, surname)`. //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY CREATE (p:Person {firstname: 'John', surname: 'Wood', age: 55}) //// @@ -1053,15 +1405,15 @@ In this case the property is not removed. .Error message [source, "error message", role="noheader"] ---- -Node(0) with label `Person` must have the properties (firstname, surname) +Node(0) with label `Person` must have the properties (`firstname`, `surname`) ---- ====== [discrete] -[[administration-constraints-failure-to-create-a-node-key-constraint-due-to-existing-node]] -=== Failure to create a node key constraint due to existing node +[[constraints-fail-to-create-a-node-key-constraint-due-to-existing-node]] +=== Creating a constraint when there exist conflicting node will fail .+CREATE CONSTRAINT+ @@ -1070,6 +1422,7 @@ Node(0) with label `Person` must have the properties (firstname, surname) Trying to create a node key constraint on the property `surname` on nodes with the `Person` label will fail when a node without a `surname` already exists in the database. //// +Set-up to get expected behavior: CREATE (p:Person {firstname: 'John', age: 55}) //// @@ -1080,39 +1433,369 @@ CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY ---- In this case the node key constraint can not be created because it is violated by existing data. -We may choose to remove the offending nodes and then re-apply the constraint. +Either use xref::indexes-for-search-performance.adoc[] instead, or remove the offending nodes and then re-apply the constraint. + +.Error message +[source, "error message", role="noheader"] +---- +Unable to create Constraint( type='NODE KEY', schema=(:Person {firstname, surname}) ): +Node(0) with label `Person` must have the properties (`firstname`, `surname`) +---- + +====== + + +[role=enterprise-edition] +[[constraints-examples-relationship-key]] +== Relationship key constraints + +A relationship key constraint ensures that all relationships with a particular relationship type have a set of defined properties whose combined value is unique. +It also ensures that all properties in the set are present. + +* xref::constraints/examples.adoc#constraints-create-a-relationship-key-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-key-constraint-if-not-exist[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-key-constraint-with-index-provider[] +* xref::constraints/examples.adoc#constraints-relationship-key-and-uniqueness-constraint-on-the-same-schema[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-key-constraint-with-the-same-name-as-existing-index[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-that-complies-with-a-relationship-key-constraint[] +* xref::constraints/examples.adoc#constraints-create-a-relationship-that-violates-a-relationship-key-constraint[] +* xref::constraints/examples.adoc#constraints-removing-a-relationship-key-constrained-property[] +* xref::constraints/examples.adoc#constraints-fail-to-create-a-relationship-key-constraint-due-to-existing-relationship[] + + +[discrete] +[[constraints-create-a-relationship-key-constraint]] +=== Create a relationship key constraint + +When creating a relationship key constraint, a name can be provided. + + +.+CREATE CONSTRAINT+ +====== + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT constraint_name +FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY +---- + +.Result +[queryresult] +---- ++-------------------+ +| No data returned. | ++-------------------+ +Relationship key constraints added: 1 +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-key-constraint-if-not-exist]] +=== Handling existing constraints when creating a constraint + +Creating an already existing constraint will fail. +To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. +This will ensure that no error is thrown and no constraint is created if any other constraint with the given name or another relationship key constraint on the same schema already exists. + + +.+CREATE CONSTRAINT+ +====== + +//// +Set-up to get expected behavior: +CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY +//// + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT constraint_name IF NOT EXISTS +FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY +---- + +Assuming a relationship key constraint on `()-[:ROAD {startPoint, endPoint}]-()` already existed: + +.Result +[queryresult] +---- ++--------------------------------------------+ +| No data returned, and nothing was changed. | ++--------------------------------------------+ +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-key-constraint-with-index-provider]] +=== Specifying an index provider when creating a constraint + +To create a relationship key constraint with a specific index provider for the backing index, the `OPTIONS` clause is used. + +The index type of the backing index is set with the `indexProvider` option. + +The only valid value for the index provider is: + +* `range-1.0` label:default[] + + +.+CREATE CONSTRAINT+ +====== + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT constraint_with_provider +FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY +OPTIONS { + indexProvider: 'range-1.0' +} +---- + +.Result +[queryresult] +---- ++-------------------+ +| No data returned. | ++-------------------+ +Relationship key constraints added: 1 +---- + +====== + +There is no valid index configuration values for the constraint-backing range indexes. + + +[discrete] +[[constraints-relationship-key-and-uniqueness-constraint-on-the-same-schema]] +=== Relationship key and property uniqueness constraints are not allowed on the same schema + + +.+CREATE CONSTRAINT+ +====== + +Create a relationship key constraint on the properties `startPoint` and `endPoint` on relationships with the `ROAD` relationship type, when a property uniqueness constraint already exists on the same relationship type and property combination. + +//// +Set-up to get expected behavior: +CREATE CONSTRAINT preExistingUnique FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE +//// + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY +---- + +In this case, the constraint cannot be created because there already exists a conflicting constraint on that relationship type and property combination. + +.Error message +[source, "error message", role="noheader"] +---- +Constraint already exists: +Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', schema=()-[:ROAD {startPoint, endPoint}]-(), ownedIndex=3 ) +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-key-constraint-with-the-same-name-as-existing-index]] +=== Creating a constraint on same name as an existing index will fail + + +.+CREATE CONSTRAINT+ +====== + +Create a named relationship key constraint on the property `coordinates` on relationships with the `INTERSECTION` relationship type, when an index already exists with the given name. + +//// +Set-up to get expected behavior: +CREATE INDEX intersections FOR ()-[intersect:Roundabout]-() ON (intersect.coordinates) +//// + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT intersections +FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY +---- + +In this case, the constraint cannot be created because there already exists an index with the given name. .Error message [source, "error message", role="noheader"] ---- -Unable to create Constraint( type='NODE PROPERTY EXISTENCE', schema=(:Person -{firstname, surname}) ): -Node(0) with label `Person` must have the properties (firstname, surname) +There already exists an index called 'intersections'. ---- ====== -[[administration-constraints-drop-constraint]] +[discrete] +[[constraints-create-a-relationship-that-complies-with-a-relationship-key-constraint]] +=== Creating a relationship that complies with an existing constraint + + +.+CREATE RELATIONSHIP+ +====== + +Create a `ROAD` relationship with both a `startPoint` and `endPoint` property. + +//// +Set-up to get expected behavior: +CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY +CREATE (:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) +//// + +.Query +[source, cypher, indent=0] +---- +MATCH (a:Intersection {name: 'a'}), (b:Intersection {name: 'b'}) +CREATE (a)-[:ROAD {startPoint: a.coordinates, endPoint: b.coordinates}]->(b) +---- + +.Result +[queryresult] +---- ++-------------------+ +| No data returned. | ++-------------------+ +Relationships created: 1 +Properties set: 2 +---- + +====== + + +[discrete] +[[constraints-create-a-relationship-that-violates-a-relationship-key-constraint]] +=== Creating a relationship that violates an existing constraint will fail + + +.+CREATE RELATIONSHIP+ +====== + +Trying to create a `INTERSECTION` relationship without a `coordinates` property, given a relationship key constraint on `:INTERSECTION(coordinates)`, will fail. + +//// +Set-up to get expected behavior: +CREATE CONSTRAINT FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY +CREATE (:Road {name: 'a'}), (:Road {name: 'b'}) +//// + +.Query +[source, cypher, indent=0] +---- +MATCH (a:Road {name: 'a'}), (b:Road {name: 'b'}) +CREATE (a)-[:INTERSECTION]->(b) +---- + +In this case, the relationship is not created in the graph. + +.Error message +[source, "error message", role="noheader"] +---- +Relationship(0) with type `INTERSECTION` must have the property `coordinates` +---- + +====== + + +[discrete] +[[constraints-removing-a-relationship-key-constrained-property]] +=== Removing a +RELATIONSHIP KEY+-constrained property will fail + + +.+REMOVE PROPERTY+ +====== + +Trying to remove the `endPoint` property from an existing relationship `ROAD`, given a `RELATIONSHIP KEY` constraint on `:ROAD(startPoint, endPoint)`. + +//// +Set-up to get expected behavior: +CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY +CREATE (a:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (b:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) +CREATE (a)-[:ROAD {startPoint: a.coordinates, endPoint: b.coordinates}]->(b) +//// + +.Query +[source, cypher, indent=0] +---- +MATCH ()-[r:ROAD {startPoint: point({x: 1, y:2}), endPoint: point({x: 2, y:5})}]->() REMOVE r.endPoint +---- + +In this case, the property is not removed. + +.Error message +[source, "error message", role="noheader"] +---- +Relationship(0) with type `ROAD` must have the properties (`startPoint`, `endPoint`) +---- + +====== + + +[discrete] +[[constraints-fail-to-create-a-relationship-key-constraint-due-to-existing-relationship]] +=== Creating a constraint when there exist conflicting relationships will fail + + +.+CREATE CONSTRAINT+ +====== + +Trying to create a relationship key constraint on the property `coordinates` on relationships with the `INTERSECTION` relationship type will fail when two relationships with identical `coordinates` already exists in the database. + +//// +Set-up to get expected behavior: +CREATE (a:Road {name: 'a'}), (b:Road {name: 'b'}) +CREATE (a)-[:INTERSECTION {coordinates: point({x:1, y:2})}]->(b) +CREATE (a)<-[:INTERSECTION {coordinates: point({x:1, y:2})}]-(b) +//// + +.Query +[source, cypher, indent=0] +---- +CREATE CONSTRAINT intersectionConstraint FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY +---- + +In this case, the relationship key constraint cannot be created because it is violated by existing data. +Either use xref::indexes-for-search-performance.adoc[] instead, or remove the offending relationships and then re-apply the constraint. + +.Error message +[source, "error message", role="noheader"] +---- +Unable to create Constraint( name='intersectionConstraint', type='RELATIONSHIP KEY', schema=()-[:INTERSECTION {coordinates}]-() ): +Both Relationship(0) and Relationship(1) have the type `INTERSECTION` and property `coordinates` = {geometry: {type: "Point", coordinates: [1.0, 2.0], crs: {type: link, properties: {href: "http://spatialreference.org/ref/sr-org/7203/", code: 7203}}}} +---- + +====== + + +[[constraints-examples-drop-constraint]] == Drop a constraint by name -* xref::constraints/examples.adoc#administration-constraints-drop-a-constraint[] -* xref::constraints/examples.adoc#administration-constraints-drop-a-non-existing-constraint[] +* xref::constraints/examples.adoc#constraints-drop-a-constraint[] +* xref::constraints/examples.adoc#constraints-drop-a-non-existing-constraint[] [discrete] -[[administration-constraints-drop-a-constraint]] +[[constraints-drop-a-constraint]] === Drop a constraint A constraint can be dropped using the name with the `DROP CONSTRAINT constraint_name` command. -It is the same command for unique property, property existence and node key constraints. -The name of the constraint can be found using the xref::constraints/syntax.adoc#administration-constraints-syntax-list[`SHOW CONSTRAINTS` command], given in the output column `name`. +It is the same command for uniqueness, property existence, and node/relationship key constraints. +The name of the constraint can be found using the xref::constraints/syntax.adoc#constraints-syntax-list[`SHOW CONSTRAINTS` command], given in the output column `name`. .+DROP CONSTRAINT+ ====== //// +Set-up to get expected behavior: CREATE CONSTRAINT constraint_name FOR (n:Person) REQUIRE (n.name) IS NOT NULL //// @@ -1135,11 +1818,11 @@ Named constraints removed: 1 [discrete] -[[administration-constraints-drop-a-non-existing-constraint]] +[[constraints-drop-a-non-existing-constraint]] === Drop a non-existing constraint If it is uncertain if any constraint with a given name exists and you want to drop it if it does but not get an error should it not, use `IF EXISTS`. -It is the same command for unique property, property existence and node key constraints. +It is the same command for uniqueness, property existence, and node/relationship key constraints. .+DROP CONSTRAINT+ ====== @@ -1161,15 +1844,15 @@ DROP CONSTRAINT missing_constraint_name IF EXISTS ====== -[[administration-constraints-list-constraint]] +[[constraints-examples-list-constraint]] == Listing constraints -* xref::constraints/examples.adoc#administration-constraints-listing-all-constraints[] -* xref::constraints/examples.adoc#administration-constraints-listing-constraints-with-filtering[] +* xref::constraints/examples.adoc#constraints-listing-all-constraints[] +* xref::constraints/examples.adoc#constraints-listing-constraints-with-filtering[] [discrete] -[[administration-constraints-listing-all-constraints]] +[[constraints-listing-all-constraints]] === Listing all constraints To list all constraints with the default output columns, the `SHOW CONSTRAINTS` command can be used. @@ -1178,7 +1861,7 @@ If all columns are required, use `SHOW CONSTRAINTS YIELD *`. [NOTE] ==== One of the output columns from `SHOW CONSTRAINTS` is the name of the constraint. -This can be used to drop the constraint with the xref::constraints/syntax.adoc#administration-constraints-syntax-drop[`DROP CONSTRAINT` command]. +This can be used to drop the constraint with the xref::constraints/syntax.adoc#constraints-syntax-drop[`DROP CONSTRAINT` command]. ==== @@ -1186,7 +1869,9 @@ This can be used to drop the constraint with the xref::constraints/syntax.adoc#a ====== //// -CREATE CONSTRAINT constraint_1bc95fcb FOR (n:Book) REQUIRE (n.isbn) IS UNIQUE +Set-up to get expected behavior: +CREATE CONSTRAINT isbnConstraint FOR (n:Book) REQUIRE (n.isbn) IS UNIQUE +CREATE CONSTRAINT roadConstraint FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE //// .Query @@ -1197,24 +1882,31 @@ SHOW CONSTRAINTS [queryresult] ---- -+-------------------------------------------------------------------------------------------------------------+ -| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | -+-------------------------------------------------------------------------------------------------------------+ -| 4 | "constraint_1bc95fcb" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | "constraint_1bc95fcb" | -+-------------------------------------------------------------------------------------------------------------+ -1 row ++------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | ++------------------------------------------------------------------------------------------------------------------------------------+ +| 4 | "isbnConstraint" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | "isbnConstraint" | +| 6 | "roadConstraint" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["ROAD"] | ["startPoint", "endPoint"] | "roadConstraint" | ++------------------------------------------------------------------------------------------------------------------------------------+ +2 rows ---- +[NOTE] +==== +The `type` column returns `UNIQUENESS` for the node property uniqueness constraint and `RELATIONSHIP_UNIQUENESS` for the relationship property uniqueness constraint. +The `type` for node property uniqueness constraint will be updated to `NODE_UNIQUENESS` in Neo4j version 6.0. +==== + ====== [discrete] -[[administration-constraints-listing-constraints-with-filtering]] +[[constraints-listing-constraints-with-filtering]] === Listing constraints with filtering One way of filtering the output from `SHOW CONSTRAINTS` by constraint type is the use of type keywords, -listed in xref::constraints/syntax.adoc#administration-constraints-syntax-list[Syntax for listing constraints]. -For example, to show only unique node property constraints, use `SHOW UNIQUE CONSTRAINTS`. +listed in the xref::constraints/syntax.adoc#constraints-syntax-list-type-filter[syntax for listing constraints type filter table]. +For example, to show only property uniqueness constraints, use `SHOW UNIQUENESS CONSTRAINTS`. Another more flexible way of filtering the output is to use the `WHERE` clause. An example is to only show constraints on relationships. @@ -1223,6 +1915,7 @@ An example is to only show constraints on relationships. ====== //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (n:Book) REQUIRE (n.isbn) IS UNIQUE CREATE CONSTRAINT FOR (book:Book) REQUIRE book.title IS NOT NULL CREATE CONSTRAINT `constraint_f076a74d` FOR ()-[r:KNOWS]-() REQUIRE r.since IS NOT NULL diff --git a/modules/ROOT/pages/constraints/index.adoc b/modules/ROOT/pages/constraints/index.adoc index 48a149bdf..472dc110e 100644 --- a/modules/ROOT/pages/constraints/index.adoc +++ b/modules/ROOT/pages/constraints/index.adoc @@ -1,6 +1,6 @@ :description: This section explains how to manage constraints used for ensuring data integrity. -[[administration-constraints]] +[[constraints]] = Constraints [abstract] @@ -14,9 +14,14 @@ This section explains how to manage constraints used for ensuring data integrity The following constraint types are available: *Unique node property constraints*:: -Unique property constraints ensure that property values are unique for all nodes with a specific label. -For unique property constraints on multiple properties, the combination of the property values is unique. -Unique constraints do not require all nodes to have a unique value for the properties listed -- nodes without all properties are not subject to this rule. +Unique node property constraints, or node property uniqueness constraints, ensure that property values are unique for all nodes with a specific label. +For property uniqueness constraints on multiple properties, the combination of the property values is unique. +Node property uniqueness constraints do not require all nodes to have a unique value for the properties listed (nodes without all properties are not subject to this rule). + +*Unique relationship property constraints*:: +Unique relationship property constraints, or relationship property uniqueness constraints, ensure that property values are unique for all relationships with a specific type. +For property uniqueness constraints on multiple properties, the combination of the property values is unique. +Relationship property uniqueness constraints do not require all relationships to have a unique value for the properties listed (relationships without all properties are not subject to this rule). *Node property existence constraints* label:enterprise-edition[]:: Node property existence constraints ensure that a property exists for all nodes with a specific label. @@ -24,7 +29,7 @@ Queries that try to create new nodes of the specified label, but without this pr The same is true for queries that try to remove the mandatory property. *Relationship property existence constraints* label:enterprise-edition[]:: -Property existence constraints ensure that a property exists for all relationships with a specific type. +Relationship property existence constraints ensure that a property exists for all relationships with a specific type. All queries that try to create relationships of the specified type, but without this property, will fail. The same is true for queries that try to remove the mandatory property. @@ -42,10 +47,24 @@ Queries attempting to do any of the following will fail: * Remove one of the mandatory properties. * Update the properties so that the combination of property values is no longer unique. +*Relationship key constraints* label:enterprise-edition[]:: +Relationship key constraints ensure that, for a given type and set of properties: ++ +[lowerroman] +. All the properties exist on all the relationships with that type. +. The combination of the property values is unique. + ++ +Queries attempting to do any of the following will fail: + +* Create new relationships without all the properties or where the combination of property values is not unique. +* Remove one of the mandatory properties. +* Update the properties so that the combination of property values is no longer unique. + [NOTE] ==== -Node key constraints, node property existence constraints and relationship property existence constraints are only available in Neo4j Enterprise Edition. +Node key constraints, relationship key constraints, node property existence constraints, and relationship property existence constraints are only available in Neo4j Enterprise Edition. Databases containing one of these constraint types cannot be opened using Neo4j Community Edition. ==== @@ -54,15 +73,15 @@ Databases containing one of these constraint types cannot be opened using Neo4j Creating a constraint has the following implications on indexes: -* Adding a node key or unique property constraint on a single property also adds an index on that property and therefore, an index of the same index type, label, and property combination cannot be added separately. -* Adding a node key or unique property constraint for a set of properties also adds an index on those properties and therefore, an index of the same index type, label, and properties combination cannot be added separately. +* Adding a node key, relationship key, or property uniqueness constraint on a single property also adds an index on that property, and therefore, an index of the same index type, label/relationship type, and property combination cannot be added separately. +* Adding a node key, relationship key, or property uniqueness constraint for a set of properties also adds an index on those properties, and therefore, an index of the same index type, label/relationship type, and properties combination cannot be added separately. * Cypher will use these indexes for lookups just like other indexes. Refer to xref::indexes-for-search-performance.adoc[] for more details on indexes. -* If a node key or unique property constraint is dropped and the backing index is still required, the index need to be created explicitly. +* If a node key, relationship key, or property uniqueness constraint is dropped and the backing index is still required, the index need to be created explicitly. Additionally, the following is true for constraints: -* A given label can have multiple constraints, and unique and property existence constraints can be combined on the same property. +* A given label or relationship type can have multiple constraints, and uniqueness and property existence constraints can be combined on the same property. * Adding constraints is an atomic operation that can take a while -- all existing data has to be scanned before Neo4j DBMS can turn the constraint 'on'. * Best practice is to give the constraint a name when it is created. If the constraint is not explicitly named, it will get an auto-generated name. diff --git a/modules/ROOT/pages/constraints/syntax.adoc b/modules/ROOT/pages/constraints/syntax.adoc index 5a759d93f..4ca00d0dd 100644 --- a/modules/ROOT/pages/constraints/syntax.adoc +++ b/modules/ROOT/pages/constraints/syntax.adoc @@ -1,6 +1,6 @@ :description: Syntax for how to manage constraints used for ensuring data integrity. -[[administration-constraints-syntax]] +[[constraints-syntax]] = Syntax :check-mark: icon:check[] @@ -9,17 +9,18 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. ==== -[[administration-constraints-syntax-create]] +[[constraints-syntax-create]] == Syntax for creating constraints Best practice when creating a constraint is to give the constraint a name. This name must be unique among both indexes and constraints. If a name is not explicitly given, a unique name will be auto-generated. -The create constraint command is optionally idempotent. This means its default behavior is to throw an error if an attempt is made to create the same constraint twice. +The `CREATE CONSTRAINT` command is optionally idempotent. +This means its default behavior is to throw an error if an attempt is made to create the same constraint twice. With the `IF NOT EXISTS` flag, no error is thrown and nothing happens should a constraint with the same name or same schema and constraint type already exist. It may still throw an error if conflicting data, indexes, or constraints exist. -Examples of this are nodes with missing properties, indexes with the same name, or constraints with same schema but a different constraint type. +Examples of this are nodes with missing properties, indexes with the same name, or constraints with same schema but a different conflicting constraint type. For constraints that are backed by an index, the index provider for the backing index can be specified using the `OPTIONS` clause. Only one valid value exists for the index provider, `range-1.0`, which is the default value. @@ -30,17 +31,17 @@ There is no supported index configuration for range indexes. Creating a constraint requires the xref::access-control/database-administration.adoc#access-control-database-administration-constraints[`CREATE CONSTRAINT` privilege]. ==== -[[administration-constraints-syntax-create-unique]] +[[constraints-syntax-create-node-unique]] [discrete] -=== Create a unique node property constraint +=== Create a node property uniqueness constraint -This command creates a uniqueness constraint on nodes with the specified label and properties. +This command creates a property uniqueness constraint on nodes with the specified label and properties. [source, syntax, role="noheader", indent=0] ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) -REQUIRE n.propertyName IS UNIQUE +REQUIRE n.propertyName IS [NODE] UNIQUE [OPTIONS "{" option: value[, ...] "}"] ---- @@ -48,14 +49,39 @@ REQUIRE n.propertyName IS UNIQUE ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) -REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS UNIQUE +REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS [NODE] UNIQUE [OPTIONS "{" option: value[, ...] "}"] ---- Index provider can be specified using the `OPTIONS` clause. -[[administration-constraints-syntax-create-node-exists]] +[[constraints-syntax-create-rel-unique]] +[discrete] +=== Create a relationship property uniqueness constraint + +This command creates a property uniqueness constraint on relationships with the specified relationship type and properties. + +[source, syntax, role="noheader", indent=0] +---- +CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] +FOR ()-"["r:RELATIONSHIP_TYPE"]"-() +REQUIRE r.propertyName IS [REL[ATIONSHIP]] UNIQUE +[OPTIONS "{" option: value[, ...] "}"] +---- + +[source, syntax, role="noheader", indent=0] +---- +CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] +FOR ()-"["r:RELATIONSHIP_TYPE"]"-() +REQUIRE (r.propertyName_1, ..., r.propertyName_n) IS [REL[ATIONSHIP]] UNIQUE +[OPTIONS "{" option: value[, ...] "}"] +---- + +Index provider can be specified using the `OPTIONS` clause. + + +[[constraints-syntax-create-node-exists]] [discrete] === Create a node property existence constraint label:enterprise-edition[] @@ -75,7 +101,7 @@ There are no supported `OPTIONS` values for existence constraints, but an empty ==== -[[administration-constraints-syntax-create-rel-exists]] +[[constraints-syntax-create-rel-exists]] [discrete] === Create a relationship property existence constraint label:enterprise-edition[] @@ -95,7 +121,7 @@ There are no supported `OPTIONS` values for existence constraints, but an empty ==== -[[administration-constraints-syntax-create-node-key]] +[[constraints-syntax-create-node-key]] [discrete] === Create a node key constraint label:enterprise-edition[] @@ -105,7 +131,7 @@ This command creates a node key constraint on nodes with the specified label and ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) -REQUIRE n.propertyName IS NODE KEY +REQUIRE n.propertyName IS [NODE] KEY [OPTIONS "{" option: value[, ...] "}"] ---- @@ -113,14 +139,39 @@ REQUIRE n.propertyName IS NODE KEY ---- CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] FOR (n:LabelName) -REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS NODE KEY +REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS [NODE] KEY [OPTIONS "{" option: value[, ...] "}"] ---- Index provider can be specified using the `OPTIONS` clause. -[[administration-constraints-syntax-drop]] +[[constraints-syntax-create-rel-key]] +[discrete] +=== Create a relationship key constraint label:enterprise-edition[] + +This command creates a relationship key constraint on relationships with the specified relationship type and properties. + +[source, syntax, role="noheader", indent=0] +---- +CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] +FOR ()-"["r:RELATIONSHIP_TYPE"]"-() +REQUIRE r.propertyName IS [REL[ATIONSHIP]] KEY +[OPTIONS "{" option: value[, ...] "}"] +---- + +[source, syntax, role="noheader", indent=0] +---- +CREATE CONSTRAINT [constraint_name] [IF NOT EXISTS] +FOR ()-"["r:RELATIONSHIP_TYPE"]"-() +REQUIRE (r.propertyName_1, ..., r.propertyName_n) IS [REL[ATIONSHIP]] KEY +[OPTIONS "{" option: value[, ...] "}"] +---- + +Index provider can be specified using the `OPTIONS` clause. + + +[[constraints-syntax-drop]] == Syntax for dropping constraints Dropping a constraint is done by specifying the name of the constraint. @@ -139,22 +190,32 @@ Dropping a constraint requires the xref::access-control/database-administration. ==== -[[administration-constraints-syntax-list]] +[[constraints-syntax-list]] == Syntax for listing constraints List constraints in the database, either all or filtered on constraint type. -This requires thexref::access-control/database-administration.adoc#access-control-database-administration-constraints[`SHOW CONSTRAINT`] privilege. + +[NOTE] +==== +Listing constraints requires the xref::access-control/database-administration.adoc#access-control-database-administration-constraints[`SHOW CONSTRAINTS` privilege]. +==== The simple version of the command allows for a `WHERE` clause and will give back the default set of output columns: [source, syntax, role="noheader", indent=0] ---- -SHOW [ALL - |UNIQUE +SHOW [ + ALL + |NODE UNIQUE[NESS] + |REL[ATIONSHIP] UNIQUE[NESS] + |UNIQUE[NESS] |NODE [PROPERTY] EXIST[ENCE] |REL[ATIONSHIP] [PROPERTY] EXIST[ENCE] |[PROPERTY] EXIST[ENCE] - |NODE KEY] CONSTRAINT[S] + |NODE KEY + |REL[ATIONSHIP] KEY + |KEY +] CONSTRAINT[S] [WHERE expression] ---- @@ -162,18 +223,66 @@ To get the full set of output columns, a yield clause is needed: [source, syntax, role="noheader", indent=0] ---- -SHOW [ALL - |UNIQUE +SHOW [ + ALL + |NODE UNIQUE[NESS] + |REL[ATIONSHIP] UNIQUE[NESS] + |UNIQUE[NESS] |NODE [PROPERTY] EXIST[ENCE] |REL[ATIONSHIP] [PROPERTY] EXIST[ENCE] |[PROPERTY] EXIST[ENCE] - |NODE KEY] CONSTRAINT[S] + |NODE KEY + |REL[ATIONSHIP] KEY + |KEY +] CONSTRAINT[S] YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- +The type filtering keywords filters the returned constraints on constraint type: + +[[constraints-syntax-list-type-filter]] +.Type filters +[options="header", width="100%", cols="4m,6a"] +|=== +| Filter | Description + +|ALL +| Returns all constraints, no filtering on constraint type. +This is the default if none is given. + +|NODE UNIQUE[NESS] +| Returns the node property uniqueness constraints. + +|REL[ATIONSHIP] UNIQUE[NESS] +| Returns the relationship property uniqueness constraints. + +|UNIQUE[NESS] +| Returns all property uniqueness constraints, for both nodes and relationships. + +|NODE [PROPERTY] EXIST[ENCE] +| Returns the node property existence constraints. + +|REL[ATIONSHIP] [PROPERTY] EXIST[ENCE] +| Returns the relationship property existence constraints. + +|[PROPERTY] EXIST[ENCE] +| Returns all property existence constraints, for both nodes and relationships. + +|NODE KEY +| Returns the node key constraints. + +|REL[ATIONSHIP] KEY +| Returns the relationship key constraints. + +|KEY +| Returns all node and relationship key constraints. + +|=== + + The returned columns from the show command is: .Listing constraints output @@ -188,7 +297,7 @@ The returned columns from the show command is: | Name of the constraint (explicitly set by the user or automatically assigned). label:default-output[] | type -| The ConstraintType of this constraint (`UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `NODE_KEY`, or `RELATIONSHIP_PROPERTY_EXISTENCE`). label:default-output[] +| The ConstraintType of this constraint (`UNIQUENESS` (node uniqueness), `RELATIONSHIP_UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, `NODE_KEY`, or `RELATIONSHIP_KEY`). label:default-output[] | entityType | Type of entities this constraint represents (nodes or relationship). label:default-output[] @@ -210,8 +319,3 @@ The returned columns from the show command is: |=== -[NOTE] -==== -Listing constraints requires the xref::access-control/database-administration.adoc#access-control-database-administration-constraints[`SHOW CONSTRAINTS` privilege]. -==== - diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 649634b25..0e48d7ebd 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -12,6 +12,29 @@ Replacement syntax for deprecated and removed features are also indicated. [[cypher-deprecations-additions-removals-5.3]] == Version 5.3 +=== Deprecated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +//not sure what category this should be, it is more information about a coming breaking change than actual deprecation +label:returnValues[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +SHOW NODE UNIQUENESS CONSTRAINTS YIELD type +---- +a| + +The current constraint type for node property uniqueness constraints, `UNIQUENESS`, will be updated to `NODE_UNIQUENESS` in Neo4j version 6.0. + +This will also be reflected in updates to some error messages and query statistics. + +|=== + === Updated features [cols="2", options="header"] @@ -62,6 +85,52 @@ a| A `COUNT` subquery now supports any non-writing query. For example, it now supports `UNION` and `CALL` clauses. +|=== + +=== New features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:new[] +[source, cypher, role="noheader"] +---- +CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS UNIQUE + +CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS RELATIONSHIP KEY +---- +a| + +Added relationship xref:constraints/syntax.adoc#constraints-syntax-create-rel-key[key] and xref:constraints/syntax.adoc#constraints-syntax-create-rel-unique[uniqueness] constraints. + +a| +label:functionality[] +label:new[] +[source, cypher, role="noheader"] +---- +SHOW NODE UNIQUE[NESS] CONSTRAINTS + +SHOW REL[ATIONSHIP] UNIQUE[NESS] CONSTRAINTS + +SHOW UNIQUE[NESS] CONSTRAINTS + +SHOW REL[ATIONSHIP] KEY CONSTRAINTS + +SHOW KEY CONSTRAINTS +---- +a| + +Added filtering for the new constraint types to `SHOW CONSTRAINTS`. +Includes filtering for the node part, relationship part, or both parts of each type (`NODE KEY` filtering already exists previously). + +The existing `UNIQUE` filter will now return both node and relationship property uniqueness constraints. +All property uniqueness constraint type filters now allow both `UNIQUE` and `UNIQUENESS` keywords. + + |=== [[cypher-deprecations-additions-removals-5.2]] @@ -404,7 +473,7 @@ CREATE CONSTRAINT OPTIONS "{" btree-option: btree-value[, ...] "}" ---- a| -Node key and uniqueness constraints backed by B-tree indexes are removed. +Node key and property uniqueness constraints backed by B-tree indexes are removed. Replaced by: [source, cypher, role="noheader"] @@ -1389,7 +1458,7 @@ CREATE CONSTRAINT OPTIONS "{" btree-option: btree-value[, ...] "}" ---- a| -Node key and uniqueness constraints with B-tree options are deprecated. +Node key and property uniqueness constraints with B-tree options are deprecated. Replaced by: [source, cypher, role="noheader"] @@ -1501,7 +1570,7 @@ REQUIRE (n.propertyName_1, …, n.propertyName_n) IS UNIQUE [OPTIONS "{" option: value[, ...] "}"] ---- a| -Unique property constraints now allow multiple properties, ensuring that the combination of property values are unique. +Property uniqueness constraints now allow multiple properties, ensuring that the combination of property values are unique. a| label:functionality[] @@ -1514,7 +1583,7 @@ ON (n:LabelName) ASSERT (n.propertyName_1, …, n.propertyName_n) IS UNIQUE ---- a| -Unique property constraints now allow multiple properties. +Property uniqueness constraints now allow multiple properties. Replaced by: [source, cypher, role="noheader"] @@ -1581,7 +1650,7 @@ CREATE CONSTRAINT OPTIONS "{" indexProvider: 'range-1.0' "}" ---- a| -Allows creating node key and uniqueness constraints backed by range indexes by providing the range index provider in the `OPTIONS` map. +Allows creating node key and property uniqueness constraints backed by range indexes by providing the range index provider in the `OPTIONS` map. a| @@ -2526,7 +2595,7 @@ label:new[] CREATE CONSTRAINT ... IS UNIQUE [OPTIONS {...}] ---- a| -Allows setting index provider and index configuration for the backing index when creating a uniqueness constraint. +Allows setting index provider and index configuration for the backing index when creating a property uniqueness constraint. a| label:syntax[] @@ -3192,7 +3261,7 @@ label:new[] DROP CONSTRAINT name ---- a| -xref:constraints/syntax.adoc#administration-constraints-syntax-drop[New command] for dropping a constraint by name, no matter the type. +xref:constraints/syntax.adoc#constraints-syntax-drop[New command] for dropping a constraint by name, no matter the type. a| @@ -3304,7 +3373,7 @@ An example of this is `CALL db.index.explicit.searchNodes('my_index','email:me*' | `MATCH (n)-[x:A\|:B\|:C*]-() RETURN n` | Syntax | Deprecated | Replaced by `MATCH (n)-[x:A\|B\|C*]-() RETURN n` | link:/docs/java-reference/5/extending-neo4j/aggregation-functions#extending-neo4j-aggregation-functions[User-defined aggregation functions] | Functionality | Added | | xref:indexes-for-search-performance.adoc[Composite indexes] | Index | Added | -| xref:constraints/examples.adoc#administration-constraints-node-key[Node Key] | Index | Added | Neo4j Enterprise Edition only +| xref:constraints/examples.adoc#constraints-examples-node-key[Node Key] | Index | Added | Neo4j Enterprise Edition only | `CYPHER runtime=compiled` (Compiled runtime) | Functionality | Added | Neo4j Enterprise Edition only | xref:functions/list.adoc#functions-reverse-list[reverse()] | Function | Extended | Now also allows a list as input | xref:functions/aggregating.adoc#functions-max[max()], xref:functions/aggregating.adoc#functions-min[min()] | Function | Extended | Now also supports aggregation over a set containing both strings and numbers diff --git a/modules/ROOT/pages/execution-plans/operator-summary.adoc b/modules/ROOT/pages/execution-plans/operator-summary.adoc index 23a525efd..06762ad46 100644 --- a/modules/ROOT/pages/execution-plans/operator-summary.adoc +++ b/modules/ROOT/pages/execution-plans/operator-summary.adoc @@ -53,13 +53,13 @@ Tests for the absence of a pattern predicate. | | xref::execution-plans/operators.adoc#query-plan-assert-same-node[AssertSameNode] -| Used to ensure that no unique constraints are violated. +| Used to ensure that no property uniqueness constraints are violated. | | | | xref::execution-plans/operators.adoc#query-plan-asserting-multi-node-index-seek[AssertingMultiNodeIndexSeek] -| Used to ensure that no unique constraints are violated. +| Used to ensure that no property uniqueness constraints are violated. | | | @@ -88,26 +88,8 @@ Tests for the absence of a pattern predicate. | label:yes[] | -| xref::execution-plans/operators.adoc#query-plan-create-node-key-constraint[CreateNodeKeyConstraint] -| Creates a node key constraint on a set of properties for all nodes with a certain label. -| -| label:yes[] -| - -| xref::execution-plans/operators.adoc#query-plan-create-node-property-existence-constraint[CreateNodePropertyExistenceConstraint] -| Creates an existence constraint on a property for all nodes with a certain label. -| -| label:yes[] -| - -| xref::execution-plans/operators.adoc#query-plan-create-relationship-property-existence-constraint[CreateRelationshipPropertyExistenceConstraint] -| Creates an existence constraint on a property for all relationships of a certain type. -| -| label:yes[] -| - -| xref::execution-plans/operators.adoc#query-plan-create-unique-constraint[CreateUniqueConstraint] -| Creates a unique constraint on a set of properties for all nodes with a certain label. +| xref::execution-plans/operators.adoc#query-plan-create-constraint[CreateConstraint] +| Creates a constraint for either nodes or relationships. | | label:yes[] | diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 9dcfaf2d2..cd9c4f9e1 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -1141,9 +1141,9 @@ Total database accesses: 0, total allocated memory: 184 == Asserting Multi Node Index Seek // AssertingMultiNodeIndexSeek -The `AssertingMultiNodeIndexSeek` operator is used to ensure that no unique constraints are violated. +The `AssertingMultiNodeIndexSeek` operator is used to ensure that no property uniqueness constraints are violated. The example looks for the presence of a team with the supplied name and id, and if one does not exist, it will be created. -Owing to the existence of two unique constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` must be the very same node, or the constraints would be violated. +Owing to the existence of two property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. .AssertingMultiNodeIndexSeek @@ -2635,9 +2635,9 @@ Total database accesses: 487, total allocated memory: 5256 == Assert Same Node // AssertSameNode -The `AssertSameNode` operator is used to ensure that no unique constraints are violated in the slotted and interpreted runtime. +The `AssertSameNode` operator is used to ensure that no property uniqueness constraints are violated in the slotted and interpreted runtime. The example looks for the presence of a team with the supplied name and id, and if one does not exist, it will be created. -Owing to the existence of two unique constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` must be the very same node, or the constraints would be violated. +Owing to the existence of two property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. .AssertSameNode @@ -4879,15 +4879,23 @@ Total database accesses: 106, total allocated memory: 184 ====== -[[query-plan-create-unique-constraint]] -== Create Unique Constraint -// CreateUniqueConstraint +[[query-plan-create-constraint]] +== Create Constraint +// CreateConstraint -The `CreateUniqueConstraint` operator creates a unique constraint on a set of properties for all nodes having a certain label. -The following query will create a unique constraint with the name `uniqueness` on the `name` property of nodes with the `Country` label. +The `CreateConstraint` operator creates a constraint. -.CreateUniqueConstraint +This constraint can have any of the available constraint types: + +* Property uniqueness constraints +* Property existence constraints label:enterprise-edition[] +* Node or relationship key constraints label:enterprise-edition[] + +The following query will create a property uniqueness constraint with the name `uniqueness` on the `name` property of nodes with the `Country` label. + + +.CreateConstraint ====== .Query @@ -4925,7 +4933,7 @@ Total database accesses: ? To not get an error creating the same constraint twice, we use the `DoNothingIfExists` operator for constraints. This will make sure no other constraint with the given name or another constraint of the same type and schema already exists before the specific `CreateConstraint` operator creates the constraint. If it finds a constraint with the given name or with the same type and schema it will stop the execution and no new constraint is created. -The following query will create a unique constraint with the name `uniqueness` on the `name` property of nodes with the `Country` label only if no constraint named `uniqueness` or unique constraint on `+(:Country {name})+` already exists. +The following query will create a property uniqueness constraint with the name `uniqueness` on the `name` property of nodes with the `Country` label only if no constraint named `uniqueness` or property uniqueness constraint on `+(:Country {name})+` already exists. .DoNothingIfExists(CONSTRAINT) @@ -4960,125 +4968,6 @@ Total database accesses: ? ====== - -[[query-plan-create-node-property-existence-constraint]] -== Create Node Property Existence Constraint -// CreateNodePropertyExistenceConstraint - -The `CreateNodePropertyExistenceConstraint` operator creates an existence constraint with the name `existence` on a property for all nodes having a certain label. -This will only appear in Enterprise Edition. - - -.CreateNodePropertyExistenceConstraint -====== - -.Query -[source, cypher, role="noplay"] ----- -CREATE CONSTRAINT existence -FOR (p:Person) REQUIRE p.name IS NOT NULL ----- - -.Query Plan -[source, query plan, subs="attributes+", role="noheader"] ----- -Planner ADMINISTRATION - -Runtime SCHEMA - -Runtime version {neo4j-version-minor} - -+-------------------+------------------------------------------------------------------+ -| Operator | Details | -+-------------------+------------------------------------------------------------------+ -| +CreateConstraint | CONSTRAINT existence FOR (p:Person) REQUIRE (p.name) IS NOT NULL | -+-------------------+------------------------------------------------------------------+ - -Total database accesses: ? ----- - -====== - - -[[query-plan-create-node-key-constraint]] -== Create Node Key Constraint -// CreateNodeKeyConstraint - -The `CreateNodeKeyConstraint` operator creates a node key constraint with the name `node_key` which ensures -that all nodes with a particular label have a set of defined properties whose combined value is unique, and where all properties in the set are present. -This will only appear in Enterprise Edition. - - -.CreateNodeKeyConstraint -====== - -.Query -[source, cypher, role="noplay"] ----- -CREATE CONSTRAINT node_key -FOR (e:Employee) REQUIRE (e.firstname, e.surname) IS NODE KEY ----- - -.Query Plan -[source, query plan, subs="attributes+", role="noheader"] ----- -Planner ADMINISTRATION - -Runtime SCHEMA - -Runtime version {neo4j-version-minor} - -+-------------------+-----------------------------------------------------------------------------------+ -| Operator | Details | -+-------------------+-----------------------------------------------------------------------------------+ -| +CreateConstraint | CONSTRAINT node_key FOR (e:Employee) REQUIRE (e.firstname, e.surname) IS NODE KEY | -+-------------------+-----------------------------------------------------------------------------------+ - -Total database accesses: ? ----- - -====== - - -[[query-plan-create-relationship-property-existence-constraint]] -== Create Relationship Property Existence Constraint -// CreateRelationshipPropertyExistenceConstraint - -The `CreateRelationshipPropertyExistenceConstraint` operator creates an existence constraint with the name `existence` on a property for all relationships of a certain type. -This will only appear in Enterprise Edition. - - -.CreateRelationshipPropertyExistenceConstraint -====== - -.Query -[source, cypher, role="noplay"] ----- -CREATE CONSTRAINT existence -FOR ()-[l:LIKED]-() REQUIRE l.when IS NOT NULL ----- - -.Query Plan -[source, query plan, subs="attributes+", role="noheader"] ----- -Planner ADMINISTRATION - -Runtime SCHEMA - -Runtime version {neo4j-version-minor} - -+-------------------+-----------------------------------------------------------------------+ -| Operator | Details | -+-------------------+-----------------------------------------------------------------------+ -| +CreateConstraint | CONSTRAINT existence FOR ()-[l:LIKED]-() REQUIRE (l.when) IS NOT NULL | -+-------------------+-----------------------------------------------------------------------+ - -Total database accesses: ? ----- - -====== - - [[query-plan-drop-constraint]] == Drop Constraint // DropConstraint diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index bea730a12..59a2511ff 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -441,6 +441,7 @@ Note that the index is not immediately available, but is created in the backgrou ====== //// +Set-up to get expected behavior: CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) //// @@ -486,6 +487,7 @@ Note that the index is not immediately available, but is created in the backgrou ====== //// +Set-up to get expected behavior: CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) CREATE (_0)-[:`KNOWS` {`lastMet`:2021, `lastMetIn`:"Stockholm", `metIn`:"Malmo", `since`:1992}]->(_1) @@ -525,6 +527,7 @@ If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure ====== //// +Set-up to get expected behavior: CREATE RANGE index `node_range_index_name` for (n:`Person`) ON (n.`surname`) CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) @@ -610,6 +613,7 @@ Note that the composite index is not immediately available, but is created in th The following statement will create a named composite range index on all nodes labeled with `Person` and which have both an `age` and `country` property: //// +Set-up to get expected behavior: CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) ---- @@ -659,6 +663,7 @@ Note that the composite index is not immediately available, but is created in th The following statement will create a named composite range index on all relationships labeled with `PURCHASED` and which have both a `date` and `amount` property: //// +Set-up to get expected behavior: CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) CREATE (_1)-[:`KNOWS`]->(_0) @@ -708,6 +713,7 @@ The index is not immediately available, but is created in the background. ====== //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -760,6 +766,7 @@ The index is not immediately available, but is created in the background. ====== //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -810,6 +817,7 @@ Only one valid value exists for the index provider, `token-lookup-1.0`, which is // hence the `node label lookup index` and `relationship type lookup index` variations above. //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -943,6 +951,7 @@ If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure ====== //// +Set-up to get expected behavior: CREATE POINT index for (n:`Person`) ON (n.`sublocation`) //// @@ -1124,6 +1133,7 @@ The index is not immediately available, but is created in the background. ====== //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -1176,6 +1186,7 @@ The index is not immediately available, but is created in the background. ====== //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -1220,6 +1231,7 @@ If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure ====== //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -1260,6 +1272,7 @@ The valid values for the index provider are `text-2.0` and `text-1.0` (deprecate ====== //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -1296,6 +1309,7 @@ Create an index on the property `title` on nodes with the `Book` label, when tha ====== //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -1331,6 +1345,7 @@ Create a named index on the property `numberOfPages` on nodes with the `Book` la ====== //// +Set-up to get expected behavior: CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') @@ -1366,6 +1381,7 @@ Create an index on the property `isbn` on nodes with the `Book` label, when an i ====== //// +Set-up to get expected behavior: CREATE CONSTRAINT FOR (book:Book) REQUIRE (book.isbn) IS UNIQUE //// @@ -1397,6 +1413,7 @@ Create a named index on the property `numberOfPages` on nodes with the `Book` la ====== //// +Set-up to get expected behavior: CREATE CONSTRAINT bookRecommendations FOR (book:Book) REQUIRE (book.recommend) IS NOT NULL //// @@ -1499,6 +1516,7 @@ If all columns are required, use `SHOW INDEXES YIELD *`. ====== //// +Set-up to get expected behavior: CREATE RANGE INDEX `index_664b28a2` for (n:`Person`) ON (n.`middlename`); CREATE RANGE INDEX `index_58a1c03e` for (n:`Person`) ON (n.`location`); CREATE RANGE INDEX `index_8a688dca` for (n:`Person`) ON (n.`highScore`); @@ -1561,6 +1579,7 @@ An example is to only show indexes not belonging to constraints. ====== //// +Set-up to get expected behavior: CREATE RANGE INDEX `index_664b28a2` for (n:`Person`) ON (n.`middlename`); CREATE RANGE INDEX `index_8a688dca` for (n:`Person`) ON (n.`highScore`); CREATE RANGE INDEX `index_b87724c3` for (n:`Person`) ON (n.`firstname`); @@ -1628,6 +1647,7 @@ The name of the index can be found using the xref::indexes-for-search-performanc ====== //// +Set-up to get expected behavior: CREATE index `index_example` for (n:`Example`) ON (n.`example`); //// diff --git a/modules/ROOT/pages/introduction/index.adoc b/modules/ROOT/pages/introduction/index.adoc index 928be4699..2ae29a020 100644 --- a/modules/ROOT/pages/introduction/index.adoc +++ b/modules/ROOT/pages/introduction/index.adoc @@ -53,7 +53,8 @@ And these are examples of clauses that are used to update the graph: * `SET` (and `REMOVE`): Set values to properties and add labels on nodes using `SET` and use `REMOVE` to remove them. -* `MERGE`: Match existing or create new nodes and patterns. This is especially useful together with unique constraints. +* `MERGE`: Match existing or create new nodes and patterns. +This is especially useful together with property uniqueness constraints. .Cypher Query diff --git a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc index 9cbf5fd18..fa3dce9f6 100644 --- a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc +++ b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc @@ -75,9 +75,15 @@ All users have full access rights. | Constraints a| -xref::constraints/examples.adoc#administration-constraints-prop-exist-nodes[Existence constraints], xref::constraints/examples.adoc#administration-constraints-unique-nodes[uniqueness constraints], and xref::constraints/examples.adoc#administration-constraints-node-key[`NODE KEY` constraints]. +All constraints: +xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], +xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], +xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], +xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship property uniqueness constraints], +xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints], and +xref::constraints/examples.adoc#constraints-examples-relationship-key[relationship key constraints]. a| -Only xref::constraints/examples.adoc#administration-constraints-unique-nodes[uniqueness constraints]. +Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node] and xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship] property uniqueness constraints. |=== diff --git a/modules/ROOT/pages/keyword-glossary.adoc b/modules/ROOT/pages/keyword-glossary.adoc index e0f8cf603..238a96168 100644 --- a/modules/ROOT/pages/keyword-glossary.adoc +++ b/modules/ROOT/pages/keyword-glossary.adoc @@ -42,22 +42,30 @@ Typically used when modifying or importing large amounts of data. | Writing | Create nodes and relationships. -| xref::constraints/syntax.adoc#administration-constraints-syntax-create-node-exists[CREATE CONSTRAINT [existence\] [IF NOT EXISTS\] FOR (n:Label) REQUIRE n.property IS NOT NULL [OPTIONS {}\]] +| xref::constraints/syntax.adoc#constraints-syntax-create-node-exists[CREATE CONSTRAINT [existence\] [IF NOT EXISTS\] FOR (n:Label) REQUIRE n.property IS NOT NULL [OPTIONS {}\]] | Schema | Create a constraint ensuring that all nodes with a particular label have a certain property. -| xref::constraints/syntax.adoc#administration-constraints-syntax-create-node-key[CREATE CONSTRAINT [node_key\] [IF NOT EXISTS\] FOR (n:Label) REQUIRE (n.prop1[, ..., n.propN\]) IS NODE KEY [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::constraints/syntax.adoc#constraints-syntax-create-rel-exists[CREATE CONSTRAINT [existence\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE r.property IS NOT NULL [OPTIONS {}\]] +| Schema +| Create a constraint that ensures all relationships with a particular type have a certain property. + +| xref::constraints/syntax.adoc#constraints-syntax-create-node-key[CREATE CONSTRAINT [node_key\] [IF NOT EXISTS\] FOR (n:Label) REQUIRE (n.prop1[, ..., n.propN\]) IS [NODE\] KEY [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a constraint that ensures all nodes with a particular label have all the specified properties and that the combination of property values is unique; i.e. ensures existence and uniqueness. -| xref::constraints/syntax.adoc#administration-constraints-syntax-create-rel-exists[CREATE CONSTRAINT [existence\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE r.property IS NOT NULL [OPTIONS {}\]] +| xref::constraints/syntax.adoc#constraints-syntax-create-rel-key[CREATE CONSTRAINT [rel_key\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] KEY [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema -| Create a constraint that ensures all relationships with a particular type have a certain property. +| Create a constraint that ensures all relationships with a particular type have all the specified properties and that the combination of property values is unique; i.e. ensures existence and uniqueness. -| xref::constraints/syntax.adoc#administration-constraints-syntax-create-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR (n:Label) REQUIRE (n.prop1[, ..., n.propN\]) IS UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::constraints/syntax.adoc#constraints-syntax-create-node-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR (n:Label) REQUIRE (n.prop1[, ..., n.propN\]) IS [NODE\] UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a constraint that ensures the uniqueness of the combination of node label and property values for a particular property key combination across all nodes. +| xref::constraints/syntax.adoc#constraints-syntax-create-rel-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] +| Schema +| Create a constraint that ensures the uniqueness of the combination of relationship type and property values for a particular property key combination across all relationships. + | xref::indexes-for-full-text-search.adoc[CREATE FULLTEXT INDEX [name\] [IF NOT EXISTS\] FOR (n:Label["\|" ... "\|" LabelN\]) ON EACH "[" n.property[, ..., n.propertyN\] "\]" [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a fulltext index on nodes. @@ -168,7 +176,7 @@ Either the pattern already exists, or it needs to be created. | Writing | Update labels on nodes and properties on nodes and relationships. -| xref::constraints/syntax.adoc#administration-constraints-syntax-list[SHOW [ALL\|UNIQUE\|NODE [PROPERTY\] EXIST[ENCE\]\|REL[ATIONSHIP\] [PROPERTY\] EXIST[ENCE\]\|[PROPERTY\] EXIST[ENCE\]\|NODE KEY\] CONSTRAINT[S\]] +| xref::constraints/syntax.adoc#constraints-syntax-list[SHOW [ALL\|UNIQUE\|NODE [PROPERTY\] EXIST[ENCE\]\|REL[ATIONSHIP\] [PROPERTY\] EXIST[ENCE\]\|[PROPERTY\] EXIST[ENCE\]\|NODE KEY\] CONSTRAINT[S\]] | Schema | List constraints in the database, either all or filtered on type. From 2366eb1f8b4315eaacddf2b3ed79709e97f4d542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:58:00 +0100 Subject: [PATCH 044/383] Backporting missing Alias examples (#279) Some information from https://github.com/neo4j/neo4j-documentation/pull/1609 was not included when switching to the new Cypher repository. --- modules/ROOT/pages/aliases.adoc | 1042 ++++++++++------------ modules/ROOT/pages/keyword-glossary.adoc | 2 +- 2 files changed, 467 insertions(+), 577 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index f798c3d5f..589543bc2 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -8,25 +8,26 @@ This section explains how to use Cypher to manage database aliases in Neo4j. -- -There are two kinds of aliases, local database aliases and remote database aliases. +There are two kinds of database aliases: local and remote. A local database alias can only target a database within the same DBMS. -A remote alias may target a database from another Neo4j DBMS. -When a query is run against an alias, it will be redirected to the target database. +A remote database alias may target a database from another Neo4j DBMS. +When a query is run against a database alias, it will be redirected to the target database. The home database for users can be set to an alias, which will be resolved to the target database on use. +Both local and remote database aliases can be created as part of a xref::databases.adoc#administration-databases-create-composite-database[composite database]. -A local alias can be used in all other Cypher commands in place of the target database. -Please note that the local alias will be resolved while executing the command. -Privileges are defined on the database, and not the local alias. +A local database alias can be used in all other Cypher commands in place of the target database. +Please note that the local database alias will be resolved while executing the command. +Privileges are defined on the database, and not the local database alias. -A remote alias can be used for connecting to a database of a remote Neo4j DBMS, use clauses, setting a user's home database and defining the access privileges to the remote database. -Remote aliases requires configuration to safely connect to the remote target, which is described in link:{neo4j-docs-base-uri}/operations-manual/{page-version}/manage-databases/remote-alias[Connecting remote databases]. -It is not possible to impersonate a user on the remote database or to execute an administration command on the remote database via a remote alias. +A remote database alias can be used for connecting to a database of a remote Neo4j DBMS, use clauses, setting a user's home database and defining the access privileges to the remote database. +Remote database aliases require configuration to safely connect to the remote target, which is described in link:{neo4j-docs-base-uri}/operations-manual/{page-version}/manage-databases/remote-alias[Connecting remote databases]. +It is not possible to impersonate a user on the remote database or to execute an administration command on the remote database via a remote database alias. -Aliases can be created and managed using a set of Cypher administration commands executed against the `system` database. +Database aliases can be created and managed using a set of Cypher administration commands executed against the `system` database. The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. When connected to the DBMS over Bolt, administration commands are automatically routed to the `system` database. -The syntax of the alias management commands is as follows: +The syntax of the database alias management commands is as follows: [NOTE] ==== @@ -71,14 +72,14 @@ CREATE OR REPLACE ALIAS name FOR DATABASE targetName [source] ----- CREATE ALIAS name [IF NOT EXISTS] FOR DATABASE targetName -AT 'url' USER username PASSSWORD 'password' +AT 'url' USER username PASSWORD 'password' [DRIVER "{" setting: value[, ...] "}"] [PROPERTIES "{" key: value[, ...] "}"] ----- [source] ----- CREATE OR REPLACE ALIAS name FOR DATABASE targetName -AT 'url' USER username PASSSWORD 'password' +AT 'url' USER username PASSWORD 'password' [DRIVER "{" setting: value[, ...] "}"] [PROPERTIES "{" key: value[, ...] "}"] ----- @@ -114,7 +115,7 @@ Drop either a local or remote database alias. |=== -This is the list of the allowed driver settings for remote aliases. +This is the list of the allowed driver settings for remote database aliases. [[remote-alias-driver-settings]] .ssl_enforced @@ -122,7 +123,7 @@ This is the list of the allowed driver settings for remote aliases. |=== | Description | -SSL for remote alias drivers is configured through the target url scheme. +SSL for remote database alias drivers is configured through the target url scheme. If `ssl_enforced` is set to true, a secure url scheme is enforced. This will be validated when the command is executed. @@ -231,7 +232,7 @@ One of `DEBUG`, `INFO`, `WARN`, `ERROR`, or `NONE`. [NOTE] ==== -If transaction modifies an alias, other transactions concurrently executing against that alias may be aborted and rolled back for safety. +If transaction modifies a database alias, other transactions concurrently executing against that alias may be aborted and rolled back for safety. This prevents issues such as a transaction executing against multiple target databases for the same alias. ==== @@ -240,6 +241,31 @@ This prevents issues such as a transaction executing against multiple target dat [[alias-management-show-alias]] == Listing database aliases +//// +CREATE DATABASE `movies`; +CREATE ALIAS `films` FOR DATABASE `movies`; +CREATE ALIAS `motion pictures` FOR DATABASE `movies` PROPERTIES { nameContainsSpace: true }; +CREATE DATABASE `northwind-graph-2020`; +CREATE DATABASE `northwind-graph-2021`; +CREATE DATABASE `northwind-graph-2022`; +CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" +DRIVER { + ssl_enforced: true, + connection_timeout: duration({seconds: 5}), + connection_max_lifetime: duration({hours: 1}), + connection_pool_acquisition_timeout: duration({minutes: 1}), + connection_pool_idle_test: duration({minutes: 2}), + connection_pool_max_size: 10, + logging_level: 'info' +}; +CREATE DATABASE `sci-fi-books`; +CREATE COMPOSITE DATABASE `library`; +CREATE ALIAS `library`.`sci-fi` FOR DATABASE `sci-fi-books`; +CREATE ALIAS `library`.`romance` FOR DATABASE `romance-books` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password'; +CREATE COMPOSITE DATABASE garden; +CREATE DATABASE `perennial-flowers` +//// + Available database aliases can be seen using `SHOW ALIASES FOR DATABASE`. The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. @@ -270,36 +296,17 @@ The driver options for connection to the remote database or `null` if the target List of xref::aliases.adoc#remote-alias-driver-settings[driver settings] allowed for remote database aliases. | properties -| Any properties set on the alias. +| Any properties set on the database alias. |=== The detailed information for a particular database alias can be displayed using the command `SHOW ALIASES FOR DATABASE YIELD *`. When a `YIELD *` clause is provided, the full set of columns is returned. - -.+SHOW ALIASES FOR DATABASE+ +.+Show all aliases for a database+ ====== -A summary of all available databases alias can be displayed using the command `SHOW ALIASES FOR DATABASE`. - -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" -DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// +A summary of all available database aliases can be displayed using the command `SHOW ALIASES FOR DATABASE`. .Query [source, cypher, indent=0] @@ -313,34 +320,61 @@ SHOW ALIASES FOR DATABASE | +name+ | +database+ | +location+ | +url+ | +user+ | +"films"+ | +"movies"+ | +"local"+ | ++ | ++ +| +"library.romance"+ | +romance-books"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ +| +"library.sci-fi"+ | +sci-fi-books"+ | +"local"+ | ++ | ++ | +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ -5+d|Rows: 3 +5+d|Rows: 5 |=== ====== +.+Show specific aliases for databases+ +====== + +To list just one database alias, the `SHOW ALIASES` command takes an alias name; + +.Query +[source, cypher, indent=0] +---- +SHOW ALIAS films FOR DATABASES +---- + +.Result +[role="queryresult",options="header,footer",cols="5*+ | ++ + +5+d|Rows: 1 + +|=== + +.Query +[source, cypher, indent=0] +---- +SHOW ALIAS library.romance FOR DATABASES +---- + +.Result +[role="queryresult",options="header,footer",cols="5*+ | ++ | ++ -| +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | ++ -| +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{connection_pool_max_size -> 10, connection_pool_idle_test -> PT2M, connection_pool_acquisition_timeout -> PT1M, connection_max_lifetime -> PT1H, logging_level -> "INFO", ssl_enforced -> true, connection_timeout -> PT5S}+ -6+d|Rows: 3 +| +name+ | +database+ | +location+ | +url+ | +user+ | +driver+ | +properties+ +| +"films"+ | +"movies"+ | +"local"+ | ++ | ++ | ++ | +{}+ +| +"library.romance"+ | +"romance-books"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{}+ | +{}+ +| +"library.sci-fi"+ | +"sci-fi-books"+ | +"local"+ | ++ | ++ | ++ | +{}+ +| +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | ++ | +{"namecontainsspace":true}+ +| +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{"connection_pool_idle_test":PT2M,"connection_pool_max_size":10,"loggi"connection_pool_idle_test":PT2M,"connection_pool_max_size":10,"logging_level":"INFO","ssl_enforced":true,"connection_pool_acquisition_timeout":PT1M,"connection_timeout":PT5S,"connection_max_lifetime":PT1H} | +{}+ + +7+d|Rows: 5 |=== ====== -.+SHOW ALIASES FOR DATABASE+ +.+Show count of aliases for a database+ ====== The number of database aliases can be seen using a `count()` aggregation with `YIELD` and `RETURN`. -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// - .Query [source, cypher, indent=0] ---- @@ -396,35 +416,18 @@ RETURN count(*) as count [role="queryresult",options="header,footer",cols="1*+ | +"movies"+ +| +"library.romance"+ | +"neo4j+s://location:7687"+ | +"romance-books"+ | +"movie scripts"+ | +"neo4j+s://location:7687"+ | +"scripts"+ -3+d|Rows: 2 +3+d|Rows: 3 |=== ====== - [role=enterprise-edition] [[alias-management-create-database-alias]] == Creating database aliases -Aliases can be created using `CREATE ALIAS`. +Database aliases can be created using `CREATE ALIAS`. The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. @@ -478,11 +481,11 @@ CREATE [OR REPLACE] ALIAS [compositeDatabaseName.]aliasName [IF NOT EXISTS] FOR [source, cypher, role=noplay] ----- CREATE [OR REPLACE] ALIAS [compositeDatabaseName.]aliasName [IF NOT EXISTS] FOR DATABASE targetName -AT 'url' USER username PASSSWORD 'password' +AT 'url' USER username PASSWORD 'password' [DRIVER "{" setting: value[, ...] "}"] [PROPERTIES "{" key: value[, ...] "}"] ----- -| Create a remote alias. +| Create a remote database alias. |=== @@ -499,11 +502,11 @@ The `IF NOT EXISTS` and `OR REPLACE` parts of this command cannot be used togeth [NOTE] ==== -Alias names are subject to the xref::syntax/naming.adoc[standard Cypher restrictions on valid identifiers]. +Database alias names are subject to the xref::syntax/naming.adoc[standard Cypher restrictions on valid identifiers]. The following naming rules apply: -* A name is a valid identifier, additionally allowing dots e.g. `main.alias` for local aliases. +* A name is a valid identifier. * Name length can be up to 65534 characters. * Names cannot end with dots. * Names that begin with an underscore or with the prefix `system` are reserved for internal use. @@ -517,27 +520,9 @@ The following naming rules apply: Local aliases are created with a target database. - -.+CREATE ALIAS+ +.+Creating aliases for local databases+ ====== -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// - .Query [source, cypher, indent=0] ---- @@ -550,111 +535,86 @@ System updates: 1 Rows: 0 ---- -====== +When a local database alias has been created, it will show up in the `aliases` column provided by the command `SHOW DATABASES` and in the `SHOW ALIASES FOR DATABASE` command. -.+SHOW DATABASE+ -====== +.Query +[source, cypher, indent=0] +---- +SHOW DATABASE `northwind` +---- -When a local database alias has been created, it will show up in the aliases column provided by the command `SHOW DATABASES` and in the `SHOW ALIASES FOR DATABASE` command. +.Result +[role="queryresult",options="header,footer",cols="13*+ | ++ +5+d|Rows: 1 |=== ====== - -.+SHOW ALIASES FOR DATABASE+ +.+Setting properties for local database aliases+ ====== -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// +Local database aliases can also be given properties. .Query [source, cypher, indent=0] ---- -SHOW ALIASES FOR DATABASE -WHERE name = 'northwind' +CREATE ALIAS `northwind-2022` +FOR DATABASE `northwind-graph-2022` +PROPERTIES { newestNorthwind: true, index: 3 } +---- + +[source, result, role="noheader"] +---- +System updates: 1 +Rows: 0 +---- + +The properties are then shown in the `SHOW ALIASES FOR DATABASE YIELD ...` command. + +.Query +[source, cypher, indent=0] +---- +SHOW ALIAS `northwind-2022` FOR DATABASE YIELD name, properties ---- .Result -[role="queryresult",options="header,footer",cols="5*+ | ++ -5+d|Rows: 1 +| +name+ | +properties+ +| +"northwind-2022"+ | +{"index":3,"newestnorthwind":true}+ +2+d|Rows: 1 |=== ====== - -.+CREATE ALIAS+ +.+Creating database aliases with the same name as an existing alias+ ====== -Adding a local alias with the same name as an existing local or remote alias will do nothing with the `IF NOT EXISTS` clause but fail without it. - -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// +Adding a local database alias with the same name as an existing local or remote alias will do nothing with the `IF NOT EXISTS` clause but fail without it. .Query [source, cypher, indent=0] @@ -664,35 +624,18 @@ CREATE ALIAS `northwind` IF NOT EXISTS FOR DATABASE `northwind-graph-2020` [source, result, role="noheader"] ---- -Rows: 0 +(no changes, no records) ---- ====== -.+CREATE OR REPLACE ALIAS+ +.+Creating or replacing database aliases+ ====== -It is possible to replace an alias. +It is also possible to replace a database alias. The old alias may be either local or remote. -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// - .Query [source, cypher, indent=0] ---- @@ -705,12 +648,17 @@ System updates: 2 Rows: 0 ---- -This is equivalent to running: +This is equivalent to running the following two queries consecutively: .Query [source, cypher, indent=0] ---- DROP ALIAS `northwind` IF EXISTS FOR DATABASE +---- + +.Query +[source, cypher, indent=0] +---- CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020` ---- @@ -724,30 +672,13 @@ CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020` Database aliases can also point to remote databases by providing an url and the credentials of a user on the remote Neo4j DBMS. See link:{neo4j-docs-base-uri}/operations-manual/{page-version}/manage-databases/remote-alias[Connecting remote databases] for the necessary configurations. -Creating remote aliases also allows `IF NOT EXISTS` and `OR REPLACE` clauses. +Creating remote database aliases also allows `IF NOT EXISTS` and `OR REPLACE` clauses. Both check for any remote or local database aliases. -.+CREATE ALIAS+ +.+Creating remote database aliases+ ====== -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// - .Query [source, cypher, indent=0] ---- @@ -763,32 +694,32 @@ System updates: 1 Rows: 0 ---- -====== +When a database alias pointing to a remote database has been created, its details can be shown with the `SHOW ALIASES FOR DATABASE` command. + +.Query +[source, cypher, indent=0] +---- +SHOW ALIAS `remote-northwind` +FOR DATABASE +---- + +.Result +[role="queryresult",options="header,footer",cols="5* 10, connection_timeout -> PT1M}+ | +{}+ +7+d|Rows: 1 + +|=== -.+SHOW ALIASES FOR DATABASE+ ====== -When a database alias pointing to a remote database has been created, its details can be shown with the `SHOW ALIASES FOR DATABASE` command. +.+Setting properties for remote database aliases+ +====== +Just as the local database aliases, the remote database aliases can be given properties. -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// +.Query +[source, cypher, indent=0] +---- +CREATE ALIAS `remote-northwind-2021` FOR DATABASE `northwind-graph-2021` AT 'neo4j+s://location:7687' +USER alice PASSWORD 'password' +PROPERTIES { newestNorthwind: false, index: 6 } +---- + +[source, result, role="noheader"] +---- +System updates: 1 +Rows: 0 +---- + +The properties are then shown in the `SHOW ALIASES FOR DATABASE YIELD ...` command. .Query [source, cypher, indent=0] ---- -SHOW ALIASES FOR DATABASE -WHERE name = 'remote-northwind' +SHOW ALIAS `remote-northwind-2021` FOR DATABASE YIELD name, properties ---- .Result -[role="queryresult",options="header,footer",cols="5* 10, connection_timeout -> PT1M}+ -6+d|Rows: 1 +| +name+ | +type+ | +constituents+ +| +"garden"+ | +"composite"+ | ["garden.flowers","garden.trees"] +3+d|Rows: 1 |=== +.Query +[source, cypher, indent=0] +---- +SHOW ALIASES FOR DATABASE WHERE name STARTS WITH 'garden' +---- + +.Result +[role="queryresult",options="header,footer",cols="5*+ | ++ +| +"garden.trees"+ | +"trees"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ +5+d|Rows: 1 + +|=== ====== -[role=enterprise-edition] +.+Aliases pointing to composite databases+ +====== +Database aliases cannot point to a composite database. + +.Query +[source, cypher, indent=0] +---- +CREATE ALIAS yard FOR DATABASE garden +---- + +.Error message +[source, output, role="noheader", indent=0] +---- +Failed to create the specified database alias 'yard': Database 'garden' is composite. +---- + +====== + [[alias-management-alter-database-alias]] == Altering database aliases -Aliases can be altered using `ALTER ALIAS` to change its database target, url, user credentials, or driver settings. +//// +CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020`; +CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER alice PASSWORD 'password'; +CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER alice PASSWORD 'password' +DRIVER { + connection_timeout: duration({ minutes: 1 }), + connection_pool_max_size: 10 + }; +CREATE ALIAS garden.flowers +FOR DATABASE `perennial-flowers`; +CREATE ALIAS garden.trees +FOR DATABASE trees AT 'neo4j+s://location:7687' +USER alice PASSWORD 'password' +//// + +Database aliases can be altered using `ALTER ALIAS` to change its database target, properties, url, user credentials, or driver settings. The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. Only the clauses used will be altered. [NOTE] ==== -Local aliases can not be altered to remote aliases or vice versa. +Local database aliases cannot be altered to remote aliases, or vice versa. ==== .Alter alias command syntax @@ -927,7 +938,7 @@ The clauses can be applied in any order, while at least one clause needs to be s ALTER ALIAS [compositeDatabaseName.]aliasName [IF EXISTS] SET DATABASE [TARGET targetName AT 'url'] [USER username] -[PASSSWORD 'password'] +[PASSWORD 'password'] [DRIVER "{" setting: value[, ...] "}"] [PROPERTIES "{" key: value[, ...] "}"] ----- @@ -937,29 +948,11 @@ The clauses can be applied in any order, while at least one clause needs to be s |=== - - -.+ALTER ALIAS+ +.+Altering local database aliases+ ====== Example of altering a local database alias target. -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// .Query [source, cypher, indent=0] @@ -974,31 +967,33 @@ System updates: 1 Rows: 0 ---- +When a local database alias has been altered, it will show up in the `aliases` column for the target database provided by the command `SHOW DATABASES`. + +.Query +[source, cypher, indent=0] +---- +SHOW DATABASE `northwind-graph-2021` +---- + +.Result +[role="queryresult",options="header,footer",cols="13*+ | ++ | ++ -| +"remote-northwind"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://other-location:7687"+ | +"alice"+ | +{}+ -| +"remote-with-driver-settings"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"bob"+ | +{logging_level -> "DEBUG", connection_timeout -> PT1M}+ -6+d|Rows: 4 +| +name+ | +database+ | +location+ | +url+ | +user+ | +driver+ | +properties+ +| +"garden.flowers"+ | +"perennial-flowers"+ | +"local"+ | ++ | ++ | ++ | +{"perennial":true}+ +| +"garden.trees"+ | +"updatedtrees"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{}+ | +{"treeversion":2}+ +| +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | ++ | +{"namecontainsspace":true,"moreinfo":"no, not really"}+ +| +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{}+ | +{"namecontainsspace":true}+ +| +"northwind"+ | +"northwind-graph-2021"+ | +"local"+ | ++ | ++ | ++ |+[]+ +| +"remote-northwind"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://other-location:7687"+ | +"alice"+ | +{}+ | +{}+ +| +"remote-with-driver-settings"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"bob"+ | +{logging_level -> "DEBUG", connection_timeout -> PT1M}+ |+[]+ +7+d|Rows: 7 |=== -====== - -.+ALTER ALIAS+ +.+Using IF EXISTS when altering database aliases+ ====== -This command is optionally idempotent, with the default behavior to fail with an error if the alias does not exist. +The `ALTER ALIAS` command is optionally idempotent, with the default behavior to fail with an error if the database alias does not exist. Appending `IF EXISTS` to the command ensures that no error is returned and nothing happens should the alias not exist. -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// + .Query [source, cypher, indent=0] @@ -1219,7 +1165,7 @@ ALTER ALIAS `no-alias` IF EXISTS SET DATABASE TARGET `northwind-graph-2021` [source, result, role="noheader"] ---- -Rows: 0 +(no changes, no records) ---- ====== @@ -1229,31 +1175,38 @@ Rows: 0 [[alias-management-drop-database-alias]] == Deleting database aliases -Both local and remote aliases can be deleted using the `DROP ALIAS` command. +//// +CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2021`; +CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://other-location:7687" USER alice PASSWORD 'password'; +CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER bob PASSWORD 'newPassword' +DRIVER { + connection_timeout: duration({ minutes: 1 }), + logging_level: "debug" + }; +CREATE ALIAS `northwind-2022` FOR DATABASE `northwind-graph-2022` PROPERTIES { newestNorthwind: true, index: 3 }; +CREATE ALIAS `remote-northwind-2021` +FOR DATABASE `northwind-graph-2021` AT 'neo4j+s://location:7687' +USER alice PASSWORD 'password' +PROPERTIES { newestNorthwind: false, index: 6 }; +CREATE ALIAS garden.flowers +FOR DATABASE `perennial-flowers` +PROPERTIES { perennial: true }; +CREATE ALIAS garden.trees +FOR DATABASE updatedTrees AT 'neo4j+s://location:7687' +USER alice PASSWORD 'password' +PROPERTIES { treeVersion: 2 } +//// + + +Both local and remote database aliases can be deleted using the `DROP ALIAS` command. The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. -.+DROP ALIAS+ +.+Deleting local database aliases+ ====== -Drop a local database alias. +Delete a local database alias. -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// .Query [source, cypher, indent=0] @@ -1267,31 +1220,32 @@ System updates: 1 Rows: 0 ---- -====== +When a database alias has been deleted, it will no longer show up in the `aliases` column provided by the command `SHOW DATABASES`. + +.Query +[source, cypher, indent=0] +---- +SHOW DATABASE `northwind-graph-2021` +---- + +.Result +[role="queryresult",options="header,footer",cols="13*+ | ++ +| +"garden.trees"+ | +"updatedtrees"+ | +"local"+ | ++ | ++ +| +"library.romance"+ | +"romance-books"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ +| +"library.sci-fi"+ | +"sci-fi-books"+ | +"local"+ | ++ | ++ | +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ +| +"northwind-2022"+ | +"northwind-graph-2022"+ | +"local"+ | ++ | ++ +| +"remote-northwind-2021"+ | +"northwind-graph-2021"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +"remote-with-driver-settings"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"bob"+ -5+d|Rows: 4 +5+d|Rows: 9 |=== +.+Using IF EXISTS when deleting database aliases+ ====== - -.+DROP ALIAS+ -====== - -This command is optionally idempotent, with the default behavior to fail with an error if the alias does not exist. +The `DROP ALIAS` command is optionally idempotent, with the default behavior to fail with an error if the database alias does not exist. Inserting `IF EXISTS` after the alias name ensures that no error is returned and nothing happens should the alias not exist. -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -CREATE DATABASE `northwind-graph-2020` -CREATE DATABASE `northwind-graph-2021` -CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { - ssl_enforced: true, - connection_timeout: duration({seconds: 5}), - connection_max_lifetime: duration({hours: 1}), - connection_pool_acquisition_timeout: duration({minutes: 1}), - connection_pool_idle_test: duration({minutes: 2}), - connection_pool_max_size: 10, - logging_level: 'info' -} -//// - .Query [source, cypher, indent=0] ---- @@ -1427,7 +1317,7 @@ DROP ALIAS `northwind` IF EXISTS FOR DATABASE [source, result, role="noheader"] ---- -Rows: 0 +(no changes, no records) ---- ====== diff --git a/modules/ROOT/pages/keyword-glossary.adoc b/modules/ROOT/pages/keyword-glossary.adoc index 238a96168..67e42632f 100644 --- a/modules/ROOT/pages/keyword-glossary.adoc +++ b/modules/ROOT/pages/keyword-glossary.adoc @@ -1136,7 +1136,7 @@ Optionally the account status and home database can also be set and if the user | xref::databases.adoc#administration-databases-drop-database[DROP DATABASE ... [IF EXISTS\] [DUMP DATA \| DESTROY DATA\]] | Database -| Deletes a specified database. +| Deletes a specified database (either standard or composite). | xref::access-control/manage-roles.adoc#access-control-drop-roles[DROP ROLE ... [IF EXISTS\]] | User and role From 510ca2aeb9314fc6e191e489a5145c82226c2aa3 Mon Sep 17 00:00:00 2001 From: Arne Fischereit <79841228+arnefischereit@users.noreply.github.com> Date: Mon, 9 Jan 2023 10:21:34 +0100 Subject: [PATCH 045/383] Typo: Replace "quering" with "querying" (#281) --- modules/ROOT/content-nav.adoc | 2 +- modules/ROOT/pages/introduction/index.adoc | 2 +- modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc | 4 ++-- ...dministering.adoc => querying-updating-administering.adoc} | 0 modules/ROOT/pages/syntax/parameters.adoc | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) rename modules/ROOT/pages/introduction/{quering-updating-administering.adoc => querying-updating-administering.adoc} (100%) diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 2640e0a88..e3a260fbd 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -2,7 +2,7 @@ * xref:introduction/index.adoc[] ** xref:introduction/neo4j-databases-graphs.adoc[] -** xref:introduction/quering-updating-administering.adoc[] +** xref:introduction/querying-updating-administering.adoc[] ** xref:introduction/transactions.adoc[] ** xref:introduction/uniqueness.adoc[] ** xref:introduction/clause_composition.adoc[] diff --git a/modules/ROOT/pages/introduction/index.adoc b/modules/ROOT/pages/introduction/index.adoc index 2ae29a020..6c60699b4 100644 --- a/modules/ROOT/pages/introduction/index.adoc +++ b/modules/ROOT/pages/introduction/index.adoc @@ -6,7 +6,7 @@ [[cypher-introduction]] == What is Cypher? -Cypher is a declarative graph query language that allows for expressive and efficient xref::introduction/quering-updating-administering.adoc[querying, updating and administering] of the graph. +Cypher is a declarative graph query language that allows for expressive and efficient xref::introduction/querying-updating-administering.adoc[querying, updating and administering] of the graph. It is designed to be suitable for both developers and operations professionals. Cypher is designed to be simple, yet powerful; highly complicated database queries can be easily expressed, enabling you to focus on your domain, instead of getting lost in database access. diff --git a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc index fa3dce9f6..da8563c88 100644 --- a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc +++ b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc @@ -18,7 +18,7 @@ A client session provides access to any graph in the DBMS. Graph:: This is a data model within a database. -Normally there is only one graph within each database, and many xref::introduction/quering-updating-administering.adoc[administrative] commands that refer to a specific graph do so using the database name. +Normally there is only one graph within each database, and many xref::introduction/querying-updating-administering.adoc[administrative] commands that refer to a specific graph do so using the database name. + Cypher queries executed in a session may declare which graph they apply to, or use a default, given by the session. + @@ -30,7 +30,7 @@ For more information, see link:{neo4j-docs-base-uri}/operations-manual/{page-ver Database:: A database is a storage and retrieval mechanism for collecting data in a defined space on disk and in memory. -Most of the time Cypher queries are xref::introduction/quering-updating-administering.adoc[reading or updating queries], which are run against a graph. +Most of the time Cypher queries are xref::introduction/querying-updating-administering.adoc[reading or updating queries], which are run against a graph. There are also administrative commands that apply to a database, or to the entire DBMS. Administrative commands cannot be run in a session connected to a normal user database, but instead need to be run within a session connected to the _system_ database. Administrative commands execute on the _system_ database. diff --git a/modules/ROOT/pages/introduction/quering-updating-administering.adoc b/modules/ROOT/pages/introduction/querying-updating-administering.adoc similarity index 100% rename from modules/ROOT/pages/introduction/quering-updating-administering.adoc rename to modules/ROOT/pages/introduction/querying-updating-administering.adoc diff --git a/modules/ROOT/pages/syntax/parameters.adoc b/modules/ROOT/pages/syntax/parameters.adoc index e75dbdbf6..f89ebdaa2 100644 --- a/modules/ROOT/pages/syntax/parameters.adoc +++ b/modules/ROOT/pages/syntax/parameters.adoc @@ -1,11 +1,11 @@ -:description: This section describes parameterized quering. +:description: This section describes parameterized querying. [[cypher-parameters]] = Parameters [abstract] -- -This section describes parameterized quering. +This section describes parameterized querying. -- [[cypher-parameters-introduction]] From fc39da6b7834e4a2edbd3200e8388e2628fc1154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nadja=20M=C3=BCller?= <73830555+nadja-muller@users.noreply.github.com> Date: Mon, 9 Jan 2023 11:13:27 +0100 Subject: [PATCH 046/383] square function returns NaN when given a negative number (#284) The documentation did not match the implementation. The result of NaN has been accepted for now until we do an overarching analysis of all numeric functions. --- modules/ROOT/pages/functions/mathematical-logarithmic.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/functions/mathematical-logarithmic.adoc b/modules/ROOT/pages/functions/mathematical-logarithmic.adoc index 9ddbf8f0a..f40fedcd2 100644 --- a/modules/ROOT/pages/functions/mathematical-logarithmic.adoc +++ b/modules/ROOT/pages/functions/mathematical-logarithmic.adoc @@ -291,7 +291,7 @@ sqrt(expression) |=== | `sqrt(null)` returns `null`. -| `sqrt()` returns `null` +| `sqrt()` returns `NaN` |=== From 4731d3812a771481ac1b074c3fe91067f9b7fbcb Mon Sep 17 00:00:00 2001 From: Ali Ince Date: Mon, 9 Jan 2023 10:23:33 +0000 Subject: [PATCH 047/383] Add missing seeding options (#285) Add missing seeding options for `CREATE DATABASE`, also fixes a couple of broken links. --- modules/ROOT/pages/databases.adoc | 22 ++++++++++++++++--- .../pages/query-tuning/query-options.adoc | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 0b42f4e7d..39c19f307 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -551,7 +551,7 @@ CREATE DATABASE `topology-example` TOPOLOGY 1 PRIMARY 0 SECONDARIES ---- // TODO: Add result -For more details on primary and secondary server roles, see link:{neo4j-docs-base-uri}/operations-manual{page-version}/clustering#clustering-introduction-operational[Cluster overview]. +For more details on primary and secondary server roles, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering#clustering-introduction-operational[Cluster overview]. [NOTE] ==== @@ -687,12 +687,28 @@ Currently this is only supported with `existingDataSeedInstance` and must be set Defines which instance is used for seeding the data of the created database. The instance id can be taken from the id column of the `dbms.cluster.overview()` procedure. Can only be used in clusters. +| `seedURI` +| URI to a backup or a dump from an existing database. +| +Defines an identical seed from an external source which will be used to seed all servers. + +| `seedConfig` +| comma separated list of configuration values. +| +Defines additional configuration specified by comma separated `name=value` pairs that might be required by certain seed providers. + +| `seedCredentials` +| credentials +| +Defines credentials that needs to be passed into certain seed providers. + |=== [NOTE] ==== -The `existingData` and `existingDataSeedInstance` options cannot be combined with the `OR REPLACE` part of this command. +The `existingData`, `existingDataSeedInstance`, `seedURI`, `seedConfig` and `seedCredentials` options cannot be combined with the `OR REPLACE` part of this command. +For details about the use of these seeding options, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/databases/#cluster-seed[Operations Manual -> Seed a cluster]. ==== @@ -707,7 +723,7 @@ Standard databases can be modified using the command `ALTER DATABASE`. By default, a database has read-write access mode on creation. The database can be limited to read-only mode on creation using the configuration parameters `dbms.databases.default_to_read_only`, `dbms.databases.read_only`, and `dbms.database.writable`. -For details, see link:{neo4j-docs-base-uri}/operations-manual{page-version}/manage-databases/configuration#manage_database_parameters[Configuration parameters]. +For details, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/manage-databases/configuration#manage_database_parameters[Configuration parameters]. A database that was created with read-write access mode can be changed to read-only. To change it to read-only, you can use the `ALTER DATABASE` command with the sub-clause `SET ACCESS READ ONLY`. diff --git a/modules/ROOT/pages/query-tuning/query-options.adoc b/modules/ROOT/pages/query-tuning/query-options.adoc index 58ee9bdb1..a9b5aeae4 100644 --- a/modules/ROOT/pages/query-tuning/query-options.adoc +++ b/modules/ROOT/pages/query-tuning/query-options.adoc @@ -256,7 +256,7 @@ Cypher replanning occurs in the following circumstances: * When the query is not in the cache. This can either be when the server is first started or restarted, if the cache has recently been cleared, or if link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings#config_server.db.query_cache_size[server.db.query_cache_size] was exceeded. -* When the time has past the link:{neo4j-docs-base-uri}/operations-manual{page-version}/reference/configuration-settings#config_dbms.cypher.min_replan_interval[dbms.cypher.min_replan_interval] value, and the database statistics have changed more than the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings#config_dbms.cypher.statistics_divergence_threshold[dbms.cypher.statistics_divergence_threshold] value. +* When the time has past the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings#config_dbms.cypher.min_replan_interval[dbms.cypher.min_replan_interval] value, and the database statistics have changed more than the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings#config_dbms.cypher.statistics_divergence_threshold[dbms.cypher.statistics_divergence_threshold] value. There may be situations where xref::execution-plans/index.adoc[Cypher query planning] can occur at a non-ideal time. For example, when a query must be as fast as possible and a valid plan is already in place. From 4e8b01397d29474e7f3da3b36905236736cbd310 Mon Sep 17 00:00:00 2001 From: Lidia Zuin <102308961+lidiazuin@users.noreply.github.com> Date: Mon, 9 Jan 2023 11:44:49 +0100 Subject: [PATCH 048/383] Fixing svg images that were not showing live (#292) (#296) Apparently the conversion from .png to .svg caused some conflict that was preventing the website to render the images. This has been fixed now. Cherry-picked from https://github.com/neo4j/docs-cypher/pull/292 --- .../ROOT/images/privileges_grant_and_deny_syntax.svg | 10 +++++++++- modules/ROOT/images/privileges_hierarchy.svg | 10 +++++++++- modules/ROOT/images/privileges_on_graph_syntax.svg | 10 +++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/modules/ROOT/images/privileges_grant_and_deny_syntax.svg b/modules/ROOT/images/privileges_grant_and_deny_syntax.svg index c68bb05fa..a53dfca4e 100644 --- a/modules/ROOT/images/privileges_grant_and_deny_syntax.svg +++ b/modules/ROOT/images/privileges_grant_and_deny_syntax.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + diff --git a/modules/ROOT/images/privileges_hierarchy.svg b/modules/ROOT/images/privileges_hierarchy.svg index 37c6791fe..e548a5230 100644 --- a/modules/ROOT/images/privileges_hierarchy.svg +++ b/modules/ROOT/images/privileges_hierarchy.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + diff --git a/modules/ROOT/images/privileges_on_graph_syntax.svg b/modules/ROOT/images/privileges_on_graph_syntax.svg index 321deeb2b..689c5c798 100644 --- a/modules/ROOT/images/privileges_on_graph_syntax.svg +++ b/modules/ROOT/images/privileges_on_graph_syntax.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + From acfe8396f7ca9d9cd4e122f954910ab9bf7ed018 Mon Sep 17 00:00:00 2001 From: Nick Giles <100630647+nick-giles-neo@users.noreply.github.com> Date: Mon, 9 Jan 2023 11:11:44 +0000 Subject: [PATCH 049/383] Add a warning about ALTER DATABASE and single primaries (#289) We cannot currently move from one primary to more than one, or from more than one primary to one primary. This limitation is noted elsewhere, but worth adding here too. --- modules/ROOT/pages/databases.adoc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 39c19f307..c89fa5f43 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -834,6 +834,12 @@ SET TOPOLOGY 3 PRIMARY 0 SECONDARIES ====== +[NOTE] +==== +It is not possible to automatically transition to or from a topology with a single primary host. +See the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/databases#_alter_topology[Operations Manual -> Alter topology] for more information. +==== + .+SHOW DATABASE+ ====== @@ -846,7 +852,7 @@ SHOW DATABASES yield name, currentPrimariesCount, currentSecondariesCount, reque ====== -For more details on primary and secondary server roles, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/inroduction#clustering-introduction-operational[Cluster overview]. +For more details on primary and secondary server roles, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/introduction#clustering-introduction-operational[Operations Manual -> Clustering overview]. [NOTE] ==== From 4dd90e8fb449b85497634103a6620411dcb60268 Mon Sep 17 00:00:00 2001 From: Tobias Johansson Date: Mon, 9 Jan 2023 12:48:30 +0100 Subject: [PATCH 050/383] Improve section on graph functions (#263) - Add graph.propertiesByName() - Nicer formatting for examples --- modules/ROOT/pages/functions/graph.adoc | 88 +++++++++++++++++++++++-- modules/ROOT/pages/functions/index.adoc | 1 + 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/modules/ROOT/pages/functions/graph.adoc b/modules/ROOT/pages/functions/graph.adoc index afc6bfb26..b707512ee 100644 --- a/modules/ROOT/pages/functions/graph.adoc +++ b/modules/ROOT/pages/functions/graph.adoc @@ -9,24 +9,100 @@ Returns a list containing the names of all graphs on the current composite database. It is only supported on link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases[composite databases]. -[source, cypher, role=noplay] +.+graph.names()+ +====== +.Setup +[source, cypher, indent=0] +---- +CREATE DATABASE dba; +CREATE DATABASE dbb; +CREATE DATABASE dbc; +CREATE COMPOSITE DATABASE composite; +CREATE ALIAS composite.first FOR DATABASE dba; +CREATE ALIAS composite.second FOR DATABASE dbb; +CREATE ALIAS composite.third FOR DATABASE dbc; +---- + +.Query +[source, cypher, indent=0] ---- -RETURN graph.names() +RETURN graph.names() AS name ---- +The names of all graphs on the current composite database are returned. + +.Result [role="queryresult",options="header,footer",cols="1*> that adds the graph as a constituent of a composite database. +It is only supported on link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases[composite databases]. + +.+graph.propertiesByName()+ +====== +.Setup +[source, cypher, indent=0] ---- -graph.names() -["composite.first", "composite.second", "composite.third"] -Rows: 1 +CREATE DATABASE dba; +CREATE DATABASE dbb; +CREATE DATABASE dbc; +CREATE COMPOSITE DATABASE composite; +CREATE ALIAS composite.first FOR DATABASE dba + PROPERTIES {number: 1, tags: ['A', 'B']}; +CREATE ALIAS composite.second FOR DATABASE dbb + PROPERTIES {number: 0, tags: ['A']}; +CREATE ALIAS composite.third FOR DATABASE dbc + PROPERTIES {number: 2, tags: ['B', 'C']}; ---- +.Query +[source, cypher, indent=0] +---- +UNWIND graph.names() AS name +RETURN name, graph.propertiesByName(name) AS props +---- + +Properties for all graphs on the current composite database are returned. + +.Result +[role="queryresult",options="header,footer",cols="2* Date: Mon, 9 Jan 2023 14:44:03 +0100 Subject: [PATCH 051/383] New data type page (#275) --- modules/ROOT/pages/syntax/values.adoc | 209 ++++++++++++++++---------- 1 file changed, 127 insertions(+), 82 deletions(-) diff --git a/modules/ROOT/pages/syntax/values.adoc b/modules/ROOT/pages/syntax/values.adoc index 2b2ce7127..63eca3a69 100644 --- a/modules/ROOT/pages/syntax/values.adoc +++ b/modules/ROOT/pages/syntax/values.adoc @@ -9,119 +9,164 @@ This section provides an overview of data types in Cypher. -- Cypher provides first class support for a number of data types. +These fall into the following three categories: *property*, *structural*, and *composite*. +This chapter will first provide a brief overview of each type, and then go into more detail about the property data type. -These fall into several categories which will be described in detail in the following subsections: +== Property types -Property types:: -Integer, Float, String, Boolean, Point, Date, Time, LocalTime, DateTime, LocalDateTime, and Duration. +The following data types are included in the property types category: `Integer`, `Float`, `String`, `Boolean`, `Point`, `Date`, `Time`, `LocalTime`, `DateTime`, `LocalDateTime`, and `Duration`. -Structural types:: -Node, Relationship, and Path. +* Property types can be returned from Cypher queries +* Property types can be used as xref::syntax/parameters.adoc[parameters] +* Property types can be stored as properties +* Property types can be constructed with xref::syntax/expressions.adoc[Cypher literals] -Composite types:: -List and Map. +Homogeneous lists of simple types can also be stored as properties, although lists in general (see xref::syntax/values.adoc#composite-types[Composite types]) cannot be stored. +Cypher also provides pass-through support for byte arrays, which can be stored as property values. +Byte arrays are supported for performance reasons, since using Cypher's generic language type, List of Integer (where each Integer has a 64-bit representation), would be too costly. +However, byte arrays are _not_ considered a first class data type by Cypher, so they do not have a literal representation. -[[property-types]] -== Property types -* [*] Can be returned from Cypher queries -* [*] Can be used as xref::syntax/parameters.adoc[parameters] -* [*] Can be stored as properties -* [*] Can be constructed with xref::syntax/expressions.adoc[Cypher literals] +[[structural-types]] +== Structural types -The property types: +The following data types are included in the structural types category: `Node`, `Relationship`, and `Path`. -* **Number**, an abstract type, which has the subtypes **Integer** and **Float** -* **String** -* **Boolean** -* The spatial type **Point** -* Temporal types: **Date**, **Time**, **LocalTime**, **DateTime**, **LocalDateTime** and **Duration** +* Structural types can be returned from Cypher queries +* Structural types cannot be used as xref::syntax/parameters.adoc[parameters] +* Structural types cannot be stored as properties +* Structural types cannot be constructed with xref::syntax/expressions.adoc[Cypher literals] -The adjective _numeric_, when used in the context of describing Cypher functions or expressions, indicates that any type of Number applies (Integer or Float). +The `Node` data type includes: Id, Label(s), and Map (of properties). +Note that labels are not values, but a form of pattern syntax. -Homogeneous lists of simple types can also be stored as properties, although lists in general (see xref::syntax/values.adoc#composite-types[Composite types]) cannot be stored. - -Cypher also provides pass-through support for byte arrays, which can be stored as property values. -Byte arrays are _not_ considered a first class data type by Cypher, so do not have a literal representation. +The `Relationship` data type includes: Id, Type, Map (of properties), Id of start node, and Id of end node. +The `Path` data type is an alternating sequence of nodes and relationships. -[[property-types-sip-note]] -.Sorting of special characters [NOTE] ==== -Strings that contain characters that do not belong to the link:https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane[_Basic Multilingual Plane_] (_BMP_) can have inconsistent or non-deterministic ordering in Neo4j. -BMP is a subset of all characters defined in Unicode. -Expressed simply, it contains all common characters from all common languages. +Nodes, relationships, and paths are returned as a result of pattern matching. +In Neo4j, all relationships have a direction. +However, you can have the notion of undirected relationships at query time. +==== -The most significant characters _not_ in BMP are those belonging to the link:https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane[_Supplementary Multilingual Plane_] or the link:https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Ideographic_Plane[_Supplementary Ideographic Plane_]. +[[composite-types]] +== Composite types -Examples are: +The following data types are included in the composite types category: `List` and `Map`. -* Historic scripts and symbols and notation used within certain fields such as: Egyptian hieroglyphs, modern musical notation, mathematical alphanumerics. -* Emojis and other pictographic sets. -* Game symbols for playing cards, Mah Jongg, and dominoes. -* CJK Ideograph that were not included in earlier character encoding standards. -==== +* Composite types can be returned from Cypher queries +* Composite types can be used as xref::syntax/parameters.adoc[parameters] +* Composite types cannot be stored as properties +* Composite types can be constructed with xref::syntax/expressions.adoc[Cypher literals] +The `List` data type is a heterogeneous, ordered collection of values, each of which can have any property, structural or composite type. -[[structural-types]] -== Structural types +As noted above, homogeneous lists of simple types can be stored as properties. -* [*] Can be returned from Cypher queries -* [ ] Cannot be used as xref::syntax/parameters.adoc[parameters] -* [ ] Cannot be stored as properties -* [ ] Cannot be constructed with xref::syntax/expressions.adoc[Cypher literals] +The `Map` data type is a heterogeneous, unordered collection of (_Key_, _Value_) pairs, where _Key_ is a string and _Value_ can have any property, structural, or composite type. -The structural types: +Composite values can also contain `null`. +For more details, see xref::syntax/working-with-null.adoc[working with null]. -* **Node** -** Id -** Label(s) -+ -[NOTE] -==== -Labels are not values but are a form of pattern syntax. -==== -** Map (of properties) -* **Relationship** -** Id -** Type -** Map (of properties) -** Id of the start node -** Id of the end node -* **Path**, an alternating sequence of nodes and relationships +== Property type details +The below table provides more detailed information about the various property types that Cypher supports. +Note that Cypher types are implemented using Java, and that below table references Java value constants. -[NOTE] -==== -Nodes, relationships, and paths are returned as a result of pattern matching. -In Neo4j, all relationships have a direction. -However, you can have the notion of undirected relationships at query time. -==== +[.types, opts="header", cols="2,2,2,1"] +|=== +| Type | Min. value | Max. value | Precision +| `Boolean` +| `False` +| `True` +| - -[[composite-types]] -== Composite types +| `Date` +| `-999_999_999-01-01` +| `+999_999_999-12-31` +| Days -* [*] Can be returned from Cypher queries -* [*] Can be used as xref::syntax/parameters.adoc[parameters] -* [ ] Cannot be stored as properties -* [*] Can be constructed with xref::syntax/expressions.adoc[Cypher literals] +| `DateTime` +| `-999_999_999-01-01T00:00:00+18:00` +| `+999_999_999-12-31T23:59:59.999999999-18:00` +| Nanoseconds -The composite types: +| `Duration` +| `P-292471208677Y-6M-15DT-15H-36M-32S` +| `P292471208677Y6M15DT15H36M32.999999999S` +| Nanoseconds -* **List**, a heterogeneous, ordered collection of values, each of which has any property, structural or composite type. -* **Map**, a heterogeneous, unordered collection of (_Key_, _Value_) pairs. -** _Key_ is a String -** _Value_ has any property, structural or composite type +| `Float` +| `Double.MIN_VALUE` footnote:[The minimum value represents the minimum positive value of a `Float`, i.e. the closest value to zero. +It is also possible to have a negative `Float`.] +| `Double.MAX_VALUE` +| 64 bit -[NOTE] -==== -Composite values can also contain `null`. -==== +| `Integer` +| `Long.MIN_VALUE` +| `Long.MAX_VALUE` +| 64 bit + +| `LocalDateTime` +| `-999_999_999-01-01T00:00:00` +| `+999_999_999-12-31T23:59:59.999999999` +| Nanoseconds + +| `LocalTime` +| `00:00:00` +| `23:59:59.999999999` +| Nanoseconds + +| `Point` +| *Cartesian*: (`-Double.MAX_VALUE`, `-Double.MAX_VALUE`) + +*Cartesian_3D*: (`-Double.MAX_VALUE`, `-Double.MAX_VALUE`, `-Double.MAX_VALUE`) + +*WGS_84*: (`-180`, `-90`) + +*WGS_84_3D*: (`-180`, `-90`, `-Double.MAX_VALUE`) + +| *Cartesian*: (`Double.MAX_VALUE`, `Double.MAX_VALUE`) + +*Cartesian_3D*: (`Double.MAX_VALUE`, `Double.MAX_VALUE`, `Double.MAX_VALUE`) + +*WGS_84*: (`180`, `90`) + +*WGS_84_3D*: (`180`, `90`, `Double.MAX_VALUE`) + +| The precision of each coordinate of the `Point` is 64 bit as they are floats. + +| `String` +| - +| - +| - + +| `Time` +| `00:00:00+18:00` +| `23:59:59.999999999-18:00`` +| Nanoseconds +|=== + +=== Java value details + +[.values, opts="header", width=75%, cols="1,3"] +|=== +| Name | Value + +| `Double.MAX_VALUE` +| 1.7976931348623157e+308 + +| `Double.MIN_VALUE` +| 4.9e-324 -''' +| `Long.MAX_VALUE` +| 2^63-1 -Special care must be taken when using `null` (see xref::syntax/working-with-null.adoc[]). +| `Long.MIN_VALUE` +| -2^63 +|=== From 08c510a66fcd8595441040d035e2d1f241298f74 Mon Sep 17 00:00:00 2001 From: Gem Lamont <106068376+gem-neo4j@users.noreply.github.com> Date: Mon, 9 Jan 2023 16:17:27 +0100 Subject: [PATCH 052/383] Explain how to use limit and where together with a WITH. (#287) The cheat sheet showed an incorrect way of using WITH and WHERE, the discussion in CLG made it apparent that it is indeed confusing, so added an example to hopefully shed some light on it. --- modules/ROOT/pages/clauses/with.adoc | 57 ++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/modules/ROOT/pages/clauses/with.adoc b/modules/ROOT/pages/clauses/with.adoc index 94517a395..0ad6b03ed 100644 --- a/modules/ROOT/pages/clauses/with.adoc +++ b/modules/ROOT/pages/clauses/with.adoc @@ -182,3 +182,60 @@ Starting at *'Anders'*, find all matching nodes, order by name descending and ge 1+d|Rows: 2 |=== + +[[with-limit-and-filter]] +== Limit and Filtering + +It is possible to limit and filter on the same `WITH` clause. +Note that the `LIMIT` clause is applied before the `WHERE` clause. + +.Query +[source, cypher, indent=0] +---- +UNWIND [1, 2, 3, 4, 5, 6] AS x +WITH x +LIMIT 5 +WHERE x > 2 +RETURN x +---- + +The limit is first applied, reducing the rows to the first 5 items in the list. The filter is then applied, reducing the final result as seen below: + +.Result +[role="queryresult",options="header,footer",cols="1* 2 +WITH x +LIMIT 5 +RETURN x +---- + +This time the filter is applied first, reducing the rows to consist of the list `[3, 4, 5, 6]`. +Then the limit is applied. +As the limit is larger than the total number of remaining rows, all rows are returned. + +.Result +[role="queryresult",options="header,footer",cols="1* Date: Mon, 9 Jan 2023 15:54:23 +0000 Subject: [PATCH 053/383] Update the readme with branch and PR management details (#206) --- README.adoc | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/README.adoc b/README.adoc index 00dbf9575..c7c7470b0 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,6 @@ = Neo4j Cypher Manual -== Build +== Building locally === Prereqs @@ -27,14 +27,14 @@ npm run build To view the built site, launch a local server: -1. `npm start` -2. In a browser tab, go to `localhost:8000` +. `npm start` +. In a browser tab, go to `localhost:8000` === Live preview When you run `npm start`, the project is monitored for updates to asciidoc files. -If a change to an asciidoc file is detected the site is automatically rebuilt. +If a change to an asciidoc file is detected, the site is automatically rebuilt. === Errors @@ -45,3 +45,29 @@ Check for xref errors and other warnings. ./node_modules/@antora/cli/bin/antora preview.yml 2>&1 | tee build.log ---- +== Raising PRs + +=== Branch management + +The docs-cypher repo (and all CoreDB docs repos) will contain the following branches: + +* `4.4` - Long term support +* `5.x` - this is the currently published version, and therefore is the branch that we publish all v5 docs from. +** PRs merged into this branch will be published live immediately. +* `dev` - this is always the next release - this branch will be published on the staging server, as a preview, but we will never publish from this branch publicly. +** Next means the “next version of documentation”, and may not mirror other Neo4j engineering repos. +Within Github we’ll update the branch descriptions with what the current and next versions are. +* Work on older branches (`3.5`, `4.0`, `4.1`, `4.2`, `4.3`) should be seen as “only when absolutely necessary”. + +=== Raising PRs, and the publishing process + +Here is the workflow for creating PRs in the repo, and our publication process: + +* For any work on the **current** version, all work should be raised & merged against the `dev` branch, and cherry-picked back to `5.x` branch. +There are a few edge cases where we might want to work only on the current branch, for example adding a warning that is not needed in the next version - this is ok! +* For content relating to the **next** release, raise PRs against the dev branch and use labels to mark the specific version it is targeting. +* For work on **next+n** docs (i.e a version beyond **next**), we have 2 options: +** Open a draft PR against `dev`, with a label for the specific version it is targeting - this can be shared & reviewed in draft mode, but will not be mergeable until it is pointed at the next release. +** Create a feature branch from `dev`, to be merged into `dev` when appropriate. +* When a new version is ready to published, the `5.x` branch will get a git tag, named with the exact version (for example, **5.1.0**), signifying that this point-in-time marks the completion of the work for that minor release. +* The `dev` branch, with all merged content for the next release, will then be merged From fdbec8a1037bf1d3297c49a3645ceae64bd4bd39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nadja=20M=C3=BCller?= <73830555+nadja-muller@users.noreply.github.com> Date: Thu, 12 Jan 2023 13:02:29 +0100 Subject: [PATCH 054/383] Document escaping in string literals, names and regular expressions (#303) --- modules/ROOT/pages/clauses/where.adoc | 36 +++++++++++++++++++++- modules/ROOT/pages/syntax/expressions.adoc | 9 ++++-- modules/ROOT/pages/syntax/naming.adoc | 21 +++++++++++-- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index 5174d8b58..211e6acf5 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -420,7 +420,7 @@ The name and age for the *'Peter'* node are returned because his name does not e == Regular expressions Cypher supports filtering using regular expressions. -The regular expression syntax is inherited from the link:https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html[Java regular expressions]. +The regular expression syntax is inherited from the link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html[Java regular expressions]. This includes support for flags that change how strings are matched, including case-insensitive `(?i)`, multiline `(?m)` and dotall `(?s)`. Flags are given at the beginning of the regular expression, for example: @@ -482,6 +482,40 @@ The name, age and email for the 'Peter' node are returned because his email ends 3+d|Rows: 1 |=== +Note that the regular expression constructs in +link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html[Java regular expressions] +are applied only after resolving the escaped character sequences in the given +xref::syntax/expressions.adoc#cypher-expressions-string-literals[string literal]. +It is sometimes necessary to add additional backslashes to express regular expression +constructs. This list clarifies the combination of these two definitions, +containing the original escape sequence and the resulting character in the regular expression: + +[options="header", cols=">1,<2,<2"] +|=================== +|String literal sequence|Resulting Regex sequence|Regex match +|`\t`|Tab|Tab +|`\\t`|`\t`|Tab +|`\b`|Backspace|Backspace +|`\\b`|`\b`|Word boundary +|`\n`|Newline|NewLine +|`\\n`|`\n`|Newline +|`\r`|Carriage return|Carriage return +|`\\r`|`\r`|Carriage return +|`\f`|Form feed|Form feed +|`\\f`|`\f`|Form feed +|`\'`|Single quote|Single quote +|`\"`|Double quote|Double quote +|`\\`|Backslash|Backslash +|`\\\`|`\\`|Backslash +|`\uxxxx`|Unicode UTF-16 code point (4 hex digits must follow the `\u`)|Unicode UTF-16 code point (4 hex digits must follow the `\u`) +|`\\uxxxx`|`\uxxxx`|Unicode UTF-16 code point (4 hex digits must follow the `\u`) +|=================== + +[NOTE] +==== +Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. +Consider using xref:syntax/parameters.adoc[parameters] instead. +==== [[case-insensitive-regular-expressions]] === Case-insensitive regular expressions diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index 8c7120ccc..b11b906d6 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -52,7 +52,7 @@ An expression in Cypher can be: String literals can contain the following escape sequences: -[options="header", cols=">1,<2", width="50%"] +[options="header", cols=">1,<2"] |=================== |Escape sequence|Character |`\t`|Tab @@ -64,9 +64,13 @@ String literals can contain the following escape sequences: |`\"`|Double quote |`\\`|Backslash |`\uxxxx`|Unicode UTF-16 code point (4 hex digits must follow the `\u`) -|`\Uxxxxxxxx`|Unicode UTF-32 code point (8 hex digits must follow the `\U`) |=================== +[NOTE] +==== +Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. +Consider using xref:syntax/parameters.adoc[parameters] instead. +==== [[cypher-expressions-number-literals]] == Note on number literals @@ -74,7 +78,6 @@ String literals can contain the following escape sequences: Any number literal may contain an underscore `_` between digits. There may be an underscore between the `0x` or `0o` and the digits for hexadecimal and octal literals. - [[query-syntax-case]] == `CASE` expressions diff --git a/modules/ROOT/pages/syntax/naming.adoc b/modules/ROOT/pages/syntax/naming.adoc index 565a7e55c..2c02a7422 100644 --- a/modules/ROOT/pages/syntax/naming.adoc +++ b/modules/ROOT/pages/syntax/naming.adoc @@ -26,12 +26,29 @@ This section describes rules and recommendations for the naming of node labels, ** Leading and trailing whitespace characters will be removed automatically. For example, `MATCH ( a ) RETURN a` is equivalent to `MATCH (a) RETURN a`. -[NOTE] -==== +=== Using special characters in names Non-alphabetic characters, including numbers, symbols and whitespace characters, *can* be used in names, but *must* be escaped using backticks. For example: `++`^n`++`, `++`1first`++`, `++`$$n`++`, and `++`my variable has spaces`++`. Database names are an exception and may include dots without the need for escaping. For example: naming a database `foo.bar.baz` is perfectly valid. + +Within an escaped name, the following escaping sequences are allowed: + +[options="header", cols=">1,<2"] +|=== +|Escape sequence|Character +|````| Backtick +|`\uxxxx`| Unicode UTF-16 code point (4 hex digits must follow the `\u`) +|=== + +[NOTE] +==== +Using escaped names with unsanitized user input makes you vulnerable to Cypher injection. +Some techniques to mitigate this are: + +* sanitizing (and validating) the user input. +* remodeling your data model to avoid this data access pattern. + ==== == Scoping and namespace rules From 459dc91344eb333400c079572e270bf4c639d7ab Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Fri, 13 Jan 2023 09:10:25 +0100 Subject: [PATCH 055/383] Temporary remove the new relationship constraint docs again as it is re-hidden by feature flag (#304) Feature flag re-added due to issues with the new constraints and the `MERGE` clause. Companion PR with: https://github.com/neo4j/docs-cheat-sheet/pull/51 - [x] Needs to be confirmed that the documentation that now gets removed isn't in the 5.x branch - [ ] check again once merged --- modules/ROOT/pages/constraints/examples.adoc | 145 ++++++++++-------- modules/ROOT/pages/constraints/index.adoc | 23 ++- modules/ROOT/pages/constraints/syntax.adoc | 67 ++++++-- ...ions-additions-removals-compatibility.adoc | 20 ++- .../ROOT/pages/execution-plans/operators.adoc | 4 +- .../introduction/neo4j-databases-graphs.adoc | 12 +- modules/ROOT/pages/keyword-glossary.adoc | 18 ++- 7 files changed, 202 insertions(+), 87 deletions(-) diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc index 0a440f566..246d35387 100644 --- a/modules/ROOT/pages/constraints/examples.adoc +++ b/modules/ROOT/pages/constraints/examples.adoc @@ -50,10 +50,13 @@ FOR (book:Book) REQUIRE book.isbn IS UNIQUE Unique constraints added: 1 ---- +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. ==== +//// ====== @@ -87,10 +90,13 @@ Assuming no constraint with the given name or other node property uniqueness con Unique constraints added: 1 ---- +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. ==== +//// ====== @@ -132,10 +138,13 @@ OPTIONS { Unique constraints added: 1 ---- +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. ==== +//// ====== @@ -172,10 +181,13 @@ Constraint already exists: Constraint( id=4, name='preExistingUnique', type='UNIQUENESS', schema=(:Book {title}), ownedIndex=3 ) ---- +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6.0. ==== +//// ====== @@ -313,14 +325,18 @@ Unable to create Constraint( name='constraint_62365a16', type='UNIQUENESS', sche Both Node(0) and Node(1) have the label `Book` and property `isbn` = '1449356265' ---- +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6.0. ==== +//// ====== - +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [[constraints-examples-relationship-uniqueness]] == Relationship property uniqueness constraints @@ -450,10 +466,8 @@ There are no valid index configuration values for the constraint-backing range i Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type, when that constraint already exists. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT preExistingUnique FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE -//// +// Set-up to get expected behavior: +// CREATE CONSTRAINT preExistingUnique FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE .Query [source, cypher, indent=0] @@ -483,10 +497,8 @@ Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', sche Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type, when an index already exists on that relationship type and property combination. -//// -Set-up to get expected behavior: -CREATE INDEX FOR ()-[friend:FRIENDS_WITH]-() ON (friend.nickname) -//// +// Set-up to get expected behavior: +// CREATE INDEX FOR ()-[friend:FRIENDS_WITH]-() ON (friend.nickname) .Query [source, cypher, indent=0] @@ -516,10 +528,8 @@ A constraint cannot be created until the index has been dropped. Create a `FRIENDS_WITH` relationship with an `nickname` that is not already in the database. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE -//// +// Set-up to get expected behavior: +// CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE .Query [source, cypher, indent=0] @@ -552,11 +562,9 @@ Labels added: 2 Create a `FRIENDS_WITH` relationship with an `nickname` that is already used in the database. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE -CREATE (:Person {name: 'Emma'}), (:Person {name: 'Josefin'})-[:FRIENDS_WITH {nickname: 'Mimi'}]->(:Person {name: 'Emilia'}) -//// +// Set-up to get expected behavior: +// CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +// CREATE (:Person {name: 'Emma'}), (:Person {name: 'Josefin'})-[:FRIENDS_WITH {nickname: 'Mimi'}]->(:Person {name: 'Emilia'}) .Query [source, cypher, indent=0] @@ -586,11 +594,9 @@ Relationship(0) already exists with type `FRIENDS_WITH` and property `nickname` Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type when there are two relationships with the same `nickname`. -//// -Set-up to get expected behavior: -CREATE (emma:Person {name: 'Emma'}), (josefin:Person {name: 'Josefin'}), (emilia:Person {name: 'Emilia'}) -CREATE (josefin)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia), (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) -//// +// Set-up to get expected behavior: +// CREATE (emma:Person {name: 'Emma'}), (josefin:Person {name: 'Josefin'}), (emilia:Person {name: 'Emilia'}) +// CREATE (josefin)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia), (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) .Query [source, cypher, indent=0] @@ -609,7 +615,7 @@ Both Relationship(0) and Relationship(1) have the type `FRIENDS_WITH` and proper ---- ====== - +//// [role=enterprise-edition] [[constraints-examples-node-property-existence]] @@ -652,11 +658,14 @@ FOR (book:Book) REQUIRE book.isbn IS NOT NULL Property existence constraints added: 1 ---- +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The statistics for property existence constraints will be split between nodes and relationships in Neo4j version 6.0. For the node property existence constraints, they will say `Node property existence constraints`. ==== +//// ====== @@ -908,11 +917,14 @@ FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL Property existence constraints added: 1 ---- +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The statistics for property existence constraints will be split between nodes and relationships in Neo4j version 6.0. For the relationship property existence constraints, they will say `Relationship property existence constraints`. ==== +//// ====== @@ -1444,7 +1456,8 @@ Node(0) with label `Person` must have the properties (`firstname`, `surname`) ====== - +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [role=enterprise-edition] [[constraints-examples-relationship-key]] == Relationship key constraints @@ -1504,10 +1517,8 @@ This will ensure that no error is thrown and no constraint is created if any oth .+CREATE CONSTRAINT+ ====== -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY -//// +// Set-up to get expected behavior: +// CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY .Query [source, cypher, indent=0] @@ -1579,10 +1590,8 @@ There is no valid index configuration values for the constraint-backing range in Create a relationship key constraint on the properties `startPoint` and `endPoint` on relationships with the `ROAD` relationship type, when a property uniqueness constraint already exists on the same relationship type and property combination. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT preExistingUnique FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE -//// +// Set-up to get expected behavior: +// CREATE CONSTRAINT preExistingUnique FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE .Query [source, cypher, indent=0] @@ -1612,10 +1621,8 @@ Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', sche Create a named relationship key constraint on the property `coordinates` on relationships with the `INTERSECTION` relationship type, when an index already exists with the given name. -//// -Set-up to get expected behavior: -CREATE INDEX intersections FOR ()-[intersect:Roundabout]-() ON (intersect.coordinates) -//// +// Set-up to get expected behavior: +// CREATE INDEX intersections FOR ()-[intersect:Roundabout]-() ON (intersect.coordinates) .Query [source, cypher, indent=0] @@ -1645,11 +1652,9 @@ There already exists an index called 'intersections'. Create a `ROAD` relationship with both a `startPoint` and `endPoint` property. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY -CREATE (:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) -//// +// Set-up to get expected behavior: +// CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY +// CREATE (:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) .Query [source, cypher, indent=0] @@ -1681,11 +1686,9 @@ Properties set: 2 Trying to create a `INTERSECTION` relationship without a `coordinates` property, given a relationship key constraint on `:INTERSECTION(coordinates)`, will fail. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY -CREATE (:Road {name: 'a'}), (:Road {name: 'b'}) -//// +// Set-up to get expected behavior: +// CREATE CONSTRAINT FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY +// CREATE (:Road {name: 'a'}), (:Road {name: 'b'}) .Query [source, cypher, indent=0] @@ -1715,12 +1718,10 @@ Relationship(0) with type `INTERSECTION` must have the property `coordinates` Trying to remove the `endPoint` property from an existing relationship `ROAD`, given a `RELATIONSHIP KEY` constraint on `:ROAD(startPoint, endPoint)`. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY -CREATE (a:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (b:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) -CREATE (a)-[:ROAD {startPoint: a.coordinates, endPoint: b.coordinates}]->(b) -//// +// Set-up to get expected behavior: +// CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY +// CREATE (a:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (b:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) +// CREATE (a)-[:ROAD {startPoint: a.coordinates, endPoint: b.coordinates}]->(b) .Query [source, cypher, indent=0] @@ -1749,12 +1750,10 @@ Relationship(0) with type `ROAD` must have the properties (`startPoint`, `endPoi Trying to create a relationship key constraint on the property `coordinates` on relationships with the `INTERSECTION` relationship type will fail when two relationships with identical `coordinates` already exists in the database. -//// -Set-up to get expected behavior: -CREATE (a:Road {name: 'a'}), (b:Road {name: 'b'}) -CREATE (a)-[:INTERSECTION {coordinates: point({x:1, y:2})}]->(b) -CREATE (a)<-[:INTERSECTION {coordinates: point({x:1, y:2})}]-(b) -//// +// Set-up to get expected behavior: +// CREATE (a:Road {name: 'a'}), (b:Road {name: 'b'}) +// CREATE (a)-[:INTERSECTION {coordinates: point({x:1, y:2})}]->(b) +// CREATE (a)<-[:INTERSECTION {coordinates: point({x:1, y:2})}]-(b) .Query [source, cypher, indent=0] @@ -1773,7 +1772,7 @@ Both Relationship(0) and Relationship(1) have the type `INTERSECTION` and proper ---- ====== - +//// [[constraints-examples-drop-constraint]] == Drop a constraint by name @@ -1787,7 +1786,9 @@ Both Relationship(0) and Relationship(1) have the type `INTERSECTION` and proper === Drop a constraint A constraint can be dropped using the name with the `DROP CONSTRAINT constraint_name` command. -It is the same command for uniqueness, property existence, and node/relationship key constraints. +It is the same command for uniqueness, property existence, and node key constraints. +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//It is the same command for uniqueness, property existence, and node/relationship key constraints. The name of the constraint can be found using the xref::constraints/syntax.adoc#constraints-syntax-list[`SHOW CONSTRAINTS` command], given in the output column `name`. @@ -1822,7 +1823,9 @@ Named constraints removed: 1 === Drop a non-existing constraint If it is uncertain if any constraint with a given name exists and you want to drop it if it does but not get an error should it not, use `IF EXISTS`. -It is the same command for uniqueness, property existence, and node/relationship key constraints. +It is the same command for uniqueness, property existence, and node constraints. +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//It is the same command for uniqueness, property existence, and node/relationship key constraints. .+DROP CONSTRAINT+ ====== @@ -1871,7 +1874,7 @@ This can be used to drop the constraint with the xref::constraints/syntax.adoc#c //// Set-up to get expected behavior: CREATE CONSTRAINT isbnConstraint FOR (n:Book) REQUIRE (n.isbn) IS UNIQUE -CREATE CONSTRAINT roadConstraint FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE +(TODO: not relevant until the constraint is re-added) CREATE CONSTRAINT roadConstraint FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE //// .Query @@ -1880,6 +1883,18 @@ CREATE CONSTRAINT roadConstraint FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.end SHOW CONSTRAINTS ---- +[queryresult] +---- ++---------------------------------------------------------------------------------------------------+ +| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | ++---------------------------------------------------------------------------------------------------+ +| 4 | "isbnConstraint" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | "isbnConstraint" | ++---------------------------------------------------------------------------------------------------+ +2 rows +---- + +//// +TODO: Switch the result above to the one below when adding back relationship key and uniqueness constraints [queryresult] ---- +------------------------------------------------------------------------------------------------------------------------------------+ @@ -1890,12 +1905,16 @@ SHOW CONSTRAINTS +------------------------------------------------------------------------------------------------------------------------------------+ 2 rows ---- +//// +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The `type` column returns `UNIQUENESS` for the node property uniqueness constraint and `RELATIONSHIP_UNIQUENESS` for the relationship property uniqueness constraint. The `type` for node property uniqueness constraint will be updated to `NODE_UNIQUENESS` in Neo4j version 6.0. ==== +//// ====== diff --git a/modules/ROOT/pages/constraints/index.adoc b/modules/ROOT/pages/constraints/index.adoc index 472dc110e..e356d748a 100644 --- a/modules/ROOT/pages/constraints/index.adoc +++ b/modules/ROOT/pages/constraints/index.adoc @@ -18,10 +18,13 @@ Unique node property constraints, or node property uniqueness constraints, ensur For property uniqueness constraints on multiple properties, the combination of the property values is unique. Node property uniqueness constraints do not require all nodes to have a unique value for the properties listed (nodes without all properties are not subject to this rule). +// TODO: Re-add this part when adding back relationship key and uniqueness constraints +//// *Unique relationship property constraints*:: Unique relationship property constraints, or relationship property uniqueness constraints, ensure that property values are unique for all relationships with a specific type. For property uniqueness constraints on multiple properties, the combination of the property values is unique. Relationship property uniqueness constraints do not require all relationships to have a unique value for the properties listed (relationships without all properties are not subject to this rule). +//// *Node property existence constraints* label:enterprise-edition[]:: Node property existence constraints ensure that a property exists for all nodes with a specific label. @@ -47,6 +50,8 @@ Queries attempting to do any of the following will fail: * Remove one of the mandatory properties. * Update the properties so that the combination of property values is no longer unique. +// TODO: Re-add this part when adding back relationship key and uniqueness constraints +//// *Relationship key constraints* label:enterprise-edition[]:: Relationship key constraints ensure that, for a given type and set of properties: + @@ -60,11 +65,14 @@ Queries attempting to do any of the following will fail: * Create new relationships without all the properties or where the combination of property values is not unique. * Remove one of the mandatory properties. * Update the properties so that the combination of property values is no longer unique. +//// [NOTE] ==== -Node key constraints, relationship key constraints, node property existence constraints, and relationship property existence constraints are only available in Neo4j Enterprise Edition. +Node key constraints, node property existence constraints, and relationship property existence constraints are only available in Neo4j Enterprise Edition. +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//Node key constraints, relationship key constraints, node property existence constraints, and relationship property existence constraints are only available in Neo4j Enterprise Edition. Databases containing one of these constraint types cannot be opened using Neo4j Community Edition. ==== @@ -73,15 +81,26 @@ Databases containing one of these constraint types cannot be opened using Neo4j Creating a constraint has the following implications on indexes: +* Adding a node key or property uniqueness constraint on a single property also adds an index on that property, and therefore, an index of the same index type, label, and property combination cannot be added separately. +* Adding a node key or property uniqueness constraint for a set of properties also adds an index on those properties, and therefore, an index of the same index type, label, and properties combination cannot be added separately. +* Cypher will use these indexes for lookups just like other indexes. + Refer to xref::indexes-for-search-performance.adoc[] for more details on indexes. +* If a node key or property uniqueness constraint is dropped and the backing index is still required, the index need to be created explicitly. + +//// +TODO: Switch the part above to the one below when adding back relationship key and uniqueness constraints * Adding a node key, relationship key, or property uniqueness constraint on a single property also adds an index on that property, and therefore, an index of the same index type, label/relationship type, and property combination cannot be added separately. * Adding a node key, relationship key, or property uniqueness constraint for a set of properties also adds an index on those properties, and therefore, an index of the same index type, label/relationship type, and properties combination cannot be added separately. * Cypher will use these indexes for lookups just like other indexes. Refer to xref::indexes-for-search-performance.adoc[] for more details on indexes. * If a node key, relationship key, or property uniqueness constraint is dropped and the backing index is still required, the index need to be created explicitly. +//// Additionally, the following is true for constraints: -* A given label or relationship type can have multiple constraints, and uniqueness and property existence constraints can be combined on the same property. +* A given label can have multiple constraints, and uniqueness and property existence constraints can be combined on the same property. +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//* A given label or relationship type can have multiple constraints, and uniqueness and property existence constraints can be combined on the same property. * Adding constraints is an atomic operation that can take a while -- all existing data has to be scanned before Neo4j DBMS can turn the constraint 'on'. * Best practice is to give the constraint a name when it is created. If the constraint is not explicitly named, it will get an auto-generated name. diff --git a/modules/ROOT/pages/constraints/syntax.adoc b/modules/ROOT/pages/constraints/syntax.adoc index 4ca00d0dd..dee270c2a 100644 --- a/modules/ROOT/pages/constraints/syntax.adoc +++ b/modules/ROOT/pages/constraints/syntax.adoc @@ -56,6 +56,8 @@ REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS [NODE] UNIQUE Index provider can be specified using the `OPTIONS` clause. +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [[constraints-syntax-create-rel-unique]] [discrete] === Create a relationship property uniqueness constraint @@ -79,6 +81,7 @@ REQUIRE (r.propertyName_1, ..., r.propertyName_n) IS [REL[ATIONSHIP]] UNIQUE ---- Index provider can be specified using the `OPTIONS` clause. +//// [[constraints-syntax-create-node-exists]] @@ -146,6 +149,8 @@ REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS [NODE] KEY Index provider can be specified using the `OPTIONS` clause. +//// +TODO: Re-add this part when adding back relationship key and uniqueness constraints [[constraints-syntax-create-rel-key]] [discrete] === Create a relationship key constraint label:enterprise-edition[] @@ -169,6 +174,7 @@ REQUIRE (r.propertyName_1, ..., r.propertyName_n) IS [REL[ATIONSHIP]] KEY ---- Index provider can be specified using the `OPTIONS` clause. +//// [[constraints-syntax-drop]] @@ -202,6 +208,21 @@ Listing constraints requires the xref::access-control/database-administration.ad The simple version of the command allows for a `WHERE` clause and will give back the default set of output columns: +[source, syntax, role="noheader", indent=0] +---- +SHOW [ + ALL + |UNIQUE[NESS] + |NODE [PROPERTY] EXIST[ENCE] + |REL[ATIONSHIP] [PROPERTY] EXIST[ENCE] + |[PROPERTY] EXIST[ENCE] + |NODE KEY +] CONSTRAINT[S] + [WHERE expression] +---- + +//// +TODO: Switch the syntax above to the one below when adding back relationship key and uniqueness constraints [source, syntax, role="noheader", indent=0] ---- SHOW [ @@ -218,9 +239,27 @@ SHOW [ ] CONSTRAINT[S] [WHERE expression] ---- +//// To get the full set of output columns, a yield clause is needed: +[source, syntax, role="noheader", indent=0] +---- +SHOW [ + ALL + |UNIQUE[NESS] + |NODE [PROPERTY] EXIST[ENCE] + |REL[ATIONSHIP] [PROPERTY] EXIST[ENCE] + |[PROPERTY] EXIST[ENCE] + |NODE KEY +] CONSTRAINT[S] +YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] + [WHERE expression] + [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] +---- + +//// +TODO: Switch the syntax above to the one below when adding back relationship key and uniqueness constraints [source, syntax, role="noheader", indent=0] ---- SHOW [ @@ -239,6 +278,7 @@ YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- +//// The type filtering keywords filters the returned constraints on constraint type: @@ -253,14 +293,18 @@ The type filtering keywords filters the returned constraints on constraint type: | Returns all constraints, no filtering on constraint type. This is the default if none is given. -|NODE UNIQUE[NESS] -| Returns the node property uniqueness constraints. +// TODO: Re-add these parts when adding back relationship key and uniqueness constraints +//|NODE UNIQUE[NESS] +//| Returns the node property uniqueness constraints. -|REL[ATIONSHIP] UNIQUE[NESS] -| Returns the relationship property uniqueness constraints. +//|REL[ATIONSHIP] UNIQUE[NESS] +//| Returns the relationship property uniqueness constraints. |UNIQUE[NESS] -| Returns all property uniqueness constraints, for both nodes and relationships. +| Returns all property uniqueness constraints. +// TODO: Switch the part above to the one below when adding back relationship key and uniqueness constraints +//|UNIQUE[NESS] +//| Returns all property uniqueness constraints, for both nodes and relationships. |NODE [PROPERTY] EXIST[ENCE] | Returns the node property existence constraints. @@ -274,11 +318,12 @@ This is the default if none is given. |NODE KEY | Returns the node key constraints. -|REL[ATIONSHIP] KEY -| Returns the relationship key constraints. +// TODO: Re-add these parts when adding back relationship key and uniqueness constraints +//|REL[ATIONSHIP] KEY +//| Returns the relationship key constraints. -|KEY -| Returns all node and relationship key constraints. +//|KEY +//| Returns all node and relationship key constraints. |=== @@ -297,7 +342,9 @@ The returned columns from the show command is: | Name of the constraint (explicitly set by the user or automatically assigned). label:default-output[] | type -| The ConstraintType of this constraint (`UNIQUENESS` (node uniqueness), `RELATIONSHIP_UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, `NODE_KEY`, or `RELATIONSHIP_KEY`). label:default-output[] +| The ConstraintType of this constraint (`UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, or `NODE_KEY`). label:default-output[] +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//| The ConstraintType of this constraint (`UNIQUENESS` (node uniqueness), `RELATIONSHIP_UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, `NODE_KEY`, or `RELATIONSHIP_KEY`). label:default-output[] | entityType | Type of entities this constraint represents (nodes or relationship). label:default-output[] diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 0e48d7ebd..7e7b9c8c7 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -12,6 +12,8 @@ Replacement syntax for deprecated and removed features are also indicated. [[cypher-deprecations-additions-removals-5.3]] == Version 5.3 +// TODO: Re-add this part in whatever version (not 5.3...) the relationship key and uniqueness constraints are added back in +//// === Deprecated features [cols="2", options="header"] @@ -34,6 +36,7 @@ The current constraint type for node property uniqueness constraints, `UNIQUENES This will also be reflected in updates to some error messages and query statistics. |=== +//// === Updated features @@ -85,8 +88,20 @@ a| A `COUNT` subquery now supports any non-writing query. For example, it now supports `UNION` and `CALL` clauses. +a| +label:syntax[] +label:updated[] +[source, cypher, role="noheader"] +---- +SHOW UNIQUE[NESS] CONSTRAINTS +---- +a| +The property uniqueness constraint type filter now allow both `UNIQUE` and `UNIQUENESS` keywords. + |=== +// TODO: Re-add this part in whatever version (not 5.3...) the relationship key and uniqueness constraints are added back in +//// === New features [cols="2", options="header"] @@ -127,11 +142,10 @@ a| Added filtering for the new constraint types to `SHOW CONSTRAINTS`. Includes filtering for the node part, relationship part, or both parts of each type (`NODE KEY` filtering already exists previously). -The existing `UNIQUE` filter will now return both node and relationship property uniqueness constraints. -All property uniqueness constraint type filters now allow both `UNIQUE` and `UNIQUENESS` keywords. - +The existing `UNIQUENESS` filter will now return both node and relationship property uniqueness constraints. |=== +//// [[cypher-deprecations-additions-removals-5.2]] == Version 5.2 diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index cd9c4f9e1..74ac01220 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -4890,7 +4890,9 @@ This constraint can have any of the available constraint types: * Property uniqueness constraints * Property existence constraints label:enterprise-edition[] -* Node or relationship key constraints label:enterprise-edition[] +* Node key constraints label:enterprise-edition[] +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//* Node or relationship key constraints label:enterprise-edition[] The following query will create a property uniqueness constraint with the name `uniqueness` on the `name` property of nodes with the `Country` label. diff --git a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc index da8563c88..c656ad02e 100644 --- a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc +++ b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc @@ -78,12 +78,22 @@ a| All constraints: xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], +xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], and +xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints]. +//// +TODO: Switch the part above to the one below when adding back relationship key and uniqueness constraints +All constraints: +xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], +xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship property uniqueness constraints], xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints], and xref::constraints/examples.adoc#constraints-examples-relationship-key[relationship key constraints]. +//// a| -Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node] and xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship] property uniqueness constraints. +Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints]. +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node] and xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship] property uniqueness constraints. |=== diff --git a/modules/ROOT/pages/keyword-glossary.adoc b/modules/ROOT/pages/keyword-glossary.adoc index 67e42632f..32b98c840 100644 --- a/modules/ROOT/pages/keyword-glossary.adoc +++ b/modules/ROOT/pages/keyword-glossary.adoc @@ -54,17 +54,19 @@ Typically used when modifying or importing large amounts of data. | Schema | Create a constraint that ensures all nodes with a particular label have all the specified properties and that the combination of property values is unique; i.e. ensures existence and uniqueness. -| xref::constraints/syntax.adoc#constraints-syntax-create-rel-key[CREATE CONSTRAINT [rel_key\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] KEY [OPTIONS {optionKey: optionValue[, ...\]}\]] -| Schema -| Create a constraint that ensures all relationships with a particular type have all the specified properties and that the combination of property values is unique; i.e. ensures existence and uniqueness. +// TODO: Re-add this part when adding back relationship key and uniqueness constraints +//| xref::constraints/syntax.adoc#constraints-syntax-create-rel-key[CREATE CONSTRAINT [rel_key\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] KEY [OPTIONS {optionKey: optionValue[, ...\]}\]] +//| Schema +//| Create a constraint that ensures all relationships with a particular type have all the specified properties and that the combination of property values is unique; i.e. ensures existence and uniqueness. | xref::constraints/syntax.adoc#constraints-syntax-create-node-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR (n:Label) REQUIRE (n.prop1[, ..., n.propN\]) IS [NODE\] UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a constraint that ensures the uniqueness of the combination of node label and property values for a particular property key combination across all nodes. -| xref::constraints/syntax.adoc#constraints-syntax-create-rel-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] -| Schema -| Create a constraint that ensures the uniqueness of the combination of relationship type and property values for a particular property key combination across all relationships. +// TODO: Re-add this part when adding back relationship key and uniqueness constraints +//| xref::constraints/syntax.adoc#constraints-syntax-create-rel-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] +//| Schema +//| Create a constraint that ensures the uniqueness of the combination of relationship type and property values for a particular property key combination across all relationships. | xref::indexes-for-full-text-search.adoc[CREATE FULLTEXT INDEX [name\] [IF NOT EXISTS\] FOR (n:Label["\|" ... "\|" LabelN\]) ON EACH "[" n.property[, ..., n.propertyN\] "\]" [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema @@ -176,7 +178,9 @@ Either the pattern already exists, or it needs to be created. | Writing | Update labels on nodes and properties on nodes and relationships. -| xref::constraints/syntax.adoc#constraints-syntax-list[SHOW [ALL\|UNIQUE\|NODE [PROPERTY\] EXIST[ENCE\]\|REL[ATIONSHIP\] [PROPERTY\] EXIST[ENCE\]\|[PROPERTY\] EXIST[ENCE\]\|NODE KEY\] CONSTRAINT[S\]] +| xref::constraints/syntax.adoc#constraints-syntax-list[SHOW [ALL\|UNIQUE[NESS\]\|NODE [PROPERTY\] EXIST[ENCE\]\|REL[ATIONSHIP\] [PROPERTY\] EXIST[ENCE\]\|[PROPERTY\] EXIST[ENCE\]\|NODE KEY\] CONSTRAINT[S\]] +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//| xref::constraints/syntax.adoc#constraints-syntax-list[SHOW [ALL\|NODE UNIQUE[NESS\]\|REL[ATIONSHIP\] UNIQUE[NESS\]\|UNIQUE[NESS\]\|NODE [PROPERTY\] EXIST[ENCE\]\|REL[ATIONSHIP\] [PROPERTY\] EXIST[ENCE\]\|[PROPERTY\] EXIST[ENCE\]\|NODE KEY\|REL[ATIONSHIP\] KEY\|KEY\] CONSTRAINT[S\]] | Schema | List constraints in the database, either all or filtered on type. From 71fc1544fee559ee630160e4dccc385a5030f7ab Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Fri, 13 Jan 2023 14:47:57 +0100 Subject: [PATCH 056/383] Improve alias documentation around escaping of alias names (#302) Also: - added links to the `graph.propertiesByName()` function when adding or changing the properties of an alias - moved create alias sub-section into create alias section Co-authored-by: Lasse Heemann --- modules/ROOT/pages/aliases.adoc | 241 +++++++++++++++++++++++++++++--- 1 file changed, 221 insertions(+), 20 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 589543bc2..409392af9 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -338,7 +338,7 @@ To list just one database alias, the `SHOW ALIASES` command takes an alias name; .Query [source, cypher, indent=0] ---- -SHOW ALIAS films FOR DATABASES +SHOW ALIAS films FOR DATABASES ---- .Result @@ -355,7 +355,7 @@ SHOW ALIAS films FOR DATABASES .Query [source, cypher, indent=0] ---- -SHOW ALIAS library.romance FOR DATABASES +SHOW ALIAS library.romance FOR DATABASES ---- .Result @@ -502,15 +502,7 @@ The `IF NOT EXISTS` and `OR REPLACE` parts of this command cannot be used togeth [NOTE] ==== -Database alias names are subject to the xref::syntax/naming.adoc[standard Cypher restrictions on valid identifiers]. - -The following naming rules apply: - -* A name is a valid identifier. -* Name length can be up to 65534 characters. -* Names cannot end with dots. -* Names that begin with an underscore or with the prefix `system` are reserved for internal use. -* Non-alphabetic characters, including numbers, symbols and whitespace characters, can be used in names, but must be escaped using backticks. +Database alias names are subject to the rules specified in the xref:alias-management-escaping[Alias names and escaping] section. ==== @@ -576,6 +568,7 @@ SHOW ALIAS `northwind` FOR DATABASE ====== Local database aliases can also be given properties. +These properties can then be used in queries with the xref::functions/graph.adoc#functions-graph-propertiesByName[`graph.propertiesByName()` function]. .Query [source, cypher, indent=0] @@ -699,7 +692,7 @@ When a database alias pointing to a remote database has been created, its detail .Query [source, cypher, indent=0] ---- -SHOW ALIAS `remote-northwind` +SHOW ALIAS `remote-northwind` FOR DATABASE ---- @@ -762,6 +755,7 @@ SHOW ALIAS `remote-with-driver-settings` FOR DATABASE YIELD * .+Setting properties for remote database aliases+ ====== Just as the local database aliases, the remote database aliases can be given properties. +These properties can then be used in queries with the xref::functions/graph.adoc#functions-graph-propertiesByName[`graph.propertiesByName()` function]. .Query [source, cypher, indent=0] @@ -799,7 +793,7 @@ SHOW ALIAS `remote-northwind-2021` FOR DATABASE YIELD name, properties [role=enterprise-edition] [[alias-management-create-composite-database-alias]] -== Create database aliases in composite databases +=== Create database aliases in composite databases Both local and remote database aliases can be part of a xref::databases.adoc#administration-databases-create-composite-database[composite database]. @@ -897,9 +891,9 @@ Failed to create the specified database alias 'yard': Database 'garden' is compo CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020`; CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER alice PASSWORD 'password'; CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER alice PASSWORD 'password' -DRIVER { +DRIVER { connection_timeout: duration({ minutes: 1 }), - connection_pool_max_size: 10 + connection_pool_max_size: 10 }; CREATE ALIAS garden.flowers FOR DATABASE `perennial-flowers`; @@ -980,7 +974,7 @@ SHOW DATABASE `northwind-graph-2021` |=== | +name+ | +type+ | +aliases+ | +access+ | +address+ | +role+ | +writer+ | +requestedStatus+ | +currentStatus+ | +statusMessage+ | +default+ | +home+ | +constituents+ -| +"northwind-graph-2021"+ | +"standard"+ | +["northwind"]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +"true"+ | +"online"+ | +"online"+ | +""+ | +false+ +| +"northwind-graph-2021"+ | +"standard"+ | +["northwind"]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +"true"+ | +"online"+ | +"online"+ | +""+ | +false+ |+false+ | +[]+ 13+d|Rows: 1 @@ -1089,6 +1083,8 @@ ALTER ALIAS `movie scripts` SET DATABASE PROPERTIES { nameContainsSpace: true } System updates: 1 Rows: 0 ---- + +The updated properties can then be used in queries with the xref::functions/graph.adoc#functions-graph-propertiesByName[`graph.propertiesByName()` function]. ====== .+Altering local and remote aliases in composite databases+ @@ -1119,6 +1115,8 @@ System updates: 1 Rows: 0 ---- +The updated properties can then be used in queries with the xref::functions/graph.adoc#functions-graph-propertiesByName[`graph.propertiesByName()` function]. + ====== The changes for all database aliases will show up in the `SHOW ALIASES FOR DATABASE` command. @@ -1128,7 +1126,7 @@ The changes for all database aliases will show up in the `SHOW ALIASES FOR DATAB [source, cypher, indent=0] ---- SHOW ALIASES FOR DATABASE YIELD * -WHERE name IN ['northwind', 'remote-northwind', 'remote-with-driver-settings', 'movie scripts', +WHERE name IN ['northwind', 'remote-northwind', 'remote-with-driver-settings', 'movie scripts', 'motion pictures', 'garden.flowers', 'garden.trees'] ---- @@ -1140,7 +1138,7 @@ WHERE name IN ['northwind', 'remote-northwind', 'remote-with-driver-settings', ' | +"garden.flowers"+ | +"perennial-flowers"+ | +"local"+ | ++ | ++ | ++ | +{"perennial":true}+ | +"garden.trees"+ | +"updatedtrees"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{}+ | +{"treeversion":2}+ | +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | ++ | +{"namecontainsspace":true,"moreinfo":"no, not really"}+ -| +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{}+ | +{"namecontainsspace":true}+ +| +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{}+ | +{"namecontainsspace":true}+ | +"northwind"+ | +"northwind-graph-2021"+ | +"local"+ | ++ | ++ | ++ |+[]+ | +"remote-northwind"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://other-location:7687"+ | +"alice"+ | +{}+ | +{}+ | +"remote-with-driver-settings"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"bob"+ | +{logging_level -> "DEBUG", connection_timeout -> PT1M}+ |+[]+ @@ -1179,9 +1177,9 @@ ALTER ALIAS `no-alias` IF EXISTS SET DATABASE TARGET `northwind-graph-2021` CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2021`; CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://other-location:7687" USER alice PASSWORD 'password'; CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER bob PASSWORD 'newPassword' -DRIVER { +DRIVER { connection_timeout: duration({ minutes: 1 }), - logging_level: "debug" + logging_level: "debug" }; CREATE ALIAS `northwind-2022` FOR DATABASE `northwind-graph-2022` PROPERTIES { newestNorthwind: true, index: 3 }; CREATE ALIAS `remote-northwind-2021` @@ -1322,3 +1320,206 @@ DROP ALIAS `northwind` IF EXISTS FOR DATABASE ====== + +[role=enterprise-edition] +[[alias-management-escaping]] +== Alias names and escaping +//// +Set-up +CREATE DATABASE `northwind-graph` +CREATE COMPOSITE DATABASE `my-composite-database-with-dashes` +CREATE COMPOSITE DATABASE `my.composite.database.with.dots` +CREATE COMPOSITE DATABASE mySimpleCompositeDatabase +CREATE COMPOSITE DATABASE `myCompositeDatabase.withDot` +//// + +Database alias names are subject to the xref::syntax/naming.adoc[standard Cypher restrictions on valid identifiers]. + +The following naming rules apply: + +* A name is a valid identifier. +* Name length can be up to 65534 characters. +* Names cannot end with dots. +* Unescaped dots signify that the database alias belongs to a composite database, separating the composite database name and the alias name. +* Names that begin with an underscore or with the prefix `system` are reserved for internal use. +* Non-alphabetic characters, including numbers, symbols, dots, and whitespace characters, can be used in names, but must be escaped using backticks. + +The name restrictions and escaping rules apply to all the different database alias commands. + +When it comes to escaping names using backticks, there are some additional things to consider around database aliases in composite databases: + +.+Escaping database alias and composite database names+ +====== + +The composite database name and the database alias name need to be escaped individually. +The following example creates a database alias named `my alias with spaces` as a constituent in the composite database named `my-composite-database-with-dashes`: + +.Query +[source, cypher, indent=0] +---- +CREATE ALIAS `my-composite-database-with-dashes`.`my alias with spaces` FOR DATABASE `northwind-graph` +---- + +[source, result, role="noheader"] +---- +System updates: 1 +Rows: 0 +---- + +When not escaped individually, a database alias with the full name `my alias with.dots and spaces` gets created instead: + +.Query +[source, cypher, indent=0] +---- +CREATE ALIAS `my alias with.dots and spaces` FOR DATABASE `northwind-graph` +---- + +[source, result, role="noheader"] +---- +System updates: 1 +Rows: 0 +---- + +====== + +.+Handling multiple dots+ +====== + +Database alias names may also include dots. +Though these always need to be escaped in order to avoid ambiguity with the composite database and database alias split character. + +.Query +[source, cypher, indent=0] +---- +CREATE ALIAS `my.alias.with.dots` FOR DATABASE `northwind-graph` +---- + +[source, result, role="noheader"] +---- +System updates: 1 +Rows: 0 +---- + +.Query +[source, cypher, indent=0] +---- +CREATE ALIAS `my.composite.database.with.dots`.`my.other.alias.with.dots` FOR DATABASE `northwind-graph` +---- + +[source, result, role="noheader"] +---- +System updates: 1 +Rows: 0 +---- + +====== + +.+Single dots and local database aliases+ label:deprecated[] +====== +There is a special case for local database aliases with a single dot without any existing composite database. +If a composite database `some` exists, the query below will create a database alias named `alias` within the composite database `some`. +If no such database exists, however, the same query will instead create a database alias named `some.alias`: + +.Query +[source, cypher, indent=0] +---- +CREATE ALIAS some.alias FOR DATABASE `northwind-graph` +---- + +[source, result, role="noheader"] +---- +System updates: 1 +Rows: 0 +---- + +====== + +.+Handling parameters+ +====== + +When using parameters, names cannot be escaped. +When the given parameter includes dots, the first dot will be considered the divider for the composite database. + +Consider the query: + +.Query +[source, cypher, indent=0] +---- +CREATE ALIAS $aliasname FOR DATABASE `northwind-graph` +---- + +And parameter + +.Parameters +[source,javascript, indent=0] +---- +{ + "aliasname": "mySimpleCompositeDatabase.myAlias" +} +---- + +If the composite database `mysimplecompositedatabase` exists, then a database alias `myalias` will be created in that composite database. +If no such composite database exists, then a database alias `mysimplecompositedatabase.myalias` will be created. + +On the contrary, a database alias `myalias` cannot be created in composite `mycompositedatabase.withdot` using parameters. +Consider the same query but with the following parameter: + +.Parameters +[source,javascript, indent=0] +---- +{ + "aliasname": "myCompositeDatabase.withDot.myAlias" +} +---- + +Since the first dot will be used as a divider, the command will attempt to create the database alias `withdot.myalias` in the composite database `mycompositedatabase`. +If `mycompositedatabase` doesn't exist, the command will create a database alias with the name `mycompositedatabase.withdot.myalias`, which is not part of any composite database. + +In these cases, it is recommended to avoid parameters and explicitly escape the composite database name and alias name separately to avoid ambiguity. + +====== + +.+Handling parameters+ +====== + +Further special handling with parameters is needed for database aliases and similarly named composite databases. + +Consider the set-up: + +.Query +[source, cypher, indent=0, role="noheader"] +---- +CREATE COMPOSITE DATABASE foo +CREATE ALIAS `foo.bar` FOR DATABASE `northwind-graph` +---- + +The alias `foo.bar` does not belong to the composite database `foo`. + +Dropping this alias using parameters fails with an error about a missing alias: + +.Query +[source, cypher, indent=0] +---- +DROP ALIAS $aliasname FOR DATABASE +---- + +.Parameters +[source,javascript, indent=0] +---- +{ + "aliasname": "foo.bar" +} +---- + +.Error message +[source, output, role="noheader", indent=0] +---- +Failed to delete the specified database alias 'foo.bar': Database alias does not exist. +---- + +Had the composite database `foo` not existed, the database alias `foo.bar` would have been dropped. + +In these cases, it is recommended to avoid parameters and explicitly escape the composite database name and alias name separately to avoid ambiguity. + +====== + From c61c334ebd7e67f1ae42ea8adec124e750694abd Mon Sep 17 00:00:00 2001 From: David Oliver Date: Mon, 16 Jan 2023 17:18:45 +0000 Subject: [PATCH 057/383] Copyright 2023 (#308) --- preview.yml | 4 ++-- publish.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/preview.yml b/preview.yml index 41c1f9d44..667419199 100644 --- a/preview.yml +++ b/preview.yml @@ -43,14 +43,14 @@ asciidoc: page-search-site: Reference Docs page-canonical-root: /docs page-pagination: true - page-no-canonical: true + page-no-canonical: true page-origin-private: true page-hide-toc: false page-mixpanel: 4bfb2414ab973c741b6f067bf06d5575 includePDF: false nonhtmloutput: "" experimental: '' - copyright: "2022 Neo4j, Inc." + copyright: "2023 Neo4j, Inc." common-license-page-uri: https://neo4j.com/docs/license/ docs-base-uri: https://neo4j.com/docs check-mark: icon:check[] diff --git a/publish.yml b/publish.yml index da9776f8f..fd8044346 100644 --- a/publish.yml +++ b/publish.yml @@ -43,14 +43,14 @@ asciidoc: page-search-site: Reference Docs page-canonical-root: /docs page-pagination: true - page-no-canonical: true + page-no-canonical: true page-origin-private: true page-hide-toc: false page-mixpanel: 4bfb2414ab973c741b6f067bf06d5575 includePDF: false nonhtmloutput: "" experimental: '' - copyright: "2022 Neo4j, Inc." + copyright: "2023 Neo4j, Inc." common-license-page-uri: https://neo4j.com/docs/license/ docs-base-uri: https://neo4j.com/docs check-mark: icon:check[] From 51004858c8448c5ab5ca3893a1848a1faed7b540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 17 Jan 2023 13:44:08 +0100 Subject: [PATCH 058/383] Update Operators page examples (#311) The examples on the Operators page returned a lot of fails when the docs team tested it. This is an attempt to fix those. --- .../ROOT/pages/execution-plans/operators.adoc | 436 ++++++++---------- 1 file changed, 185 insertions(+), 251 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 74ac01220..fcd2fdca8 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -267,7 +267,7 @@ Batch size 128 +-------------------------------+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +ProduceResults | r, n1 | 1 | 1 | 0 | | | | | | | +---------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipByIdSeek | (n1)-[r]->(anon_0) WHERE elementId(r) = $autoint_0 | 1 | 1 | 1 | 120 | 4/0 | 0.459 | Fused in Pipeline 0 | +| +DirectedRelationshipByIdSeek | (n1)-[r]->(anon_0) WHERE id(r) = $autoint_0 | 1 | 1 | 1 | 120 | 4/0 | 0.459 | Fused in Pipeline 0 | +-------------------------------+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 1, total allocated memory: 184 @@ -306,13 +306,13 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r, n1 | 2 | 2 | 0 | | | | | -| | +--------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipByIdSeek | (n1)-[r]-(anon_0) WHERE elementId(r) = $autoint_0 | 2 | 2 | 1 | 120 | 4/0 | 0.332 | Fused in Pipeline 0 | -+---------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r, n1 | 2 | 2 | 0 | | | | | +| | +--------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipByElementIdSeek| (n1)-[r]-(anon_0) WHERE elementId(r) = $autoint_0 | 2 | 2 | 1 | 120 | 4/0 | 0.332 | Fused in Pipeline 0 | ++---------------------------------+--------------------------------------------+-----+---------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 1, total allocated memory: 184 ---- @@ -940,13 +940,13 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | n | 1 | 1 | 0 | | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +NodeByIdSeek | n WHERE elementId(n) = $autoint_0 | 1 | 1 | 1 | 120 | 3/0 | 2.108 | Fused in Pipeline 0 | -+-----------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | n | 1 | 1 | 0 | | | | | +| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------- | | | +| +NodeByElementIdSeek | n WHERE elementId(n) = $autoint_0 | 1 | 1 | 1 | 120 | 3/0 | 2.108 | Fused in Pipeline 0 | ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 1, total allocated memory: 184 ---- @@ -1502,9 +1502,7 @@ Runtime version {neo4j-version-minor} | | | +-------------------------------------+----------------+------+---------+------------------------+ | | +Argument | p | 14 | 14 | 0 | 0/0 | | | +-------------------------------------+----------------+------+---------+------------------------+ -| +Filter | p:Person | 14 | 14 | 35 | 1/0 | -| | +-------------------------------------+----------------+------+---------+------------------------+ -| +AllNodesScan | p | 35 | 35 | 36 | 1/0 | +| +NodeByLabelScan| p:Person | 14 | 14 | 35 | 1/0 | +-----------------+-------------------------------------+----------------+------+---------+------------------------+ Total database accesses: 142, total allocated memory: 64 @@ -1559,9 +1557,7 @@ Runtime version {neo4j-version-minor} | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+ | +CartesianProduct | | 14 | 14 | 0 | | 0/0 | | |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+ -| | +Filter | other:Person | 14 | 14 | 35 | | 1/0 | -| | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+ -| | +AllNodesScan | other | 35 | 35 | 36 | | 1/0 | +| | +NodeByLabelScan| other:Person | 14 | 14 | 35 | | 1/0 | | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+ | +NodeIndexSeek | RANGE INDEX me:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | | 0/1 | +-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+ @@ -1625,9 +1621,7 @@ Batch size 128 | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +CartesianProduct | | 14 | 14 | 0 | 3672 | | 1.466 | In Pipeline 2 | | |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | other:Person | 14 | 14 | 35 | | | | | -| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +AllNodesScan | other | 35 | 35 | 36 | 136 | 1/0 | 0.354 | Fused in Pipeline 1 | +| | +NodeByLabelScan| other:Person | 14 | 14 | 35 | | | | | | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +NodeIndexSeek | RANGE INDEX me:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.493 | In Pipeline 0 | +-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ @@ -1692,9 +1686,7 @@ Runtime version {neo4j-version-minor} | | | +-----------------------------------------+----------------+------+---------+------------------------+ | | +Argument | other | 14 | 14 | 0 | 0/0 | | | +-----------------------------------------+----------------+------+---------+------------------------+ -| +Filter | other:Person | 14 | 14 | 35 | 1/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +AllNodesScan | other | 35 | 35 | 36 | 1/0 | +| +NodeByLabelScan | other:Person | 14 | 14 | 35 | 1/0 | +--------------------+-----------------------------------------+----------------+------+---------+------------------------+ Total database accesses: 165, total allocated memory: 64 @@ -1733,7 +1725,6 @@ Planner COST Runtime SLOTTED Runtime version {neo4j-version-minor} - +--------------------+-----------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | +--------------------+-----------------------------------------+----------------+------+---------+------------------------+ @@ -1757,12 +1748,10 @@ Runtime version {neo4j-version-minor} | | | +-----------------------------------------+----------------+------+---------+------------------------+ | | +Argument | other | 14 | 14 | 0 | 0/0 | | | +-----------------------------------------+----------------+------+---------+------------------------+ -| +Filter | other:Person | 14 | 14 | 35 | 1/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +AllNodesScan | other | 35 | 35 | 36 | 1/0 | +| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 1/0 | +--------------------+-----------------------------------------+----------------+------+---------+------------------------+ -Total database accesses: 194, total allocated memory: 64 +Total database accesses: 142, total allocated memory: 64 ---- ====== @@ -1817,9 +1806,7 @@ Batch size 128 | | | +-----------------------------------------+----------------+------+---------+----------------+ | | | | | +Argument | other | 14 | 14 | 0 | 2168 | 2/0 | 0.435 | Fused in Pipeline 1 | | | +-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | other:Person | 14 | 14 | 35 | | | | | -| | +-----------------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | other | 35 | 35 | 36 | 120 | 1/0 | 0.196 | Fused in Pipeline 0 | +| +NodeByLabelScan | other:Person | 14 | 14 | 35 | | | | Fused in Pipeline 0 | +--------------------+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 148, total allocated memory: 2952 @@ -1878,9 +1865,7 @@ Batch size 128 | | | +-----------------------------------------+----------------+------+---------+----------------+ | | | | | +Argument | other | 14 | 14 | 0 | 2168 | 2/0 | 0.449 | Fused in Pipeline 1 | | | +-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | other:Person | 14 | 14 | 35 | | | | | -| | +-----------------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | other | 35 | 35 | 36 | 120 | 1/0 | 0.195 | Fused in Pipeline 0 | +| +NodyByLabelScan | other:Person | 14 | 14 | 35 | | | | Fused in Pipeline 0 | +------------------------+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 136, total allocated memory: 4208 @@ -1941,9 +1926,7 @@ Runtime version {neo4j-version-minor} | | | +-----------------------------------------+----------------+------+---------+------------------------+ | | +Argument | other | 14 | 14 | 0 | 0/0 | | | +-----------------------------------------+----------------+------+---------+------------------------+ -| +Filter | other:Person | 14 | 14 | 35 | 1/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +AllNodesScan | other | 35 | 35 | 36 | 1/0 | +| +NodeByLabelScan | other:Person | 14 | 14 | 35 | 1/0 | +-----------------------+-----------------------------------------+----------------+------+---------+------------------------+ Total database accesses: 179, total allocated memory: 64 @@ -2004,9 +1987,7 @@ Runtime version {neo4j-version-minor} | | | +-----------------------------------------+----------------+------+---------+------------------------+ | | +Argument | other | 14 | 14 | 0 | 0/0 | | | +-----------------------------------------+----------------+------+---------+------------------------+ -| +Filter | other:Person | 14 | 14 | 35 | 1/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +AllNodesScan | other | 35 | 35 | 36 | 1/0 | +| +NodeByLabelScan | other:Person | 14 | 14 | 35 | 1/0 | +---------------------------+-----------------------------------------+----------------+------+---------+------------------------+ Total database accesses: 208, total allocated memory: 64 @@ -2163,9 +2144,7 @@ Runtime version {neo4j-version-minor} | | | +-----------------------------------+----------------+------+---------+------------------------+ | | +Argument | p | 14 | 14 | 0 | 0/0 | | | +-----------------------------------+----------------+------+---------+------------------------+ -| +Filter | p:Person | 14 | 14 | 35 | 1/0 | -| | +-----------------------------------+----------------+------+---------+------------------------+ -| +AllNodesScan | p | 35 | 35 | 36 | 1/0 | +| +NodeByLabelScan| p:Person | 14 | 14 | 35 | 1/0 | +-----------------+-----------------------------------+----------------+------+---------+------------------------+ Total database accesses: 153, total allocated memory: 64 @@ -2308,7 +2287,7 @@ Batch size 128 +-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +ProduceResults | fof | 0 | 0 | 0 | | | | | | | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | not anon_0 = anon_1 | 0 | 0 | 0 | | | | | +| +Filter | not anon_1 = anon_0 | 0 | 0 | 0 | | | | | | | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | | +Expand(Into) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 0 | 0 | 6 | 896 | | | | | | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | @@ -2361,9 +2340,7 @@ Batch size 128 | | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | | +OptionalExpand(All) | (p)-[works_in:WORKS_IN]->(l) WHERE works_in.duration > $autoint_0 | 14 | 15 | 53 | | | | | | | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 5/0 | 0.785 | Fused in Pipeline 0 | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 5/0 | 1,233 | Fused in Pipeline 0 | +----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 125, total allocated memory: 184 @@ -2413,10 +2390,8 @@ Batch size 128 | | +------------------------------+----------------+------+---------+----------------+ | | | | +Expand(All) | (p)-[works_in:WORKS_IN]->(l) | 15 | 15 | 39 | | | | | | | +------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 6/0 | 1.165 | Fused in Pipeline 0 | -+-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 7/0 | 3,925 | Fused in Pipeline 0 | ++-----------------------+--- --------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 215, total allocated memory: 3440 ---- @@ -2461,9 +2436,7 @@ Batch size 128 | | +-----------------------------------+----------------+------+---------+----------------+ | | | | +VarLengthExpand(All) | (p)-[anon_0:FRIENDS_WITH*..2]-(q) | 40 | 48 | 151 | 128 | | | | | | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 7/0 | 2.258 | Fused in Pipeline 0 | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 10,457 | Fused in Pipeline 0 | +-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 318, total allocated memory: 208 @@ -2507,11 +2480,10 @@ Batch size 128 | | +-----------------------------------+----------------+------+---------+----------------+ | | | | +VarLengthExpand(Into) | (p)-[anon_0:FRIENDS_WITH*..2]-(p) | 3 | 4 | 151 | 128 | | | | | | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 6/0 | 1.231 | Fused in Pipeline 0 | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 0,797 | Fused in Pipeline 0 | +------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + Total database accesses: 222, total allocated memory: 192 ---- @@ -2565,9 +2537,7 @@ Batch size 128 | | +------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +VarLengthExpand(Pruning) | (p)-[:FRIENDS_WITH*3..4]-(q) | 18 | 32 | 204 | 1696 | | | In Pipeline 1 | | | +------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 1/0 | 0.220 | Fused in Pipeline 0 | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 1/0 | 0,100 | In Pipeline 0 | +---------------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 339, total allocated memory: 2288 @@ -2622,9 +2592,7 @@ Batch size 128 | | +-----------------------------+----------------+------+---------+----------------+ | | | | +VarLengthExpand(Pruning,BFS) | (p)-[:FRIENDS_WITH*..4]-(q) | 59 | 68 | 280 | 696 | | | | | | +-----------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +-----------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 6/0 | 18.868 | Fused in Pipeline 0 | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 4/0 | 3,202 | Fused in Pipeline 0 | +-------------------------------+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 487, total allocated memory: 5256 @@ -2846,7 +2814,7 @@ As primitive types and arrays can be used, it can be done very efficiently. .Query [source, cypher, role="noplay"] ---- -MATCH (bob:Person {name: 'Bob'})-[:WORKS_IN]->(loc)<-[:WORKS_IN]-(matt:Person {name: 'Mattis'}) +MATCH (bob:Person {name: 'Bob'})-[:WORKS_IN]->(loc)<-[:WORKS_IN]-(matt:Person {name: 'Mattias'}) RETURN loc.name ---- @@ -2920,21 +2888,17 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, q | 10 | 0 | 0 | | 0/0 | 0.000 | | -| | +---------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +ValueHashJoin | p.age = q.age | 10 | 0 | 0 | 344 | | | In Pipeline 2 | -| |\ +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | q:Person | 14 | 0 | 0 | | | | | -| | | +---------------+----------------+------+---------+----------------+ | | | -| | +AllNodesScan | q | 35 | 0 | 0 | 120 | 0/0 | 0.000 | Fused in Pipeline 1 | -| | +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +---------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 1/0 | 0.293 | Fused in Pipeline 0 | -+-----------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, q | 10 | 0 | 0 | | 0/0 | 0.000 | | +| | +---------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +ValueHashJoin | p.age = q.age| 10 | 0 | 0 | 344 | | | In Pipeline 2 | +| |\ +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +NodeByLabelScan| q:Person | 15 | 0 | 0 | 120 | 0/0 | 0,000 | In Pipeline 1 | +| | +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | p:Person | 15 | 15 | 16 | 120 | 1/0 | 0,211 | In Pipeline 0 | ++-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 71, total allocated memory: 664 ---- @@ -2977,25 +2941,23 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `a.name`, `b.name` | 14 | 16 | 0 | | 0/0 | 0.102 | | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | cache[a.name] AS `a.name`, cache[b.name] AS `b.name` | 14 | 16 | 8 | | 0/0 | 0.055 | | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +NodeRightOuterHashJoin | a | 14 | 16 | 0 | 4232 | | 0.269 | In Pipeline 2 | -| |\ +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | a:Person | 14 | 14 | 35 | | | | | -| | | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +AllNodesScan | a | 35 | 35 | 36 | 120 | 1/0 | 0.155 | Fused in Pipeline 1 | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +CacheProperties | cache[b.name], cache[a.name] | 12 | 12 | 48 | | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | b:Person | 12 | 12 | 56 | | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedAllRelationshipsScan | (a)-[anon_0]->(b) | 28 | 28 | 28 | 120 | 3/0 | 0.353 | Fused in Pipeline 0 | -+-------------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | `a.name`, `b.name` | 14 | 16 | 0 | | 0/0 | 0.102 | | +| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | cache[a.name] AS `a.name`, cache[b.name] AS `b.name` | 14 | 16 | 8 | | 0/0 | 0.055 | | +| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +NodeRightOuterHashJoin | a | 14 | 16 | 0 | 4232 | | 0.269 | In Pipeline 2 | +| |\ +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +NodeByLabelScan | a:Person | 15 | 15 | 16 | 120 | 1/0 | 0,049 | In Pipeline 1 | +| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +CacheProperties | cache[b.name], cache[a.name] | 13 | 13 | 39 | | | | | +| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (b)<-[anon_0]-(a) | 13 | 13 | 55 | | | | | +| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | b:Person | 15 | 15 | 16 | 120 | 5/0 | 1,150 | Fused in Pipeline 0 | ++-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 211, total allocated memory: 4312 ---- @@ -3040,9 +3002,10 @@ Runtime version {neo4j-version-minor} | | +----------------------------------------+----------------+------+---------+------------------------+ | +Projection | other.name AS `other.name` | 4 | 24 | 24 | 1/0 | | | +----------------------------------------+----------------+------+---------+------------------------+ +| +Filter | not anon_2 = anon_4 | 16 | 24 | 0 | 0/0 | +| | +----------------------------------------+----------------+------+---------+------------------------+ | +TriadicSelection | WHERE NOT (me)--(other) | 4 | 24 | 0 | 0/0 | | |\ +----------------------------------------+----------------+------+---------+------------------------+ -| | +Filter | not anon_2 = anon_4 | 16 | 24 | 0 | 0/0 | | | | +----------------------------------------+----------------+------+---------+------------------------+ | | +Expand(All) | (anon_3)-[anon_4:FRIENDS_WITH]-(other) | 16 | 48 | 98 | 48/0 | | | | +----------------------------------------+----------------+------+---------+------------------------+ @@ -3050,9 +3013,7 @@ Runtime version {neo4j-version-minor} | | +----------------------------------------+----------------+------+---------+------------------------+ | +Expand(All) | (me)-[anon_2:FRIENDS_WITH]-(anon_3) | 24 | 24 | 53 | 28/0 | | | +----------------------------------------+----------------+------+---------+------------------------+ -| +Filter | me:Person | 14 | 14 | 35 | 1/0 | -| | +----------------------------------------+----------------+------+---------+------------------------+ -| +AllNodesScan | me | 35 | 35 | 36 | 1/0 | +| +NodeByLabelScan | me:Person | 15 | 15 | 16 | 1/0 | +-------------------+----------------------------------------+----------------+------+---------+------------------------+ Total database accesses: 246, total allocated memory: 64 @@ -3100,12 +3061,13 @@ Batch size 128 | +ProduceResults | `other.name` | 4 | 24 | 0 | | 0/0 | 0.133 | | | | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +Projection | other.name AS `other.name` | 4 | 24 | 48 | | 2/0 | 0.056 | | -| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Filter | not anon_2 = anon_4 | 16 | 24 | 0 | | | | | +| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +TriadicFilter | WHERE NOT (me)--(other) | 4 | 24 | 0 | 4136 | 0/0 | 0.195 | In Pipeline 3 | | | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +Apply | | 16 | 24 | 0 | | 0/0 | | | | |\ +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | not anon_2 = anon_4 | 16 | 24 | 0 | | | | | | | | +----------------------------------------+----------------+------+---------+----------------+ | | | | | +Expand(All) | (anon_3)-[anon_4:FRIENDS_WITH]-(other) | 16 | 48 | 98 | | | | | | | | +----------------------------------------+----------------+------+---------+----------------+ | | | @@ -3115,9 +3077,7 @@ Batch size 128 | | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +Expand(All) | (me)-[anon_2:FRIENDS_WITH]-(anon_3) | 24 | 24 | 39 | | | | | | | +----------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | me:Person | 14 | 14 | 35 | | | | | -| | +----------------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | me | 35 | 35 | 36 | 120 | 2/0 | 0.357 | Fused in Pipeline 0 | +| +NodeByLabelScan| me:Person | 15 | 15 | 16 | 120 | 3/0 | 0,200 | Fused in Pipeline 0 | +-----------------+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 256, total allocated memory: 7376 @@ -3165,12 +3125,13 @@ Batch size 128 | +ProduceResults | `other.name` | 4 | 24 | 0 | | 0/0 | 0.189 | | | | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +Projection | other.name AS `other.name` | 4 | 24 | 48 | | 2/0 | 0.381 | | -| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Filter | not anon_2 = anon_4 | 16 | 24 | 0 | | | | | +| | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +TriadicFilter | WHERE NOT (me)--(other) | 4 | 24 | 0 | 4136 | 0/0 | 0.685 | In Pipeline 3 | | | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +Apply | | 16 | 24 | 0 | | 0/0 | | | | |\ +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | not anon_2 = anon_4 | 16 | 24 | 0 | | | | | | | | +----------------------------------------+----------------+------+---------+----------------+ | | | | | +Expand(All) | (anon_3)-[anon_4:FRIENDS_WITH]-(other) | 16 | 48 | 98 | | | | | | | | +----------------------------------------+----------------+------+---------+----------------+ | | | @@ -3180,9 +3141,7 @@ Batch size 128 | | +----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | +Expand(All) | (me)-[anon_2:FRIENDS_WITH]-(anon_3) | 24 | 24 | 39 | | | | | | | +----------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | me:Person | 14 | 14 | 35 | | | | | -| | +----------------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | me | 35 | 35 | 36 | 120 | 2/0 | 0.361 | Fused in Pipeline 0 | +| +NodeByLabelScan| me:Person | 15 | 15 | 16 | 120 | 3/0 | 0,481 | Fused in Pipeline 0 | +-----------------+----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 256, total allocated memory: 7376 @@ -3222,21 +3181,17 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, t | 140 | 140 | 0 | | 2/0 | 1.917 | | -| | +----------+----------------+------+---------+----------------+------------------------+-----------+ | -| +CartesianProduct | | 140 | 140 | 0 | 1736 | | 1.209 | In Pipeline 2 | -| |\ +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | t:Team | 10 | 10 | 35 | | | | | -| | | +----------+----------------+------+---------+----------------+ | | | -| | +AllNodesScan | t | 35 | 35 | 36 | 136 | 0/0 | 0.184 | Fused in Pipeline 1 | -| | +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 1/0 | 0.361 | Fused in Pipeline 0 | -+-------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, t | 140 | 140 | 0 | | 2/0 | 1.917 | | +| | +----------+----------------+------+---------+----------------+------------------------+-----------+ | +| +CartesianProduct | | 140 | 140 | 0 | 1736 | | 1.209 | In Pipeline 2 | +| |\ +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +NodeByLabelScan | t:Team | 10 | 10 | 11 | 136 | 1/0 | 1,145 | In Pipeline 1 | +| | +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | p:Person | 15 | 15 | 16 | 120 | 1/0 | 0,409 | In Pipeline 0 | ++--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 142, total allocated memory: 1816 ---- @@ -3408,9 +3363,7 @@ Batch size 128 | | +------------------------------------------------------+----------------+------+---------+----------------+ | | | | +CacheProperties | cache[l.name] | 10 | 10 | 10 | | | | | | | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | l:Location | 10 | 10 | 35 | | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | l | 35 | 35 | 36 | 120 | 4/0 | 1.361 | Fused in Pipeline 0 | +| +NodeByLabelScan | l:Location | 10 | 10 | 11 | 120 | 5/0 | 3,077 | Fused in Pipeline 0 | +-------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 157, total allocated memory: 2664 @@ -3585,28 +3538,25 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 14 | 6 | 0 | | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +Distinct | l | 14 | 6 | 0 | 224 | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 15 | 15 | 30 | | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (l)<-[anon_0:WORKS_IN]-(p) | 15 | 15 | 16 | | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +Filter | l:Location | 10 | 10 | 35 | | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | l | 35 | 35 | 36 | 120 | 5/0 | 0.333 | Fused in Pipeline 0 | -+-----------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | l | 14 | 6 | 0 | | | | | +| | +----------------------------+----------------+------+---------+----------------+ | | | +| +Distinct | l | 14 | 6 | 0 | 224 | | | | +| | +----------------------------+----------------+------+---------+----------------+ | | | +| +Filter | p:Person | 15 | 15 | 30 | | | | | +| | +----------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (l)<-[anon_0:WORKS_IN]-(p) | 15 | 15 | 16 | | | | | +| | +----------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | l:Location | 10 | 10 | 11 | 120 | 4/0 | 0,744 | Fused in Pipeline 0 | ++------------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 117, total allocated memory: 304 ---- ====== - [[query-plan-ordered-distinct]] == Ordered Distinct // OrderedDistinct @@ -3734,9 +3684,7 @@ Batch size 128 | | +----------+----------------+------+---------+----------------+ | | | | +Limit | 3 | 3 | 3 | 0 | 32 | | | | | | +----------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 3 | 3 | 3 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 8 | 4 | 5 | 120 | 3/0 | 0.356 | Fused in Pipeline 0 | +| +NodeByLabelScan| p:Person | 3 | 4 | 5 | 120 | 3/0 | 0,540 | Fused in Pipeline 0 | +-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 8, total allocated memory: 184 @@ -3775,21 +3723,19 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | p | 13 | 13 | 0 | | 2/0 | 0.165 | | | -| | +----------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Skip | $autoint_0 | 13 | 13 | 0 | 32 | 0/0 | 0.043 | | | -| | +----------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Sort | `p.id` ASC | 14 | 14 | 0 | 400 | 0/0 | 0.155 | p.id ASC | In Pipeline 1 | -| | +----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Projection | p.id AS `p.id` | 14 | 14 | 0 | | | | | | -| | +----------------+----------------+------+---------+----------------+ | +------------+ | -| +Filter | p:Person | 14 | 14 | 35 | | | | | | -| | +----------------+----------------+------+---------+----------------+ | +------------+ | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 2/0 | 0.207 | | Fused in Pipeline 0 | -+-----------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | p | 13 | 13 | 0 | | 2/0 | 0.165 | | | +| | +----------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Skip | $autoint_0 | 13 | 13 | 0 | 32 | 0/0 | 0.043 | | | +| | +----------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Sort | `p.id` ASC | 14 | 14 | 0 | 400 | 0/0 | 0.155 | p.id ASC | In Pipeline 1 | +| | +----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +Projection | p.id AS `p.id` | 14 | 14 | 0 | | | | | | +| | +----------------+----------------+------+---------+----------------+ | +------------+ | +| +NodeByLabelScan | p:Person | 18 | 18 | 19 | 120 | 3/0 | 0,157 | | Fused in Pipeline 0 | ++------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ Total database accesses: 71, total allocated memory: 512 ---- @@ -3827,19 +3773,17 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | p | 14 | 14 | 0 | | 2/0 | 0.178 | | | -| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Sort | `p.name` ASC | 14 | 14 | 0 | 1192 | 0/0 | 0.107 | p.name ASC | In Pipeline 1 | -| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Projection | p.name AS `p.name` | 14 | 14 | 14 | | | | | | -| | +--------------------+----------------+------+---------+----------------+ | +------------+ | -| +Filter | p:Person | 14 | 14 | 35 | | | | | | -| | +--------------------+----------------+------+---------+----------------+ | +------------+ | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 2/0 | 0.238 | | Fused in Pipeline 0 | -+-----------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResult s | p | 14 | 14 | 0 | | 2/0 | 0.178 | | | +| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Sort | `p.name` ASC | 14 | 14 | 0 | 1192 | 0/0 | 0.107 | p.name ASC | In Pipeline 1 | +| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +Projection | p.name AS `p.name` | 14 | 14 | 14 | | | | | | +| | +--------------------+----------------+------+---------+----------------+ | +------------+ | +| +NodeByLabelScan |p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,221 | | Fused in Pipeline 0 | ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ Total database accesses: 85, total allocated memory: 1272 ---- @@ -3928,19 +3872,17 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | p | 2 | 2 | 0 | | 2/0 | 0.093 | | | -| | +----------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Top | `p.name` ASC LIMIT 2 | 2 | 2 | 0 | 1184 | 0/0 | 0.295 | p.name ASC | In Pipeline 1 | -| | +----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Projection | p.name AS `p.name` | 14 | 14 | 14 | | | | | | -| | +----------------------+----------------+------+---------+----------------+ | +------------+ | -| +Filter | p:Person | 14 | 14 | 35 | | | | | | -| | +----------------------+----------------+------+---------+----------------+ | +------------+ | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 2/0 | 0.355 | | Fused in Pipeline 0 | -+-----------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | p | 2 | 2 | 0 | | 2/0 | 0.093 | | | +| | +----------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Top | `p.name` ASC LIMIT 2 | 2 | 2 | 0 | 1184 | 0/0 | 0.295 | p.name ASC | In Pipeline 1 | +| | +----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +Projection | p.name AS `p.name` | 14 | 14 | 14 | | | | | | +| | +----------------------+----------------+------+---------+----------------+ | +------------+ | +| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,166 | | Fused in Pipeline 0 | ++------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ Total database accesses: 85, total allocated memory: 1264 ---- @@ -4030,29 +3972,27 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `p.name` | 20 | 11 | 0 | | | | | -| | +--------------------+----------------+------+---------+----------------+ | | | -| +Union | | 20 | 11 | 0 | 776 | 0/0 | 0.171 | Fused in Pipeline 2 | -| |\ +--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Projection | `p.name` | 10 | 1 | 0 | | | | | -| | | +--------------------+----------------+------+---------+----------------+ | | | -| | +Projection | p.name AS `p.name` | 10 | 1 | 1 | | | | | -| | | +--------------------+----------------+------+---------+----------------+ | | | -| | +Filter | p:Country | 10 | 1 | 35 | | | | | -| | | +--------------------+----------------+------+---------+----------------+ | | | -| | +AllNodesScan | p | 35 | 35 | 36 | 120 | 0/0 | 0.127 | Fused in Pipeline 1 | -| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Projection | `p.name` | 10 | 10 | 0 | | | | | -| | +--------------------+----------------+------+---------+----------------+ | | | -| +Projection | p.name AS `p.name` | 10 | 10 | 10 | | | | | -| | +--------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Location | 10 | 10 | 35 | | | | | -| | +--------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 2/0 | 0.191 | Fused in Pipeline 0 | -+-----------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | `p.name` | 20 | 11 | 0 | | | | | +| | +--------------------+----------------+------+---------+----------------+ | | | +| +Union | | 20 | 11 | 0 | 776 | 0/0 | 0.171 | Fused in Pipeline 2 | +| |\ +--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Projection | `p.name` | 10 | 1 | 0 | | | | | +| | | +--------------------+----------------+------+---------+----------------+ | | | +| | +Projection | p.name AS `p.name` | 10 | 1 | 1 | | | | | +| | | +--------------------+----------------+------+---------+----------------+ | | | +| | +Filter | p:Country | 10 | 1 | 35 | | | | | +| | | +--------------------+----------------+------+---------+----------------+ | | | +| | +AllNodesScan | p | 35 | 35 | 36 | 120 | 0/0 | 0.127 | Fused in Pipeline 1 | +| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Projection | `p.name` | 10 | 10 | 0 | | | | | +| | +--------------------+----------------+------+---------+----------------+ | | | +| +Projection | p.name AS `p.name` | 10 | 10 | 10 | | | | | +| | +--------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Location | 10 | 10 | 11 | 120 | 3/0 | 0,171 | Fused in Pipeline 0 | ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 153, total allocated memory: 984 ---- @@ -4143,9 +4083,7 @@ Batch size 128 | | +---------------+----------------+------+---------+----------------+ | | | | +SetProperty | p.seen = true | 14 | 14 | 28 | | | | | | | +---------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +---------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 3/0 | 0.727 | Fused in Pipeline 0 | +| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 5/0 | 41,656 | Fused in Pipeline 0 | +------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 99, total allocated memory: 200 @@ -4374,21 +4312,21 @@ Runtime version {neo4j-version-minor} +-----------------+--------------------------------------+----------------+------+---------+------------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+-----------------+--------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | | 1 | 0 | 0 | 0/0 | -| | +--------------------------------------+----------------+------+---------+------------------------+ -| +EmptyResult | | 1 | 0 | 0 | 0/0 | -| | +--------------------------------------+----------------+------+---------+------------------------+ -| +Foreach | value IN [1, 2, 3] | 1 | 1 | 0 | 0/0 | -| |\ +--------------------------------------+----------------+------+---------+------------------------+ -| | +Merge | CREATE (anon_0:Person {age: value}) | 1 | 3 | 9 | 0/0 | -| | | +--------------------------------------+----------------+------+---------+------------------------+ -| | +Filter | anon_0:Person AND anon_0.age = value | 1 | 0 | 184 | 2/0 | -| | | +--------------------------------------+----------------+------+---------+------------------------+ -| | +AllNodesScan | anon_0 | 35 | 108 | 111 | 3/0 | -| | +--------------------------------------+----------------+------+---------+------------------------+ -| +EmptyRow | | 1 | 1 | 0 | 0/0 | -+-----------------+--------------------------------------+----------------+------+---------+------------------------+ ++--------------------+--------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | | 1 | 0 | 0 | 0/0 | +| | +--------------------------------------+----------------+------+---------+------------------------+ +| +EmptyResult | | 1 | 0 | 0 | 0/0 | +| | +--------------------------------------+----------------+------+---------+------------------------+ +| +Foreach | value IN [1, 2, 3] | 1 | 1 | 0 | 0/0 | +| |\ +--------------------------------------+----------------+------+---------+------------------------+ +| | +Merge | CREATE (anon_0:Person {age: value}) | 1 | 3 | 9 | 0/0 | +| | | +--------------------------------------+----------------+------+---------+------------------------+ +| | +Filter | anon_0.age = value | 1 | 0 | 184 | 2/0 | +| | | +--------------------------------------+----------------+------+---------+------------------------+ +| | +NodeByLabelScan | anon_0:Person | 35 | 108 | 111 | 3/0 | +| | +--------------------------------------+----------------+------+---------+------------------------+ +| +EmptyRow | | 1 | 1 | 0 | 0/0 | ++--------------------+--------------------------------------+----------------+------+---------+------------------------+ Total database accesses: 304, total allocated memory: 64 ---- @@ -4486,9 +4424,7 @@ Batch size 128 | | +-------------------------------------------+----------------+------+---------+----------------+ | | | | +CacheProperties | cache[l.name] | 10 | 10 | 10 | | | | | | | +-------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | l:Location | 10 | 10 | 35 | | | | | -| | +-------------------------------------------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | l | 35 | 35 | 36 | 120 | 4/0 | 0.439 | Fused in Pipeline 0 | +| +NodeByLabelScan | l:Location | 10 | 10 | 35 | 120 | 5/0 | 0,422 | Fused in Pipeline 0 | +------------------+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 157, total allocated memory: 200 @@ -4624,19 +4560,17 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | | 14 | 0 | 0 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +EmptyResult | | 14 | 0 | 0 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +DetachDelete | p | 14 | 14 | 41 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 14 | 14 | 35 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | p | 35 | 35 | 36 | 120 | 19/0 | 1.248 | Fused in Pipeline 0 | -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | | 14 | 0 | 0 | | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +EmptyResult | | 14 | 0 | 0 | | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +DetachDelete | p | 14 | 14 | 41 | | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 21/0 | 12,439 | Fused in Pipeline 0 | ++------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 112, total allocated memory: 200 ---- From ae033d277b6dc053153e80f7cce7c178e0c8558d Mon Sep 17 00:00:00 2001 From: Lidia Zuin <102308961+lidiazuin@users.noreply.github.com> Date: Wed, 18 Jan 2023 14:32:10 +0100 Subject: [PATCH 059/383] Fixing svg images that were not showing (#315) (#316) This is a bug only happening in 5.x. cherry picked from https://github.com/neo4j/docs-cypher/pull/315 --- .../privileges_grant_and_deny_syntax.svg | 4 +- ...nt_and_deny_syntax_database_privileges.svg | 100 +++++++++++++++++- ..._grant_and_deny_syntax_dbms_privileges.svg | 10 +- modules/ROOT/images/privileges_hierarchy.svg | 4 +- .../images/privileges_hierarchy_database.svg | 10 +- .../ROOT/images/privileges_hierarchy_dbms.png | Bin 133936 -> 150556 bytes .../ROOT/images/privileges_hierarchy_dbms.svg | 15 ++- 7 files changed, 135 insertions(+), 8 deletions(-) diff --git a/modules/ROOT/images/privileges_grant_and_deny_syntax.svg b/modules/ROOT/images/privileges_grant_and_deny_syntax.svg index a53dfca4e..651f74540 100644 --- a/modules/ROOT/images/privileges_grant_and_deny_syntax.svg +++ b/modules/ROOT/images/privileges_grant_and_deny_syntax.svg @@ -2,8 +2,8 @@ - + - + diff --git a/modules/ROOT/images/privileges_grant_and_deny_syntax_database_privileges.svg b/modules/ROOT/images/privileges_grant_and_deny_syntax_database_privileges.svg index 536399193..4e0babc35 100644 --- a/modules/ROOT/images/privileges_grant_and_deny_syntax_database_privileges.svg +++ b/modules/ROOT/images/privileges_grant_and_deny_syntax_database_privileges.svg @@ -1 +1,99 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.svg b/modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.svg index 5d4e4ed5f..63e06b2a5 100644 --- a/modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.svg +++ b/modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + diff --git a/modules/ROOT/images/privileges_hierarchy.svg b/modules/ROOT/images/privileges_hierarchy.svg index e548a5230..ce7ef40b1 100644 --- a/modules/ROOT/images/privileges_hierarchy.svg +++ b/modules/ROOT/images/privileges_hierarchy.svg @@ -2,8 +2,8 @@ - + - + diff --git a/modules/ROOT/images/privileges_hierarchy_database.svg b/modules/ROOT/images/privileges_hierarchy_database.svg index 84a029432..0ccfd067d 100644 --- a/modules/ROOT/images/privileges_hierarchy_database.svg +++ b/modules/ROOT/images/privileges_hierarchy_database.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + diff --git a/modules/ROOT/images/privileges_hierarchy_dbms.png b/modules/ROOT/images/privileges_hierarchy_dbms.png index f7969030e19f706551a22cfb7ea3a5c4a6136c4b..0f7d2a7a50eddfceb2dc14385e2413016d9d09ce 100644 GIT binary patch literal 150556 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 literal 133936 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 \ No newline at end of file + + + + + + + + + + + + + + From dc8538a22595c94f4796571a12d9b611a12e6e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 19 Jan 2023 13:14:48 +0100 Subject: [PATCH 060/383] Fix result in scalar and predicate functions (#317) --- modules/ROOT/pages/functions/predicate.adoc | 2 +- modules/ROOT/pages/functions/scalar.adoc | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index ff8f713d6..2e1034125 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -102,7 +102,7 @@ All nodes in the returned paths will have a property `age` with a value larger t |=== | +p+ -| +(0)-[KNOWS,1]->(2)-[KNOWS,3]->(3)+ +| +[{"name":"Alice","eyes":"brown","age":38},{},{"name":"Charlie","eyes":"green","age":53},{"name":"Charlie","eyes":"green","age":53},{},{"name":"Daniel","eyes":"brown","age":54}]+ 1+d|Rows: 1 |=== diff --git a/modules/ROOT/pages/functions/scalar.adoc b/modules/ROOT/pages/functions/scalar.adoc index 1e26a0db2..7d526424c 100644 --- a/modules/ROOT/pages/functions/scalar.adoc +++ b/modules/ROOT/pages/functions/scalar.adoc @@ -270,8 +270,8 @@ RETURN endNode(r) [role="queryresult",options="header,footer",cols="1* "Berlin", name -> "Stefan"}+ +| +{"city":"Berlin","name":"Stefan"}+ 1+d|Rows: 1 + Nodes created: 1 + Properties set: 2 + @@ -928,8 +928,8 @@ RETURN startNode(r) |=== | +startNode(r)+ -| +Node[0]{name:"Alice",age:38,eyes:"brown"}+ -| +Node[0]{name:"Alice",age:38,eyes:"brown"}+ +| +{name:"Alice",age:38,eyes:"brown"}+ +| +{name:"Alice",age:38,eyes:"brown"}+ 1+d|Rows: 2 |=== From 86d0de02b484a8f07f53915b5e1e3c6371a18c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 19 Jan 2023 14:25:01 +0100 Subject: [PATCH 061/383] Update MATCH page (#307) --- .../images/MATCH_allshortestpaths_example.svg | 1 + .../ROOT/images/MATCH_named_path_example.svg | 1 + ...TCH_properties_on_variable_length_path.svg | 1 + .../images/MATCH_shortestpath_example.svg | 1 + ...H_shortestpath_with_predicates_example.svg | 1 + modules/ROOT/images/graph_match_clause.svg | 124 +---- .../images/graph_match_clause_backtick.svg | 131 +----- .../graph_match_clause_variable_length.svg | 172 +------ modules/ROOT/pages/clauses/match.adoc | 441 ++++++------------ 9 files changed, 143 insertions(+), 730 deletions(-) create mode 100644 modules/ROOT/images/MATCH_allshortestpaths_example.svg create mode 100644 modules/ROOT/images/MATCH_named_path_example.svg create mode 100644 modules/ROOT/images/MATCH_properties_on_variable_length_path.svg create mode 100644 modules/ROOT/images/MATCH_shortestpath_example.svg create mode 100644 modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg diff --git a/modules/ROOT/images/MATCH_allshortestpaths_example.svg b/modules/ROOT/images/MATCH_allshortestpaths_example.svg new file mode 100644 index 000000000..88b55ddb0 --- /dev/null +++ b/modules/ROOT/images/MATCH_allshortestpaths_example.svg @@ -0,0 +1 @@ +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)ACTED_INACTED_INACTED_INACTED_IN Martin Sheen Michael Douglas Wall Street The American Pre… \ No newline at end of file diff --git a/modules/ROOT/images/MATCH_named_path_example.svg b/modules/ROOT/images/MATCH_named_path_example.svg new file mode 100644 index 000000000..fe3c994cc --- /dev/null +++ b/modules/ROOT/images/MATCH_named_path_example.svg @@ -0,0 +1 @@ +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)ACTED_INACTED_IN Michael Douglas Wall Street The American Pre… \ No newline at end of file diff --git a/modules/ROOT/images/MATCH_properties_on_variable_length_path.svg b/modules/ROOT/images/MATCH_properties_on_variable_length_path.svg new file mode 100644 index 000000000..d1cbc9593 --- /dev/null +++ b/modules/ROOT/images/MATCH_properties_on_variable_length_path.svg @@ -0,0 +1 @@ +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)ACTED_INACTED_IN Charlie Sheen Martin Sheen No Code of Con… \ No newline at end of file diff --git a/modules/ROOT/images/MATCH_shortestpath_example.svg b/modules/ROOT/images/MATCH_shortestpath_example.svg new file mode 100644 index 000000000..bfba3596d --- /dev/null +++ b/modules/ROOT/images/MATCH_shortestpath_example.svg @@ -0,0 +1 @@ +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)ACTED_INDIRECTED Martin Sheen Oliver Stone Wall Street \ No newline at end of file diff --git a/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg b/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg new file mode 100644 index 000000000..e3e0b9848 --- /dev/null +++ b/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg @@ -0,0 +1 @@ +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)ACTED_INACTED_IN Charlie Sheen Martin Sheen Wall Street \ No newline at end of file diff --git a/modules/ROOT/images/graph_match_clause.svg b/modules/ROOT/images/graph_match_clause.svg index b8fea68e9..9426d8918 100644 --- a/modules/ROOT/images/graph_match_clause.svg +++ b/modules/ROOT/images/graph_match_clause.svg @@ -1,123 +1 @@ - - - - - - -L - - - -N0 - -Person - -name = 'Charlie Sheen' - - - -N5 - -Movie - -title = 'Wall Street' - - - -N0->N5 - - -ACTED_IN -role = 'Bud Fox' - - - -N1 - -Person - -name = 'Martin Sheen' - - - -N1->N5 - - -ACTED_IN -role = 'Carl Fox' - - - -N6 - -Movie - -title = 'The American President' - - - -N1->N6 - - -ACTED_IN -role = 'A.J. MacInerney' - - - -N2 - -Person - -name = 'Michael Douglas' - - - -N2->N5 - - -ACTED_IN -role = 'Gordon Gekko' - - - -N2->N6 - - -ACTED_IN -role = 'President Andrew Shepherd' - - - -N3 - -Person - -name = 'Oliver Stone' - - - -N3->N5 - - -DIRECTED - - - -N4 - -Person - -name = 'Rob Reiner' - - - -N4->N6 - - -DIRECTED - - - +DIRECTEDACTED_INrole:'Gordon Gekko'ACTED_INrole:'Carl Fox'ACTED_INrole:'President Andrew Shepherd'ACTED_INrole:'A.J. MacInerney'ACTED_INrole:'Bud Fox'DIRECTEDFATHER_OFPersonname:'Oliver Stone'Movietitle:'Wall Street'Personname:'Michael Douglas'Personname:'Martin Sheen'Movietitle:'The American President'Personname:'Charlie Sheen'Personname:'Rob Reiner' \ No newline at end of file diff --git a/modules/ROOT/images/graph_match_clause_backtick.svg b/modules/ROOT/images/graph_match_clause_backtick.svg index a224c5d9c..3c379d15c 100644 --- a/modules/ROOT/images/graph_match_clause_backtick.svg +++ b/modules/ROOT/images/graph_match_clause_backtick.svg @@ -1,130 +1 @@ - - - - - - -L - - - -N0 - -Person - -name = 'Charlie Sheen' - - - -N5 - -Movie - -title = 'Wall Street' - - - -N0->N5 - - -ACTED_IN -role = 'Bud Fox' - - - -N1 - -Person - -name = 'Martin Sheen' - - - -N1->N5 - - -ACTED_IN -role = 'Carl Fox' - - - -N6 - -Movie - -title = 'The American President' - - - -N1->N6 - - -ACTED_IN -role = 'A.J. MacInerney' - - - -N2 - -Person - -name = 'Michael Douglas' - - - -N2->N5 - - -ACTED_IN -role = 'Gordon Gekko' - - - -N2->N6 - - -ACTED_IN -role = 'President Andrew Shepherd' - - - -N3 - -Person - -name = 'Oliver Stone' - - - -N3->N5 - - -DIRECTED - - - -N4 - -Person - -name = 'Rob Reiner' - - - -N4->N0 - - -TYPE INCLUDING A SPACE - - - -N4->N6 - - -DIRECTED - - - +DIRECTEDACTED_INrole:'Gordon Gekko'ACTED_INrole:'Carl Fox'ACTED_INrole:'President Andrew Shepherd'ACTED_INrole:'A.J. MacInerney'ACTED_INrole:'Bud Fox'DIRECTEDFATHER_OFOLD FRIENDSPersonname:'Oliver Stone'Movietitle:'Wall Street'Personname:'Michael Douglas'Personname:'Martin Sheen'Movietitle:'The American President'Personname:'Charlie Sheen'Personname:'Rob Reiner' \ No newline at end of file diff --git a/modules/ROOT/images/graph_match_clause_variable_length.svg b/modules/ROOT/images/graph_match_clause_variable_length.svg index 3fcf069f4..8c79b6f6d 100644 --- a/modules/ROOT/images/graph_match_clause_variable_length.svg +++ b/modules/ROOT/images/graph_match_clause_variable_length.svg @@ -1,171 +1 @@ - - - - - - -L - - - -N0 - -Person - -name = 'Charlie Sheen' - - - -N7 - -UNBLOCKED - - - - - -N0->N7 - - -X -blocked = false - - - -N8 - -BLOCKED - - - - - -N0->N8 - - -X -blocked = true - - - -N5 - -Movie - -title = 'Wall Street' - - - -N0->N5 - - -ACTED_IN -role = 'Bud Fox' - - - -N1 - -Person - -name = 'Martin Sheen' - - - -N1->N7 - - -X -blocked = false - - - -N1->N8 - - -X -blocked = false - - - -N1->N5 - - -ACTED_IN -role = 'Carl Fox' - - - -N6 - -Movie - -title = 'The American President' - - - -N1->N6 - - -ACTED_IN -role = 'A.J. MacInerney' - - - -N2 - -Person - -name = 'Michael Douglas' - - - -N2->N5 - - -ACTED_IN -role = 'Gordon Gekko' - - - -N2->N6 - - -ACTED_IN -role = 'President Andrew Shepherd' - - - -N3 - -Person - -name = 'Oliver Stone' - - - -N3->N5 - - -DIRECTED - - - -N4 - -Person - -name = 'Rob Reiner' - - - -N4->N6 - - -DIRECTED - - - +DIRECTEDACTED_INrole:'Gordon Gekko'ACTED_INrole:'Carl Fox'ACTED_INrole:'President Andrew Shepherd'ACTED_INrole:'A.J. MacInerney'ACTED_INrole:'Bud Fox'DIRECTEDACTED_INrole:'Bud'lead:trueACTED_INrole:'New Warden'lead:falseACTED_INrole:'Bill Peterson'lead:trueACTED_INrole:'Jake Peterson'lead:trueFATHER_OFOLD FRIENDSPersonname:'Oliver Stone'Movietitle:'Wall Street'Personname:'Michael Douglas'Personname:'Martin Sheen'Movietitle:'The American President'Personname:'Charlie Sheen'Personname:'Rob Reiner'Movietitle:'Free Money'Movietitle:'No Code of Conduct' \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 3bca6f825..c5d01e365 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -35,10 +35,10 @@ The `MATCH` clause is used to search for the pattern described in it. ** xref::clauses/match.adoc#single-shortest-path[Single shortest path] ** xref::clauses/match.adoc#single-shortest-path-with-predicates[Single shortest path with predicates] ** xref::clauses/match.adoc#all-shortest-paths[All shortest paths] -* xref::clauses/match.adoc#get-node-rel-by-id[Get node or relationship by ID] - ** xref::clauses/match.adoc#match-node-by-id[Node by ID] - ** xref::clauses/match.adoc#match-rel-by-id[Relationship by ID] - ** xref::clauses/match.adoc#match-multiple-nodes-by-id[Multiple nodes by ID] +* xref::clauses/match.adoc#get-node-rel-by-id[Get node or relationship by elementId] + ** xref::clauses/match.adoc#match-node-by-id[Node by elementId] + ** xref::clauses/match.adoc#match-rel-by-id[Relationship by elementId] + ** xref::clauses/match.adoc#match-multiple-nodes-by-id[Multiple nodes by elementId] [[match-introduction]] @@ -66,14 +66,18 @@ Read more about indexes in xref::indexes-for-search-performance.adoc[], and more [TIP] ==== -To understand more about the patterns used in the `MATCH` clause, read xref::syntax/patterns.adoc[Patterns] +To understand more about the patterns used in the `MATCH` clause, read the chapter on xref::syntax/patterns.adoc[Patterns]. ==== The following graph is used for the examples below: -image:graph_match_clause.svg[] +image::graph_match_clause.svg[width="600",role="middle"] -//// + +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- CREATE (charlie:Person {name: 'Charlie Sheen'}), (martin:Person {name: 'Martin Sheen'}), @@ -88,8 +92,10 @@ CREATE (thePresident:Movie {title: 'The American President'}), (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// + (rob)-[:DIRECTED]->(thePresident), + (martin)-[:FATHER_OF]->(charlie) +---- + [[basic-node-finding]] == Basic node finding @@ -97,7 +103,7 @@ CREATE [[get-all-nodes]] === Get all nodes -By just specifying a pattern with a single node and no labels, all nodes in the graph will be returned. +By specifying a pattern with a single node and no labels, all nodes in the graph will be returned. .Query [source, cypher, indent=0] @@ -112,13 +118,13 @@ Returns all the nodes in the database. [role="queryresult",options="header,footer",cols="1*(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// - .Query [source, cypher] ---- @@ -244,7 +232,8 @@ RETURN n.name AS name, n.title AS title [[outgoing-relationships]] === Outgoing relationships -When the direction of a relationship is of interest, it is shown by using `+-->+` or `+<--+`, like this: +When the direction of a relationship is of interest, it is shown by using `+-->+` or `+<--+`. +For example: .Query [source, cypher, indent=0] @@ -253,7 +242,7 @@ MATCH (:Person {name: 'Oliver Stone'})-->(movie) RETURN movie.title ---- -Returns any nodes connected with the `Person` *'Oliver'* by an outgoing relationship. +Returns any nodes connected by an outgoing relationship to the `Person` node with the `name` property set to `Oliver Stone`. .Result [role="queryresult",options="header,footer",cols="1*(movie) RETURN type(r) ---- -Returns the type of each outgoing relationship from *'Oliver'*. +Returns the type of each outgoing relationship from `Oliver Stone`. .Result [role="queryresult",options="header,footer",cols="1*(charlie) +CREATE (rob)-[:`OLD FRIENDS`]->(martin) ---- -Which leads to the following graph: +This leads to the following graph: -image:graph_match_clause_backtick.svg[] - -//// -CREATE - (charlie:Person {name: 'Charlie Sheen'}), - (martin:Person {name: 'Martin Sheen'}), - (michael:Person {name: 'Michael Douglas'}), - (oliver:Person {name: 'Oliver Stone'}), - (rob:Person {name: 'Rob Reiner'}), - (wallStreet:Movie {title: 'Wall Street'}), - (charlie)-[:ACTED_IN {role: 'Bud Fox'}]->(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -MATCH - (charlie:Person {name: 'Charlie Sheen'}), - (rob:Person {name: 'Rob Reiner'}) -CREATE (rob)-[:`TYPE INCLUDING A SPACE`]->(charlie) -//// +image::graph_match_clause_backtick.svg[width="600", role="middle"] .Query [source, cypher, indent=0] ---- -MATCH (n {name: 'Rob Reiner'})-[r:`TYPE INCLUDING A SPACE`]->() +MATCH (n {name: 'Rob Reiner'})-[r:`OLD FRIENDS`]->() RETURN type(r) ---- -Returns a relationship type with spaces in it. - .Result [role="queryresult",options="header,footer",cols="1*(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// +Relationships can be expressed by using multiple statements in the form of `()--()`, or they can be strung together. +For example: .Query [source, cypher, indent=0] @@ -462,7 +414,7 @@ MATCH (charlie {name: 'Charlie Sheen'})-[:ACTED_IN]->(movie)<-[:DIRECTED]-(direc RETURN movie.title, director.name ---- -Returns the movie *'Charlie Sheen'* acted in and its director. +Returns the movie in which `Charlie Sheen` acted and its director. .Result [role="queryresult",options="header,footer",cols="2*node+` hops away can be foun `+-[:TYPE*minHops..maxHops]->+`. `minHops` and `maxHops` are optional and default to 1 and infinity respectively. When no bounds are given the dots may be omitted. -The dots may also be omitted when setting only one bound and this implies a fixed length pattern. +The dots may also be omitted when setting only one bound as this implies a fixed length pattern. [NOTE] ==== @@ -488,27 +439,6 @@ Variable length relationships can be planned with an optimisation under certain ==== -.+Variable length relationships+ -====== - -//// -CREATE - (charlie:Person {name: 'Charlie Sheen'}), - (martin:Person {name: 'Martin Sheen'}), - (michael:Person {name: 'Michael Douglas'}), - (oliver:Person {name: 'Oliver Stone'}), - (rob:Person {name: 'Rob Reiner'}), - (wallStreet:Movie {title: 'Wall Street'}), - (charlie)-[:ACTED_IN {role: 'Bud Fox'}]->(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// - .Query [source, cypher, indent=0] ---- @@ -519,7 +449,7 @@ RETURN movie.title Returns all movies related to `Charlie Sheen` by 1 to 3 hops: * `Wall Street` is found through the direct connection, whereas the other two results are found via `Michael Douglas` and `Martin Sheen` respectively. -* As one can see from this example, variable length relationships do not impose any requirements on the intermediate nodes. +* As this example demonstrates, variable length relationships do not impose any requirements on the intermediate nodes. .Result [role="queryresult",options="header,footer",cols="1*(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// +Variable length relationships can be combined with multiple relationship types. +In this case, `*minHops..maxHops` applies to all relationship types as well as any combination of them. .Query [source, cypher, indent=0] @@ -564,7 +473,7 @@ MATCH (charlie {name: 'Charlie Sheen'})-[:ACTED_IN|DIRECTED*2]-(person:Person) RETURN person.name ---- -Returns all people related to *'Charlie Sheen'* by 2 hops with any combination of the relationship types `ACTED_IN` and `DIRECTED`. +Returns all people related to `Charlie Sheen` by 2 hops with any combination of the relationship types `ACTED_IN` and `DIRECTED`. .Result [role="queryresult",options="header,footer",cols="1*(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// .Query [source, cypher, indent=0] @@ -613,8 +505,8 @@ Returns a list of relationships. [role="queryresult",options="header,footer",cols="1*(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// +The following query adds two new paths between `Charlie Sheen` and his father `Martin Sheen`, where a `lead` property is added to the `ACTED_IN` relationships connecting them to the `Movie` nodes `No Code of Conduct` and `Free Money`. +The query makes evident that both actors had a leading role in the movie `No Code of Conduct`, but only `Martin Sheen` had a leading role in the movie `Free Money`. .Query [source, cypher, indent=0] @@ -651,52 +525,32 @@ CREATE MATCH (charlie:Person {name: 'Charlie Sheen'}), (martin:Person {name: 'Martin Sheen'}) -CREATE (charlie)-[:X {blocked: false}]->(:UNBLOCKED)<-[:X {blocked: false}]-(martin) -CREATE (charlie)-[:X {blocked: true}]->(:BLOCKED)<-[:X {blocked: false}]-(martin) +CREATE (charlie)-[:ACTED_IN {role: 'Bud', lead: true}]->(:Movie {title: 'Free Money'})<-[:ACTED_IN {role:'New Warden', lead: false}]-(martin), +(charlie)-[:ACTED_IN {role: 'Jake Peterson', lead: true}]->(:Movie {title: 'No Code of Conduct'})<-[:ACTED_IN {role: 'Bill Peterson', lead: true}]-(martin) ---- -This means that we are starting out with the following graph: +This leads to the following graph: -image:graph_match_clause_variable_length.svg[] - -//// -CREATE - (charlie:Person {name: 'Charlie Sheen'}), - (martin:Person {name: 'Martin Sheen'}), - (michael:Person {name: 'Michael Douglas'}), - (oliver:Person {name: 'Oliver Stone'}), - (rob:Person {name: 'Rob Reiner'}), - (wallStreet:Movie {title: 'Wall Street'}), - (charlie)-[:ACTED_IN {role: 'Bud Fox'}]->(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident); -MATCH - (charlie:Person {name: 'Charlie Sheen'}), - (martin:Person {name: 'Martin Sheen'}) -CREATE (charlie)-[:X {blocked: false}]->(:UNBLOCKED)<-[:X {blocked: false}]-(martin); -CREATE (charlie)-[:X {blocked: true}]->(:BLOCKED)<-[:X {blocked: false}]-(martin) -//// +image::graph_match_clause_variable_length.svg[width="600", role="middle"] .Query -[source, cypher, indent=0] +[source, cypher] ---- -MATCH p = (charlie:Person)-[* {blocked:false}]-(martin:Person) +MATCH p = (charlie:Person)-[* {lead: true}]-(martin:Person) WHERE charlie.name = 'Charlie Sheen' AND martin.name = 'Martin Sheen' RETURN p ---- -Returns the paths between *'Charlie Sheen'* and *'Martin Sheen'* where all relationships have the `blocked` property set to `false`. +The above query returns the paths between `Charlie Sheen` and `Martin Sheen` where all relationships have the `lead` property set to `true`. +The following graph and text are returned: + +image::MATCH_properties_on_variable_length_path.svg[width="400",role="middle"] .Result [role="queryresult",options="header,footer",cols="1*(7)<-[X,8]-(1)+ +| +[{"name":"Charlie Sheen"},{"role":"Jake Peterson","lead":true},{"title":"No Code of Conduct"},{"title":"No Code of Conduct"},{"role":"Bill Peterson","lead":true},{"name":"Martin Sheen"}]+ 1+d|Rows: 1 |=== @@ -708,24 +562,6 @@ Using variable length paths that have the lower bound zero means that two variab If the path length between two nodes is zero, they are by definition the same node. Note that when matching zero length paths the result may contain a match even when matching on a relationship type not in use. -//// -CREATE - (charlie:Person {name: 'Charlie Sheen'}), - (martin:Person {name: 'Martin Sheen'}), - (michael:Person {name: 'Michael Douglas'}), - (oliver:Person {name: 'Oliver Stone'}), - (rob:Person {name: 'Rob Reiner'}), - (wallStreet:Movie {title: 'Wall Street'}), - (charlie)-[:ACTED_IN {role: 'Bud Fox'}]->(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// - .Query [source, cypher, indent=0] ---- @@ -739,11 +575,11 @@ Returns the movie itself as well as actors and directors one relationship away [role="queryresult",options="header,footer",cols="1*(wallStreet), - (martin)-[:ACTED_IN {role: 'Carl Fox'}]->(wallStreet), - (michael)-[:ACTED_IN {role: 'Gordon Gekko'}]->(wallStreet), - (oliver)-[:DIRECTED]->(wallStreet), - (thePresident:Movie {title: 'The American President'}), - (martin)-[:ACTED_IN {role: 'A.J. MacInerney'}]->(thePresident), - (michael)-[:ACTED_IN {role: 'President Andrew Shepherd'}]->(thePresident), - (rob)-[:DIRECTED]->(thePresident) -//// +It is possible to introduce a named path to return or filter on a path in the pattern graph. +For example: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH p = (michael {name: 'Michael Douglas'})-->() RETURN p ---- -Returns the two paths starting from *'Michael Douglas'* +This query returns the following graph and text, showing the two paths starting from `Michael Douglas`. + +image::MATCH_named_path_example.svg[width="500",role="middle"] .Result [role="queryresult",options="header,footer",cols="1*(5)+ -| +(2)-[ACTED_IN,5]->(6)+ +| +[{"name":"Michael Douglas"},{"role":"Gordon Gekko"},{"title":"Wall Street"}]+ +| +[{"name":"Michael Douglas"},{"role":"President Andrew Shepherd"},{"title":"The American President"}]+ 1+d|Rows: 2 |=== @@ -793,13 +614,14 @@ Returns the two paths starting from *'Michael Douglas'* [[match-on-bound-rel]] === Matching on a bound relationship -When your pattern contains a bound relationship, and that relationship pattern does not specify direction, Cypher will try to match the relationship in both directions. +When a pattern contains a bound relationship, and that relationship pattern does not specify direction, Cypher will try to match the relationship in both directions. +For example: .Query [source, cypher, indent=0] ---- MATCH (a)-[r]-(b) -WHERE elementId(r) = 0 +WHERE split(elementId(r), ":")[2] = "0" RETURN a, b ---- @@ -809,8 +631,8 @@ This returns the two connected nodes, once as the start node, and once as the en [role="queryresult",options="header,footer",cols="2*(5)<-[DIRECTED,3]-(3)+ +| +[{"name":"Martin Sheen"},{"role":"Carl Fox"},{"title":"Wall Street"},{"title":"Wall Street"},{},{"name":"Oliver Stone"}] + 1+d|Rows: 1 |=== - [[single-shortest-path-with-predicates]] === Single shortest path with predicates Predicates used in the `WHERE` clause that apply to the shortest path pattern are evaluated before deciding what the shortest matching path is. .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (charlie:Person {name: 'Charlie Sheen'}), (martin:Person {name: 'Martin Sheen'}), p = shortestPath((charlie)-[*]-(martin)) -WHERE none(r IN relationships(p) WHERE type(r) = 'FATHER') +WHERE none(r IN relationships(p) WHERE type(r) = 'FATHER_OF') RETURN p ---- -This query will find the shortest path between *'Charlie Sheen'* and *'Martin Sheen'*, and the `WHERE` predicate will ensure that we do not consider the father/son relationship between the two. +This query will find the shortest path between `Charlie Sheen` and `Martin Sheen`, and the `WHERE` predicate will ensure that the `FATHER_OF` relationship between the two is not considered. + +It returns the following graph and text: + +image::MATCH_shortestpath_with_predicates_example.svg[width="400",role="middle"] .Result [role="queryresult",options="header,footer",cols="1*(5)<-[ACTED_IN,1]-(1)+ +| +[{"name":"Charlie Sheen"},{"role":"Bud Fox"},{"title":"Wall Street"},{"title":"Wall Street"},{"role":"Carl Fox"},{"name":"Martin Sheen"}]+ 1+d|Rows: 1 |=== @@ -878,10 +707,10 @@ This query will find the shortest path between *'Charlie Sheen'* and *'Martin Sh [[all-shortest-paths]] === All shortest paths -Finds all the shortest paths between two nodes. +Finding all shortest paths between two nodes can be done by using the `allShortestPaths` function: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (martin:Person {name: 'Martin Sheen'} ), @@ -890,23 +719,26 @@ MATCH RETURN p ---- -Finds the two shortest paths between *'Martin Sheen'* and *'Michael Douglas'*. +This query finds the two shortest paths between `Martin Sheen` and `Michael Douglas`. +It returns the following graph and text: + +image::MATCH_allshortestpaths_example.svg[width="400",role="middle"] .Result [role="queryresult",options="header,footer",cols="1*(5)<-[ACTED_IN,2]-(2)+ -| +(1)-[ACTED_IN,4]->(6)<-[ACTED_IN,5]-(2)+ +| +[{"name":"Martin Sheen"},{"role":"Carl Fox"},{"title":"Wall Street"},{"title":"Wall Street"},{"role":"Gordon Gekko"},{"name":"Michael Douglas"}]+ +| +[{"name":"Martin Sheen"},{"role":"A.J. MacInerney"},{"title":"The American President"},{"title":"The American President"},{"role":"President Andrew Shepherd"},{"name":"Michael Douglas"}]+ 1+d|Rows: 2 |=== -[[get-node-rel-by-id]] -== Get node or relationship by ID +[[get-node-rel-by-elementid]] +== Get node or relationship by elementId -[[match-node-by-id]] -=== Node by ID +[[match-node-by-elementid]] +=== Node by elementId Searching for nodes by ID can be done with the `elementId()` function in a predicate. @@ -921,7 +753,7 @@ It is therefore recommended to rather use application-generated IDs. [source, cypher, indent=0] ---- MATCH (n) -WHERE elementId(n) = 0 +WHERE split(elementId(n), ":")[2] = "0" RETURN n ---- @@ -931,40 +763,37 @@ The corresponding node is returned. [role="queryresult",options="header,footer",cols="1*() -WHERE elementId(r) = 0 +WHERE split(elementId(r), ":")[2] = "0" RETURN r ---- -The relationship with ID `0` is returned. +The relationship with the elementId `0` is returned. .Result [role="queryresult",options="header,footer",cols="1* Date: Mon, 23 Jan 2023 11:26:37 +0100 Subject: [PATCH 062/383] Add notes about not recommending dots in database and alias names --- modules/ROOT/pages/aliases.adoc | 6 ++++ modules/ROOT/pages/databases.adoc | 49 ++++++++++++++++--------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 409392af9..6632abeb7 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -1346,6 +1346,12 @@ The following naming rules apply: The name restrictions and escaping rules apply to all the different database alias commands. +[NOTE] +==== +Having dots (`.`) in the database alias names are not recommended. +This is due to the difficulty of separating if a dot is part of the database alias name or a delimiter for a database alias in a composite database. +==== + When it comes to escaping names using backticks, there are some additional things to consider around database aliases in composite databases: .+Escaping database alias and composite database names+ diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index c89fa5f43..36ac68349 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -470,6 +470,22 @@ SHOW DATABASE library YIELD name, constituents Databases can be created using `CREATE DATABASE`. +Database names are subject to the xref::syntax/naming.adoc[standard Cypher restrictions on valid identifiers]. + +The following naming rules apply: + +* Database name length must be between 3 and 63 characters. +* The first character must be an ASCII alphabetic character. +* Subsequent characters can be ASCII alphabetic (`mydatabase`), numeric characters (`mydatabase2`), dots (`main.db`), and dashes (enclosed within backticks, e.g., `CREATE DATABASE ++`main-db`++`). +Using database names with dots without enclosing them in backticks is deprecated. +* Names cannot end with dots or dashes. +* Names that begin with an underscore or with the prefix `system` are reserved for internal use. + +[NOTE] +==== +Having dots (`.`) in the database names are not recommended. +This is due to the difficulty of separating if a dot is part of the database name or a delimiter for a database alias in a composite database. +==== .+CREATE DATABASE+ ====== @@ -487,20 +503,6 @@ System updates: 1 Rows: 0 ---- -[NOTE] -==== -Database names are subject to the xref::syntax/naming.adoc[standard Cypher restrictions on valid identifiers]. - -The following naming rules apply: - -* Database name length must be between 3 and 63 characters. -* The first character must be an ASCII alphabetic character. -* Subsequent characters can be ASCII alphabetic (`mydatabase`), numeric characters (`mydatabase2`), dots (`main.db`), and dashes (enclosed within backticks, e.g., `CREATE DATABASE ++`main-db`++`). -Using database names with dots without enclosing them in backticks is deprecated. -* Names cannot end with dots or dashes. -* Names that begin with an underscore or with the prefix `system` are reserved for internal use. -==== - ====== @@ -569,6 +571,16 @@ For more information about composite databases, see link:{neo4j-docs-base-uri}/o Composite databases can be created using `CREATE COMPOSITE DATABASE`. +Composite database names are subject to the same rules as xref:administration-databases-create-database[standard databases]. +One difference is however that the deprecated syntax using dots without enclosing the name in backticks is not available. +Both dots and dashes needs to be enclosed within backticks when using composite databases. + +[NOTE] +==== +Having dots (`.`) in the composite database names are not recommended. +This is due to the difficulty of separating if a dot is part of the composite database name or a delimiter for a database alias in a composite database. +==== + .Query [source, cypher, indent=0] ---- @@ -578,15 +590,6 @@ CREATE COMPOSITE DATABASE inventory [role="statsonlyqueryresult"] 0 rows, System updates: 1 -[NOTE] -==== -Composite database names are subject to the same rules as standard databases. -One difference is however that the deprecated syntax using dots without enclosing the name in backticks is not available. -Both dots and dashes needs to be enclosed within backticks when using composite databases. - - -==== - When a composite database has been created, it will show up in the listing provided by the command `SHOW DATABASES`. From 903b738f4516838b037daae3a3201bb9c5cfce40 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Mon, 23 Jan 2023 11:27:28 +0100 Subject: [PATCH 063/383] Update link to correct name noticed the link for aliases in composite didn't have the right handle --- modules/ROOT/pages/databases.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 36ac68349..9b25c5ec7 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -615,7 +615,7 @@ SHOW DATABASES YIELD name, type, access, role, writer, constituents |=== In order to create database aliases in the composite database, give the composite database as namespace for the alias. -For information about creating aliases in composite databases, see <>. +For information about creating aliases in composite databases, see xref:aliases.adoc#alias-management-create-composite-database-alias[here]. [role=enterprise-edition] From 5192ad4fcf632ab4865b291a7020ffdabb235de3 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Mon, 23 Jan 2023 11:54:10 +0100 Subject: [PATCH 064/383] Review comments --- modules/ROOT/pages/aliases.adoc | 4 ++-- modules/ROOT/pages/databases.adoc | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 6632abeb7..52e8ea50d 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -1348,8 +1348,8 @@ The name restrictions and escaping rules apply to all the different database ali [NOTE] ==== -Having dots (`.`) in the database alias names are not recommended. -This is due to the difficulty of separating if a dot is part of the database alias name or a delimiter for a database alias in a composite database. +Having dots (`.`) in the database alias names is not recommended. +This is due to the difficulty of determining if a dot is part of the database alias name or a delimiter for a database alias in a composite database. ==== When it comes to escaping names using backticks, there are some additional things to consider around database aliases in composite databases: diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 9b25c5ec7..4171350f0 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -483,8 +483,8 @@ Using database names with dots without enclosing them in backticks is deprecated [NOTE] ==== -Having dots (`.`) in the database names are not recommended. -This is due to the difficulty of separating if a dot is part of the database name or a delimiter for a database alias in a composite database. +Having dots (`.`) in the database names is not recommended. +This is due to the difficulty of determining if a dot is part of the database name or a delimiter for a database alias in a composite database. ==== .+CREATE DATABASE+ @@ -573,12 +573,12 @@ Composite databases can be created using `CREATE COMPOSITE DATABASE`. Composite database names are subject to the same rules as xref:administration-databases-create-database[standard databases]. One difference is however that the deprecated syntax using dots without enclosing the name in backticks is not available. -Both dots and dashes needs to be enclosed within backticks when using composite databases. +Both dots and dashes need to be enclosed within backticks when using composite databases. [NOTE] ==== -Having dots (`.`) in the composite database names are not recommended. -This is due to the difficulty of separating if a dot is part of the composite database name or a delimiter for a database alias in a composite database. +Having dots (`.`) in the composite database names is not recommended. +This is due to the difficulty of determining if a dot is part of the composite database name or a delimiter for a database alias in a composite database. ==== .Query From 0dec7984200684fbd6d131b5009818b34f6c89dc Mon Sep 17 00:00:00 2001 From: Lidia Zuin <102308961+lidiazuin@users.noreply.github.com> Date: Mon, 23 Jan 2023 12:10:33 +0100 Subject: [PATCH 065/383] Adding link to specific fulltext procedures as requested (#322) (#325) Used had difficulty in finding the way to the procedures, so linking them in the list could be helpful. This PR is related to https://github.com/neo4j/docs-operations/pull/373. Cherry-pick of https://github.com/neo4j/docs-cypher/pull/322 --- modules/ROOT/pages/indexes-for-full-text-search.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/indexes-for-full-text-search.adoc b/modules/ROOT/pages/indexes-for-full-text-search.adoc index 7c28e512b..6dccf83ef 100644 --- a/modules/ROOT/pages/indexes-for-full-text-search.adoc +++ b/modules/ROOT/pages/indexes-for-full-text-search.adoc @@ -68,15 +68,15 @@ a| Create a relationship full-text index for the given relationship types and properties. | List available analyzers. -| `db.index.fulltext.listAvailableAnalyzers` +| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_listavailableanalyzers[`db.index.fulltext.listAvailableAnalyzers`] | List the available analyzers that the full-text indexes can be configured with. | Use full-text node index. -| `db.index.fulltext.queryNodes` +| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_querynodes[`db.index.fulltext.queryNodes`] | Query the given full-text index. Returns the matching nodes and their Lucene query score, ordered by score. | Use full-text relationship index. -| `db.index.fulltext.queryRelationships` +| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_queryrelationships[`db.index.fulltext.queryRelationships`] | Query the given full-text index. Returns the matching relationships and their Lucene query score, ordered by score. | Drop full-text index. @@ -84,7 +84,7 @@ Create a relationship full-text index for the given relationship types and prope | Drop the specified index. | Eventually consistent indexes. -| `db.index.fulltext.awaitEventuallyConsistentIndexRefresh` +| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_awaiteventuallyconsistentindexrefresh[`db.index.fulltext.awaitEventuallyConsistentIndexRefresh`] | Wait for the updates from recently committed transactions to be applied to any eventually-consistent full-text indexes. | Listing all full-text indexes. From bce99d9eaba11d065ac893e874d36ba6bd7bae60 Mon Sep 17 00:00:00 2001 From: AlexicaWright <49636617+AlexicaWright@users.noreply.github.com> Date: Mon, 23 Jan 2023 12:00:03 +0100 Subject: [PATCH 066/383] updated cluster roles and added more columns for show databases --- modules/ROOT/pages/databases.adoc | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 4171350f0..7fea50ce3 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -155,9 +155,9 @@ Instance address in a clustered DBMS. The default for a standalone database is `neo4j://localhost:7687`. label:default-output[] | role -| The current role of the database (`standalone`, `leader`, `follower`, `read_replica`, `unknown`). label:default-output[] +| The current role of the database (`primary`, `secondary`, `unknown`). label:default-output[] -|writer +| writer |`true` for the database node that accepts writes (this node is the leader for this database in a cluster or this is a standalone instance). label:default-output[] | requestedStatus @@ -187,6 +187,22 @@ Shown if this is the home database for the current user. label:default-output[] Not returned by `SHOW HOME DATABASE` or `SHOW DEFAULT DATABASE`. ==== +| `currentPrimariesCount` +| Number of primaries for this database reported as running currently. +It is the same as the number of rows where `role=primary` and `name=this database`. + +| `currentSecondariesCount` +| Number of secondaries for this database reported as running currently. +It is the same as the number of rows where `role=secondary` and `name=this database`. + +| `requestedPrimariesCount` +| The requested number of primaries for this database. +May be lower than current if the DBMS is currently reducing the number of copies of the database, or higher if it is currently increasing the number of copies. + +| `requestedSecondariesCount` +| The requested number of secondaries for this database. +May be lower than current if the DBMS is currently reducing the number of copies of the database, or higher if it is currently increasing the number of copies. + | creationTime | The date and time at which the database was created. From 05064c8241912dfa0afe6eb0b016119f3a78a506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 23 Jan 2023 13:31:19 +0100 Subject: [PATCH 067/383] Add PROFILE to queries on operators page (#327) - Add PROFILE to queries on operators page - fix 1 spelling error --- .../ROOT/pages/execution-plans/operators.adoc | 112 +++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index fcd2fdca8..b280f1836 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -30,6 +30,7 @@ Any query using this operator is likely to encounter performance problems on a n .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n) RETURN n ---- @@ -72,6 +73,7 @@ The `DirectedRelationshipIndexScan` operator examines all values stored in an in .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH ()-[r: WORKS_IN]->() WHERE r.title IS NOT NULL RETURN r @@ -115,6 +117,7 @@ The `UndirectedRelationshipIndexScan` operator examines all values stored in an .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH ()-[r: WORKS_IN]-() WHERE r.title IS NOT NULL RETURN r @@ -159,6 +162,7 @@ The relationship variable and the index used are shown in the arguments of the o .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (candidate)-[r:WORKS_IN]->() WHERE r.title = 'chief architect' RETURN candidate @@ -203,6 +207,7 @@ The relationship variable and the index used are shown in the arguments of the o .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (candidate)-[r:WORKS_IN]-() WHERE r.title = 'chief architect' RETURN candidate @@ -246,6 +251,7 @@ The `DirectedRelationshipByIdSeek` operator reads one or more relationships by i .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n1)-[r]->() WHERE id(r) = 0 RETURN r, n1 @@ -290,6 +296,7 @@ As the direction is unspecified, two rows are produced for each relationship as .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n1)-[r]-() WHERE elementId(r) = 1 RETURN r, n1 @@ -334,6 +341,7 @@ Although this is slower than an index seek (since all entries need to be examine .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH ()-[r: WORKS_IN]->() WHERE r.title CONTAINS 'senior' RETURN r @@ -378,6 +386,7 @@ Although this is slower than an index seek (since all entries need to be examine .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH ()-[r: WORKS_IN]-() WHERE r.title CONTAINS 'senior' RETURN r @@ -422,6 +431,7 @@ Although this is slower than an index seek (since all entries need to be examine .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH ()-[r: WORKS_IN]->() WHERE r.title ENDS WITH 'developer' RETURN r @@ -466,6 +476,7 @@ Although this is slower than an index seek (since all entries need to be examine .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH ()-[r: WORKS_IN]-() WHERE r.title ENDS WITH 'developer' RETURN r @@ -510,6 +521,7 @@ The `DirectedRelationshipIndexSeekByRange` operator finds relationships and thei .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (candidate: Person)-[r:WORKS_IN]->(location) WHERE r.duration > 100 RETURN candidate @@ -556,6 +568,7 @@ The `UndirectedRelationshipIndexSeekByRange` operator finds relationships and th .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (candidate: Person)-[r:WORKS_IN]-(location) WHERE r.duration > 100 RETURN candidate @@ -603,6 +616,7 @@ CREATE LOOKUP INDEX lookup_index_name FOR (n) ON EACH labels(n) .Query [source,cypher] ---- +PROFILE MATCH (countryOrLocation:Country|Location) RETURN countryOrLocation ---- @@ -646,6 +660,7 @@ CREATE LOOKUP INDEX lookup_index_name FOR (n) ON EACH labels(n) .Query [source,cypher] ---- +PROFILE MATCH (countryAndLocation:Country&Location) RETURN countryAndLocation ---- @@ -684,6 +699,7 @@ The `DirectedAllRelationshipsScan` operator fetches all relationships and their .Query [source,cypher] ---- +PROFILE MATCH ()-[r]->() RETURN r ---- @@ -718,6 +734,7 @@ The `UndirectedAllRelationshipsScan` operator fetches all relationships and thei .Query [source,cypher] ---- +PROFILE MATCH ()-[r]-() RETURN r ---- @@ -758,6 +775,7 @@ The `DirectedRelationshipTypeScan` operator fetches all relationships and their .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH ()-[r: FRIENDS_WITH]->() RETURN r ---- @@ -798,6 +816,7 @@ The `UndirectedRelationshipTypeScan` operator fetches all relationships and thei .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH ()-[r: FRIENDS_WITH]-() RETURN r ---- @@ -840,6 +859,7 @@ The `DirectedUnionRelationshipTypeScan` operator fetches all relationships and t .Query [source,cypher] ---- +PROFILE MATCH ()-[friendOrFoe: FRIENDS_WITH|FOE]->() RETURN friendOrFoe ---- @@ -882,6 +902,7 @@ The `UndirectedUnionRelationshipTypeScan` operator fetches all relationships and .Query [source,cypher] ---- +PROFILE MATCH ()-[friendOrFoe: FRIENDS_WITH|FOE]-() RETURN friendOrFoe ---- @@ -924,6 +945,7 @@ The `NodeByIdSeek` operator reads one or more nodes by id from the node store. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n) WHERE elementId(n) = 0 RETURN n @@ -967,6 +989,7 @@ The `NodeByLabelScan` operator fetches all nodes with a specific label from the .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (person:Person) RETURN person ---- @@ -1011,6 +1034,7 @@ If the index is a unique index, the operator is instead called xref::execution-p .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (location:Location {name: 'Malmo'}) RETURN location ---- @@ -1057,6 +1081,7 @@ This makes it clear that any nodes returned from the index will be locked in ord .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (t:Team {name: 'Malmo'}) RETURN t ---- @@ -1104,6 +1129,7 @@ For example, if the operator does two seeks and the first seek finds the nodes ` .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=pipelined MATCH (location:Location {name: 'Malmo'}), @@ -1152,6 +1178,7 @@ Owing to the existence of two property uniqueness constraints on `:Team(name)` a .Query [source, cypher, role="noplay"] ---- +PROFILE MERGE (t:Team {name: 'Engineering', id: 42}) ---- @@ -1199,6 +1226,7 @@ If the index is a unique index, the operator is instead called `NodeUniqueIndexS .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (l:Location) WHERE l.name STARTS WITH 'Lon' RETURN l @@ -1244,6 +1272,7 @@ If the index is not unique, the operator is instead called `NodeIndexSeekByRange .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (t:Team) WHERE t.name STARTS WITH 'Ma' RETURN t @@ -1288,6 +1317,7 @@ Although this is slower than an index seek (since all entries need to be examine .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (l:Location) WHERE l.name CONTAINS 'al' RETURN l @@ -1332,6 +1362,7 @@ Although this is slower than an index seek (since all entries need to be examine .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (l:Location) WHERE l.name ENDS WITH 'al' RETURN l @@ -1375,6 +1406,7 @@ The `NodeIndexScan` operator examines all values stored in an index, returning a .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (l:Location) WHERE l.name IS NOT NULL RETURN l @@ -1423,6 +1455,7 @@ The `Apply` operator (i.e. the standard version) takes the row produced by the r .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person {name: 'me'}) MATCH (q:Person {name: p.secondName}) RETURN p, q @@ -1472,6 +1505,7 @@ This makes `SemiApply` a filtering operator, used mostly for pattern predicates .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MATCH (p:Person) WHERE (p)-[:FRIENDS_WITH]->(:Person) @@ -1525,6 +1559,7 @@ This makes `AntiSemiApply` a filtering operator, used for pattern predicates in .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MATCH (me:Person {name: 'me'}), @@ -1583,6 +1618,7 @@ If there are no incoming rows, the `Anti` operator will yield a single row. .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=pipelined MATCH (me:Person {name: 'me'}), @@ -1648,6 +1684,7 @@ In the example, `LetSemiApply` will be used to check for the presence of the `FR .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MATCH (other:Person) WHERE (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) @@ -1711,6 +1748,7 @@ In the example, `LetAntiSemiApply` will be used to check for the absence of the .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MATCH (other:Person) WHERE NOT ((other)-[:FRIENDS_WITH]->(:Person)) OR (other)-[:WORKS_IN]->(:Location) @@ -1773,6 +1811,7 @@ First, the normal expression predicate is evaluated, and, only if it returns `fa .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (other:Person) WHERE other.age > 25 OR (other)-[:FRIENDS_WITH]->(:Person) RETURN other.name @@ -1830,6 +1869,7 @@ If the predicate returns `false` or `null`, `SelectOrAntiSemiApply` will instead .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (other:Person) WHERE other.age > 25 OR NOT (other)-[:FRIENDS_WITH]->(:Person) RETURN other.name @@ -1865,7 +1905,7 @@ Batch size 128 | | | +-----------------------------------------+----------------+------+---------+----------------+ | | | | | +Argument | other | 14 | 14 | 0 | 2168 | 2/0 | 0.449 | Fused in Pipeline 1 | | | +-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodyByLabelScan | other:Person | 14 | 14 | 35 | | | | Fused in Pipeline 0 | +| +NodeByLabelScan | other:Person | 14 | 14 | 35 | | | | Fused in Pipeline 0 | +------------------------+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 136, total allocated memory: 4208 @@ -1888,6 +1928,7 @@ This is a variation of the xref::execution-plans/operators.adoc#query-plan-apply .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MATCH (other:Person) WHERE (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) OR other.age = 5 @@ -1949,6 +1990,7 @@ This operator is a variation of the xref::execution-plans/operators.adoc#query-p .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MATCH (other:Person) WHERE NOT (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) OR other.age = 5 @@ -2014,6 +2056,7 @@ If no matches are found instead nodes and relationships are created and all `ON .Query [source, cypher, role="noplay"] ---- +PROFILE MERGE (p:Person {name: 'Andy'}) ON MATCH SET p.existed = true ON CREATE SET p.existed = false @@ -2062,6 +2105,7 @@ The `LockingMerge` operator is just like a normal `Merge` but will lock the star .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (s:Person {name: 'me'}) MERGE (s)-[:FRIENDS_WITH]->(s) ---- @@ -2115,6 +2159,7 @@ This operator is a variation of the xref::execution-plans/operators.adoc#query-p .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MATCH (p:Person) RETURN p.name, [(p)-[:WORKS_IN]->(location) | location.name] AS cities @@ -2166,6 +2211,7 @@ The `Argument` operator indicates the variable to be used as an argument to the .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (s:Person {name: 'me'}) MERGE (s)-[:FRIENDS_WITH]->(s) ---- @@ -2221,6 +2267,7 @@ Given a start node, and depending on the pattern relationship, the `Expand(All)` .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof) RETURN fof ---- @@ -2267,6 +2314,7 @@ This can make a noticeable difference when dense nodes appear as end points. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof)-->(p) RETURN fof ---- @@ -2316,6 +2364,7 @@ In this situation, `OptionalExpand(all)` will return a single row with the relat .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) OPTIONAL MATCH (p)-[works_in:WORKS_IN]->(l) WHERE works_in.duration > 180 @@ -2365,6 +2414,7 @@ This can make a noticeable difference when dense nodes appear as end points. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person)-[works_in:WORKS_IN]->(l) OPTIONAL MATCH (l)-->(p) RETURN p @@ -2412,6 +2462,7 @@ Given a start node, the `VarLengthExpand(All)` operator will traverse variable-l .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(q:Person) RETURN p, q ---- @@ -2458,6 +2509,7 @@ When both the start and end node have already been found, the `VarLengthExpand(I .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(p:Person) RETURN p ---- @@ -2511,6 +2563,7 @@ The `VarLengthExpand(Pruning)` operator guarantees that all the end nodes produc .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person)-[:FRIENDS_WITH *3..4]-(q:Person) RETURN DISTINCT p, q ---- @@ -2566,6 +2619,7 @@ This operator guarantees that all the end nodes produced are unique. .Query [source, cypher] ---- +PROFILE MATCH (p:Person)-[:FRIENDS_WITH *..4]-(q:Person) RETURN DISTINCT p, q ---- @@ -2614,6 +2668,7 @@ Owing to the existence of two property uniqueness constraints on `:Team(name)` a .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MERGE (t:Team {name: 'Engineering', id: 42}) ---- @@ -2665,6 +2720,7 @@ The `EmptyResult` operator eagerly loads all incoming data and discards it. .Query [source, cypher, role="noplay"] ---- +PROFILE CREATE (:Person) ---- @@ -2709,6 +2765,7 @@ It is present in every single query that returns data to the user, and has littl .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n) RETURN n ---- @@ -2752,6 +2809,7 @@ It is used whenever the xref::clauses/load-csv.adoc[LOAD CSV] clause is used in .Query [source, cypher, role="noplay"] ---- +PROFILE LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line RETURN line ---- @@ -2814,6 +2872,7 @@ As primitive types and arrays can be used, it can be done very efficiently. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (bob:Person {name: 'Bob'})-[:WORKS_IN]->(loc)<-[:WORKS_IN]-(matt:Person {name: 'Mattias'}) RETURN loc.name ---- @@ -2870,6 +2929,7 @@ It is most frequently used to solve predicates of the form: `n.prop1 = m.prop2` .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person), (q:Person) @@ -2924,6 +2984,7 @@ On the other hand, if `(a:Person)` returns more results than `(a)-->(b:Person)`, .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (a:Person) OPTIONAL MATCH (a)-->(b:Person) USING JOIN ON a @@ -2980,6 +3041,7 @@ The example finds the names of all friends of my friends that are not already my .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) WHERE NOT (me)-[:FRIENDS_WITH]-(other) @@ -3038,6 +3100,7 @@ The example finds the names of all friends of my friends that are not already my .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=pipelined MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) WHERE NOT (me)-[:FRIENDS_WITH]-(other) @@ -3102,6 +3165,7 @@ The example finds the names of all friends of my friends that are not already my .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=pipelined MATCH (me:Person)-[:FRIENDS_WITH]-()-[:FRIENDS_WITH]-(other) WHERE NOT (me)-[:FRIENDS_WITH]-(other) @@ -3164,6 +3228,7 @@ The `CartesianProduct` operator produces a cartesian product of the two inputs - .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person), (t:Team) @@ -3214,6 +3279,7 @@ In an analogous manner to the xref::execution-plans/operators.adoc#query-plan-ap .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) ---- @@ -3266,6 +3332,7 @@ Alternatively, the records to be updated can be returned, followed by an update .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (a), (b) DELETE a, b MERGE () @@ -3333,6 +3400,7 @@ To do this, `EagerAggregation`, as the name implies, needs to pull in all data e .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (l:Location)<-[:WORKS_IN]-(p:Person) RETURN l.name AS location, @@ -3386,6 +3454,7 @@ This operator uses lazy evaluation and has a lower memory pressure in the system .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) WHERE p.name STARTS WITH 'P' RETURN p.name, count(*) AS count @@ -3434,6 +3503,7 @@ For example, we can get counts for all nodes, and nodes with a label, but not no .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) RETURN count(p) AS people ---- @@ -3479,6 +3549,7 @@ For example, we can get counts for all relationships, relationships with a type, .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person)-[r:WORKS_IN]->() RETURN count(r) AS jobs ---- @@ -3523,6 +3594,7 @@ This may lead to increased memory pressure in the system. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (l:Location)<-[:WORKS_IN]-(p:Person) RETURN DISTINCT l ---- @@ -3571,6 +3643,7 @@ This operator has a lower memory pressure in the system than the `Distinct` oper .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) WHERE p.name STARTS WITH 'P' RETURN DISTINCT p.name @@ -3616,6 +3689,7 @@ The `Filter` operator filters each row coming from the child operator, only pass .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) WHERE p.name =~ '^a.*' RETURN p @@ -3661,6 +3735,7 @@ The `Limit` operator returns the first `+n+` rows from the incoming input. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) RETURN p LIMIT 3 @@ -3706,6 +3781,7 @@ The `Skip` operator skips `+n+` rows from the incoming rows. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) RETURN p ORDER BY p.id @@ -3757,6 +3833,7 @@ In order to sort the data, all data from the source operator needs to be pulled .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) RETURN p ORDER BY p.name @@ -3806,6 +3883,7 @@ Partial sort is only applicable when sorting on multiple columns. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) WHERE p.name STARTS WITH 'P' RETURN p @@ -3855,6 +3933,7 @@ Instead of sorting the entire input, only the top `+n+` rows are retained. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) RETURN p ORDER BY p.name @@ -3905,6 +3984,7 @@ Partial top is only applicable when sorting on multiple columns. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) WHERE p.name STARTS WITH 'P' RETURN p @@ -3954,6 +4034,7 @@ The `Union` operator concatenates the results from the right child operator with .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Location) RETURN p.name UNION ALL @@ -4013,6 +4094,7 @@ The `Unwind` operator returns one row per item in a list. .Query [source, cypher, role="noplay"] ---- +PROFILE UNWIND range(1, 5) AS value RETURN value ---- @@ -4057,6 +4139,7 @@ Used when combining `LIMIT` and updates .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) SET p.seen = true RETURN p @@ -4107,6 +4190,7 @@ However, if no data is returned by its source, `Optional` will yield a single ro .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person {name: 'me'}) OPTIONAL MATCH (q:Person {name: 'Lulu'}) RETURN p, q @@ -4156,6 +4240,7 @@ The `ProjectEndpoints` operator projects the start and end node of a relationshi .Query [source, cypher, role="noplay"] ---- +PROFILE CREATE (n)-[p:KNOWS]->(m) WITH p AS r MATCH (u)-[r]->(v) @@ -4208,6 +4293,7 @@ For each incoming row, the `Projection` operator evaluates a set of expressions .Query [source, cypher, role="noplay"] ---- +PROFILE RETURN 'hello' AS greeting ---- @@ -4249,6 +4335,7 @@ The `ShortestPath` operator finds one or all shortest paths between two previous .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (andy:Person {name: 'Andy'}), (mattias:Person {name: 'Mattias'}), @@ -4297,6 +4384,7 @@ The `EmptyRow` operator returns a single row with no columns. .Query [source, cypher, role="noplay"] ---- +PROFILE CYPHER runtime=slotted FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) ---- @@ -4347,6 +4435,7 @@ The `ProcedureCall` operator indicates an invocation to a procedure. .Query [source, cypher, role="noplay"] ---- +PROFILE CALL db.labels() YIELD label RETURN * ORDER BY label @@ -4394,6 +4483,7 @@ In the plan below we will cache `l.name` before `Expand(All)` where there are fe .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (l:Location)<-[:WORKS_IN]-(p:Person) RETURN l.name AS location, @@ -4446,6 +4536,7 @@ The `Create` operator is used to create nodes and relationships. .Query [source, cypher, role="noplay"] ---- +PROFILE CREATE (max:Person {name: 'Max'}), (chris:Person {name: 'Chris'}) @@ -4493,6 +4584,7 @@ The `Delete` operator is used to delete a node or a relationship. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (me:Person {name: 'me'})-[w:WORKS_IN {duration: 190}]->(london:Location {name: 'London'}) DELETE w ---- @@ -4545,6 +4637,7 @@ The `DetachDelete` operator is used in all queries containing the xref::clauses/ .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (p:Person) DETACH DELETE p ---- @@ -4596,6 +4689,7 @@ The `SetLabels` operator is used when setting labels on a node. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n) SET n:Person ---- @@ -4642,6 +4736,7 @@ The `RemoveLabels` operator is used when deleting labels from a node. .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n) REMOVE n:Person ---- @@ -4688,6 +4783,7 @@ The `SetNodePropertiesFromMap` operator is used when setting properties from a m .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n) SET n = {weekday: 'Monday', meal: 'Lunch'} ---- @@ -4734,6 +4830,7 @@ The `SetRelationshipPropertiesFromMap` operator is used when setting properties .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n)-[r]->(m) SET r = {weight: 5, unit: 'kg'} ---- @@ -4780,6 +4877,7 @@ The `SetProperty` operator is used when setting a property on a node or relation .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (n) SET n.checked = true ---- @@ -4837,6 +4935,7 @@ The following query will create a property uniqueness constraint with the name ` .Query [source, cypher, role="noplay"] ---- +PROFILE CREATE CONSTRAINT uniqueness FOR (c:Country) REQUIRE c.name is UNIQUE ---- @@ -4878,6 +4977,7 @@ The following query will create a property uniqueness constraint with the name ` .Query [source, cypher, role="noplay"] ---- +PROFILE CREATE CONSTRAINT uniqueness IF NOT EXISTS FOR (c:Country) REQUIRE c.name is UNIQUE ---- @@ -4917,6 +5017,7 @@ The `DropConstraint` operator removes a constraint using the name of the constra .Query [source, cypher, role="noplay"] ---- +PROFILE DROP CONSTRAINT name ---- @@ -4955,6 +5056,7 @@ It may include filtering on constraint type and can have either default or full .Query [source, cypher, role="noplay"] ---- +PROFILE SHOW CONSTRAINTS ---- @@ -4997,6 +5099,7 @@ The following query will create an index with the name `my_index` on the `name` .Query [source, cypher, role="noplay"] ---- +PROFILE CREATE INDEX my_index FOR (c:Country) ON (c.name) ---- @@ -5038,6 +5141,7 @@ The following query will create an index with the name `my_index` on the `since` .Query [source, cypher, role="noplay"] ---- +PROFILE CREATE INDEX my_index IF NOT EXISTS FOR ()-[k:KNOWS]-() ON (k.since) ---- @@ -5078,6 +5182,7 @@ The `DropIndex` operator removes an index using the name of the index. .Query [source, cypher, role="noplay"] ---- +PROFILE DROP INDEX name ---- @@ -5116,6 +5221,7 @@ It may include filtering on index type and can have either default or full outpu .Query [source, cypher, role="noplay"] ---- +PROFILE SHOW INDEXES ---- @@ -5158,6 +5264,7 @@ The output can either be default or full output. .Query [source, cypher, role="noplay"] ---- +PROFILE SHOW FUNCTIONS ---- @@ -5198,6 +5305,7 @@ It may include filtering on whether a given user can execute the procedure and c .Query [source, cypher, role="noplay"] ---- +PROFILE SHOW PROCEDURES ---- @@ -5238,6 +5346,7 @@ It may include filtering on given ids and can have either default or full output .Query [source, cypher, role="noplay"] ---- +PROFILE SHOW TRANSACTIONS ---- @@ -5278,6 +5387,7 @@ The `TerminateTransactions` operator terminates transactions by ID. .Query [source, cypher, role="noplay"] ---- +PROFILE TERMINATE TRANSACTIONS 'database-transaction-123' ---- From 39f21d235aea6e4cb08ca43a8994053fb2a70350 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Mon, 23 Jan 2023 14:07:32 +0100 Subject: [PATCH 068/383] Clean aliases examples (#318) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scope of this PR is mostly to fix the aliases page to work with the new testing framework, excluding the examples we cannot test. 1. make all syntax example blocks into `[source, syntax]`, and remove the bulky header from those. 1. exclude remote aliases from testing. It is too complicated to set up the needed infrastructure to test them, both server and client side. Not a priority for now. 1. as a result of 2., change the setup queries so that our new testing doesn't pick up the remote aliases commands. 1. add `WAIT` on database creation in test setup queries, so that we don't get un-ready databases in subsequent queries. 1. exclude aliases with dots from testing, when the dots are not separators between a composite db name and an alias name. This is because it becomes impossible to automate their deletion by taking the list of aliases from `SHOW ALIASES` and deleting them one by one, as the escaping is lost. 1. remove all `indent=0` attributes in code blocks, which had no effect. Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- modules/ROOT/pages/aliases.adoc | 217 +++++++++++++++++--------------- 1 file changed, 115 insertions(+), 102 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 52e8ea50d..ddf944b16 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -40,12 +40,12 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax | Command | Syntax | Show Database Alias | -[source] +[source, syntax, role=noheader] ----- SHOW ALIAS[ES] [name] FOR DATABASE[S] [WHERE expression] ----- -[source] +[source, syntax, role=noheader] ----- SHOW ALIAS[ES] [name] FOR DATABASE[S] YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] @@ -56,12 +56,12 @@ Lists both local and remote database aliases, optionally filtered on the alias n | Create Local Alias | -[source] +[source, syntax, role=noheader] ----- CREATE ALIAS name [IF NOT EXISTS] FOR DATABASE targetName [PROPERTIES "{" key: value[, ...] "}"] ----- -[source] +[source, syntax, role=noheader] ----- CREATE OR REPLACE ALIAS name FOR DATABASE targetName [PROPERTIES "{" key: value[, ...] "}"] @@ -69,14 +69,14 @@ CREATE OR REPLACE ALIAS name FOR DATABASE targetName | Create Remote Alias | -[source] +[source, syntax, role=noheader] ----- CREATE ALIAS name [IF NOT EXISTS] FOR DATABASE targetName AT 'url' USER username PASSWORD 'password' [DRIVER "{" setting: value[, ...] "}"] [PROPERTIES "{" key: value[, ...] "}"] ----- -[source] +[source, syntax, role=noheader] ----- CREATE OR REPLACE ALIAS name FOR DATABASE targetName AT 'url' USER username PASSWORD 'password' @@ -86,7 +86,7 @@ AT 'url' USER username PASSWORD 'password' | Alter Local Alias | -[source] +[source, syntax, role=noheader] ----- ALTER ALIAS name [IF EXISTS] SET DATABASE [TARGET targetName] @@ -95,7 +95,7 @@ ALTER ALIAS name [IF EXISTS] SET DATABASE | Alter Remote Alias | -[source] +[source, syntax, role=noheader] ----- ALTER ALIAS name [IF EXISTS] SET DATABASE [TARGET targetName AT 'url'] @@ -107,7 +107,7 @@ ALTER ALIAS name [IF EXISTS] SET DATABASE | Drop Alias | -[source] +[source, syntax, role=noheader] ----- DROP ALIAS name [IF EXISTS] FOR DATABASE ----- @@ -242,12 +242,22 @@ This prevents issues such as a transaction executing against multiple target dat == Listing database aliases //// +[source, cypher, role=test-setup] +---- CREATE DATABASE `movies`; CREATE ALIAS `films` FOR DATABASE `movies`; CREATE ALIAS `motion pictures` FOR DATABASE `movies` PROPERTIES { nameContainsSpace: true }; -CREATE DATABASE `northwind-graph-2020`; -CREATE DATABASE `northwind-graph-2021`; -CREATE DATABASE `northwind-graph-2022`; +CREATE DATABASE `northwind-graph-2020` WAIT; // wait, so aliases get `currentStatus: online` +CREATE DATABASE `northwind-graph-2021` WAIT; +CREATE DATABASE `northwind-graph-2022` WAIT; +CREATE DATABASE `sci-fi-books`; +CREATE COMPOSITE DATABASE `library`; +CREATE ALIAS `library`.`sci-fi` FOR DATABASE `sci-fi-books`; +CREATE COMPOSITE DATABASE garden; +CREATE DATABASE `perennial-flowers`; +---- + +CREATE ALIAS `library`.`romance` FOR DATABASE `romance-books` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password'; CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { ssl_enforced: true, @@ -258,12 +268,6 @@ DRIVER { connection_pool_max_size: 10, logging_level: 'info' }; -CREATE DATABASE `sci-fi-books`; -CREATE COMPOSITE DATABASE `library`; -CREATE ALIAS `library`.`sci-fi` FOR DATABASE `sci-fi-books`; -CREATE ALIAS `library`.`romance` FOR DATABASE `romance-books` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password'; -CREATE COMPOSITE DATABASE garden; -CREATE DATABASE `perennial-flowers` //// Available database aliases can be seen using `SHOW ALIASES FOR DATABASE`. @@ -309,7 +313,7 @@ When a `YIELD *` clause is provided, the full set of columns is returned. A summary of all available database aliases can be displayed using the command `SHOW ALIASES FOR DATABASE`. .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIASES FOR DATABASE ---- @@ -336,7 +340,7 @@ SHOW ALIASES FOR DATABASE To list just one database alias, the `SHOW ALIASES` command takes an alias name; .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIAS films FOR DATABASES ---- @@ -353,7 +357,7 @@ SHOW ALIAS films FOR DATABASES |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIAS library.romance FOR DATABASES ---- @@ -377,7 +381,7 @@ SHOW ALIAS library.romance FOR DATABASES ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIASES FOR DATABASE YIELD * ---- @@ -405,8 +409,9 @@ SHOW ALIASES FOR DATABASE YIELD * The number of database aliases can be seen using a `count()` aggregation with `YIELD` and `RETURN`. +//Skip testing this example because we skip remote aliases in testing, so the count is not the same. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- SHOW ALIASES FOR DATABASE YIELD * RETURN count(*) as count @@ -429,7 +434,7 @@ RETURN count(*) as count It is possible to filter and sort the results by using `YIELD`, `ORDER BY` and `WHERE`. .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIASES FOR DATABASE YIELD name, url, database ORDER BY database @@ -470,7 +475,7 @@ The required privileges are described xref::access-control/dbms-administration.a |=== | Syntax | Comment | -[source, cypher, role=noplay] +[source, syntax, role=noheader] ----- CREATE [OR REPLACE] ALIAS [compositeDatabaseName.]aliasName [IF NOT EXISTS] FOR DATABASE targetName [PROPERTIES "{" key: value[, ...] "}"] @@ -478,7 +483,7 @@ CREATE [OR REPLACE] ALIAS [compositeDatabaseName.]aliasName [IF NOT EXISTS] FOR | Create a local alias. | -[source, cypher, role=noplay] +[source, syntax, role=noheader] ----- CREATE [OR REPLACE] ALIAS [compositeDatabaseName.]aliasName [IF NOT EXISTS] FOR DATABASE targetName AT 'url' USER username PASSWORD 'password' @@ -516,7 +521,7 @@ Local aliases are created with a target database. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2021` ---- @@ -531,7 +536,7 @@ When a local database alias has been created, it will show up in the `aliases` c .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW DATABASE `northwind` ---- @@ -547,7 +552,7 @@ SHOW DATABASE `northwind` |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIAS `northwind` FOR DATABASE ---- @@ -571,7 +576,7 @@ Local database aliases can also be given properties. These properties can then be used in queries with the xref::functions/graph.adoc#functions-graph-propertiesByName[`graph.propertiesByName()` function]. .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS `northwind-2022` FOR DATABASE `northwind-graph-2022` @@ -587,7 +592,7 @@ Rows: 0 The properties are then shown in the `SHOW ALIASES FOR DATABASE YIELD ...` command. .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIAS `northwind-2022` FOR DATABASE YIELD name, properties ---- @@ -610,7 +615,7 @@ SHOW ALIAS `northwind-2022` FOR DATABASE YIELD name, properties Adding a local database alias with the same name as an existing local or remote alias will do nothing with the `IF NOT EXISTS` clause but fail without it. .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS `northwind` IF NOT EXISTS FOR DATABASE `northwind-graph-2020` ---- @@ -630,7 +635,7 @@ It is also possible to replace a database alias. The old alias may be either local or remote. .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE OR REPLACE ALIAS `northwind` FOR DATABASE `northwind-graph-2020` ---- @@ -644,13 +649,13 @@ Rows: 0 This is equivalent to running the following two queries consecutively: .Query -[source, cypher, indent=0] +[source, cypher] ---- DROP ALIAS `northwind` IF EXISTS FOR DATABASE ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020` ---- @@ -672,8 +677,10 @@ Both check for any remote or local database aliases. .+Creating remote database aliases+ ====== +//Skip testing all remote examples because it requires a lot of configuration, both server and client side. + .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" @@ -690,7 +697,7 @@ Rows: 0 When a database alias pointing to a remote database has been created, its details can be shown with the `SHOW ALIASES FOR DATABASE` command. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- SHOW ALIAS `remote-northwind` FOR DATABASE @@ -714,7 +721,7 @@ It is possible to override the default driver settings per database alias, which The full list of supported driver settings can be seen xref::aliases.adoc#remote-alias-driver-settings[here]. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" @@ -735,7 +742,7 @@ Rows: 0 When a database alias pointing to a remote database has been created, its details can be shown with the `SHOW ALIASES FOR DATABASE` command. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- SHOW ALIAS `remote-with-driver-settings` FOR DATABASE YIELD * ---- @@ -758,7 +765,7 @@ Just as the local database aliases, the remote database aliases can be given pro These properties can then be used in queries with the xref::functions/graph.adoc#functions-graph-propertiesByName[`graph.propertiesByName()` function]. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE ALIAS `remote-northwind-2021` FOR DATABASE `northwind-graph-2021` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password' @@ -774,7 +781,7 @@ Rows: 0 The properties are then shown in the `SHOW ALIASES FOR DATABASE YIELD ...` command. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- SHOW ALIAS `remote-northwind-2021` FOR DATABASE YIELD name, properties ---- @@ -803,7 +810,7 @@ Create a database alias in a composite database by giving the name of the compos ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS garden.flowers FOR DATABASE `perennial-flowers` @@ -816,7 +823,7 @@ Rows: 0 ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS garden.trees FOR DATABASE trees AT 'neo4j+s://location:7687' @@ -832,7 +839,7 @@ Rows: 0 When a database alias has been created in a composite database, it will show up in the `constituents` column provided by the command `SHOW DATABASES` and in the `SHOW ALIASES FOR DATABASE` command. .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW DATABASE garden YIELD name, type, constituents ---- @@ -848,7 +855,7 @@ SHOW DATABASE garden YIELD name, type, constituents |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIASES FOR DATABASE WHERE name STARTS WITH 'garden' ---- @@ -871,13 +878,13 @@ SHOW ALIASES FOR DATABASE WHERE name STARTS WITH 'garden' Database aliases cannot point to a composite database. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- CREATE ALIAS yard FOR DATABASE garden ---- .Error message -[source, output, role="noheader", indent=0] +[source, output, role="noheader"] ---- Failed to create the specified database alias 'yard': Database 'garden' is composite. ---- @@ -888,18 +895,19 @@ Failed to create the specified database alias 'yard': Database 'garden' is compo == Altering database aliases //// +[source, cypher, role=test-setup] +---- CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020`; +CREATE ALIAS garden.flowers FOR DATABASE `perennial-flowers`; +---- + CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER alice PASSWORD 'password'; CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER alice PASSWORD 'password' DRIVER { connection_timeout: duration({ minutes: 1 }), connection_pool_max_size: 10 }; -CREATE ALIAS garden.flowers -FOR DATABASE `perennial-flowers`; -CREATE ALIAS garden.trees -FOR DATABASE trees AT 'neo4j+s://location:7687' -USER alice PASSWORD 'password' +CREATE ALIAS garden.trees FOR DATABASE trees AT 'neo4j+s://location:7687' USER alice PASSWORD 'password' //// Database aliases can be altered using `ALTER ALIAS` to change its database target, properties, url, user credentials, or driver settings. @@ -916,7 +924,7 @@ Local database aliases cannot be altered to remote aliases, or vice versa. |=== | Syntax | Comment | -[source, cypher, role=noplay] +[source, source, role=noheader] ----- ALTER ALIAS [compositeDatabaseName.]aliasName [IF EXISTS] SET DATABASE [TARGET targetName] @@ -927,7 +935,7 @@ ALTER ALIAS [compositeDatabaseName.]aliasName [IF EXISTS] SET DATABASE The clauses can be applied in any order, while at least one clause needs to be set. | -[source, cypher, role=noplay] +[source, source, role=noheader] ----- ALTER ALIAS [compositeDatabaseName.]aliasName [IF EXISTS] SET DATABASE [TARGET targetName AT 'url'] @@ -949,7 +957,7 @@ Example of altering a local database alias target. .Query -[source, cypher, indent=0] +[source, cypher] ---- ALTER ALIAS `northwind` SET DATABASE TARGET `northwind-graph-2021` @@ -964,7 +972,7 @@ Rows: 0 When a local database alias has been altered, it will show up in the `aliases` column for the target database provided by the command `SHOW DATABASES`. .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW DATABASE `northwind-graph-2021` ---- @@ -989,7 +997,7 @@ SHOW DATABASE `northwind-graph-2021` Example of altering a remote database alias target. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- ALTER ALIAS `remote-northwind` SET DATABASE TARGET `northwind-graph-2020` AT "neo4j+s://other-location:7687" @@ -1011,7 +1019,7 @@ Example of altering a remote database alias credentials and driver settings. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- ALTER ALIAS `remote-with-driver-settings` SET DATABASE USER bob @@ -1043,7 +1051,7 @@ In this case, by not repeating the driver setting `connection_pool_max_size` the Example of altering a remote database alias to remove all custom driver settings. .Query -[source, cypher, indent=0] +[source, cypher] ---- ALTER ALIAS `movie scripts` SET DATABASE DRIVER {} @@ -1061,7 +1069,7 @@ Rows: 0 Examples of altering local and remote database alias properties. .Query -[source, cypher, indent=0] +[source, cypher] ---- ALTER ALIAS `motion pictures` SET DATABASE PROPERTIES { nameContainsSpace: true, moreInfo: 'no, not really' } ---- @@ -1073,7 +1081,7 @@ Rows: 0 ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- ALTER ALIAS `movie scripts` SET DATABASE PROPERTIES { nameContainsSpace: true } ---- @@ -1092,7 +1100,7 @@ The updated properties can then be used in queries with the xref::functions/grap Examples of altering local and remote database alias in composite databases. .Query -[source, cypher, indent=0] +[source, cypher] ---- ALTER ALIAS garden.flowers SET DATABASE PROPERTIES { perennial: true } ---- @@ -1104,7 +1112,7 @@ Rows: 0 ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- ALTER ALIAS garden.trees SET DATABASE TARGET updatedTrees AT 'neo4j+s://location:7687' PROPERTIES { treeVersion: 2 } ---- @@ -1123,7 +1131,7 @@ The changes for all database aliases will show up in the `SHOW ALIASES FOR DATAB .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIASES FOR DATABASE YIELD * WHERE name IN ['northwind', 'remote-northwind', 'remote-with-driver-settings', 'movie scripts', @@ -1156,7 +1164,7 @@ Appending `IF EXISTS` to the command ensures that no error is returned and nothi .Query -[source, cypher, indent=0] +[source, cypher] ---- ALTER ALIAS `no-alias` IF EXISTS SET DATABASE TARGET `northwind-graph-2021` ---- @@ -1174,23 +1182,24 @@ ALTER ALIAS `no-alias` IF EXISTS SET DATABASE TARGET `northwind-graph-2021` == Deleting database aliases //// +[source, cypher, role=test-setup] +---- CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2021`; +CREATE ALIAS `northwind-2022` FOR DATABASE `northwind-graph-2022` PROPERTIES { newestNorthwind: true, index: 3 }; +CREATE ALIAS `remote-northwind-2021` +CREATE ALIAS garden.flowers FOR DATABASE `perennial-flowers` +PROPERTIES { perennial: true }; +---- + +FOR DATABASE `northwind-graph-2021` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password' +PROPERTIES { newestNorthwind: false, index: 6 }; CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://other-location:7687" USER alice PASSWORD 'password'; CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER bob PASSWORD 'newPassword' DRIVER { connection_timeout: duration({ minutes: 1 }), logging_level: "debug" }; -CREATE ALIAS `northwind-2022` FOR DATABASE `northwind-graph-2022` PROPERTIES { newestNorthwind: true, index: 3 }; -CREATE ALIAS `remote-northwind-2021` -FOR DATABASE `northwind-graph-2021` AT 'neo4j+s://location:7687' -USER alice PASSWORD 'password' -PROPERTIES { newestNorthwind: false, index: 6 }; -CREATE ALIAS garden.flowers -FOR DATABASE `perennial-flowers` -PROPERTIES { perennial: true }; -CREATE ALIAS garden.trees -FOR DATABASE updatedTrees AT 'neo4j+s://location:7687' +CREATE ALIAS garden.trees FOR DATABASE updatedTrees AT 'neo4j+s://location:7687' USER alice PASSWORD 'password' PROPERTIES { treeVersion: 2 } //// @@ -1207,7 +1216,7 @@ Delete a local database alias. .Query -[source, cypher, indent=0] +[source, cypher] ---- DROP ALIAS `northwind` FOR DATABASE ---- @@ -1221,7 +1230,7 @@ Rows: 0 When a database alias has been deleted, it will no longer show up in the `aliases` column provided by the command `SHOW DATABASES`. .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW DATABASE `northwind-graph-2021` ---- @@ -1246,7 +1255,7 @@ SHOW DATABASE `northwind-graph-2021` Delete a remote database alias. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- DROP ALIAS `remote-northwind` FOR DATABASE ---- @@ -1264,7 +1273,7 @@ Rows: 0 Delete an alias in a composite database. .Query -[source, cypher, indent=0] +[source, cypher] ---- DROP ALIAS garden.flowers FOR DATABASE ---- @@ -1278,7 +1287,7 @@ Rows: 0 When a database alias has been deleted, it will no longer show up in the `SHOW ALIASES FOR DATABASE` command. .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW ALIASES FOR DATABASE ---- @@ -1308,7 +1317,7 @@ The `DROP ALIAS` command is optionally idempotent, with the default behavior to Inserting `IF EXISTS` after the alias name ensures that no error is returned and nothing happens should the alias not exist. .Query -[source, cypher, indent=0] +[source, cypher] ---- DROP ALIAS `northwind` IF EXISTS FOR DATABASE ---- @@ -1325,12 +1334,14 @@ DROP ALIAS `northwind` IF EXISTS FOR DATABASE [[alias-management-escaping]] == Alias names and escaping //// -Set-up -CREATE DATABASE `northwind-graph` -CREATE COMPOSITE DATABASE `my-composite-database-with-dashes` -CREATE COMPOSITE DATABASE `my.composite.database.with.dots` -CREATE COMPOSITE DATABASE mySimpleCompositeDatabase -CREATE COMPOSITE DATABASE `myCompositeDatabase.withDot` +[source, cypher, role=test-setup] +---- +CREATE DATABASE `northwind-graph`; +CREATE COMPOSITE DATABASE `my-composite-database-with-dashes`; +CREATE COMPOSITE DATABASE `my.composite.database.with.dots`; +CREATE COMPOSITE DATABASE mySimpleCompositeDatabase; +CREATE COMPOSITE DATABASE `myCompositeDatabase.withDot`; +---- //// Database alias names are subject to the xref::syntax/naming.adoc[standard Cypher restrictions on valid identifiers]. @@ -1361,7 +1372,7 @@ The composite database name and the database alias name need to be escaped indiv The following example creates a database alias named `my alias with spaces` as a constituent in the composite database named `my-composite-database-with-dashes`: .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS `my-composite-database-with-dashes`.`my alias with spaces` FOR DATABASE `northwind-graph` ---- @@ -1375,7 +1386,7 @@ Rows: 0 When not escaped individually, a database alias with the full name `my alias with.dots and spaces` gets created instead: .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS `my alias with.dots and spaces` FOR DATABASE `northwind-graph` ---- @@ -1391,11 +1402,13 @@ Rows: 0 .+Handling multiple dots+ ====== -Database alias names may also include dots. +//Examples where dots are not separators between composite name and alias name are impossible to test, because the right escaping cannot be inferred automatically. + +Database alias names may also include dots. Though these always need to be escaped in order to avoid ambiguity with the composite database and database alias split character. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE ALIAS `my.alias.with.dots` FOR DATABASE `northwind-graph` ---- @@ -1407,7 +1420,7 @@ Rows: 0 ---- .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE ALIAS `my.composite.database.with.dots`.`my.other.alias.with.dots` FOR DATABASE `northwind-graph` ---- @@ -1423,11 +1436,11 @@ Rows: 0 .+Single dots and local database aliases+ label:deprecated[] ====== There is a special case for local database aliases with a single dot without any existing composite database. -If a composite database `some` exists, the query below will create a database alias named `alias` within the composite database `some`. +If a composite database `some` exists, the query below will create a database alias named `alias` within the composite database `some`. If no such database exists, however, the same query will instead create a database alias named `some.alias`: .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE ALIAS some.alias FOR DATABASE `northwind-graph` ---- @@ -1449,7 +1462,7 @@ When the given parameter includes dots, the first dot will be considered the div Consider the query: .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE ALIAS $aliasname FOR DATABASE `northwind-graph` ---- @@ -1457,7 +1470,7 @@ CREATE ALIAS $aliasname FOR DATABASE `northwind-graph` And parameter .Parameters -[source,javascript, indent=0] +[source, javascript] ---- { "aliasname": "mySimpleCompositeDatabase.myAlias" @@ -1471,14 +1484,14 @@ On the contrary, a database alias `myalias` cannot be created in composite `myco Consider the same query but with the following parameter: .Parameters -[source,javascript, indent=0] +[source, javascript] ---- { "aliasname": "myCompositeDatabase.withDot.myAlias" } ---- -Since the first dot will be used as a divider, the command will attempt to create the database alias `withdot.myalias` in the composite database `mycompositedatabase`. +Since the first dot will be used as a divider, the command will attempt to create the database alias `withdot.myalias` in the composite database `mycompositedatabase`. If `mycompositedatabase` doesn't exist, the command will create a database alias with the name `mycompositedatabase.withdot.myalias`, which is not part of any composite database. In these cases, it is recommended to avoid parameters and explicitly escape the composite database name and alias name separately to avoid ambiguity. @@ -1493,7 +1506,7 @@ Further special handling with parameters is needed for database aliases and simi Consider the set-up: .Query -[source, cypher, indent=0, role="noheader"] +[source, cypher, role="noheader test-skip"] ---- CREATE COMPOSITE DATABASE foo CREATE ALIAS `foo.bar` FOR DATABASE `northwind-graph` @@ -1504,13 +1517,13 @@ The alias `foo.bar` does not belong to the composite database `foo`. Dropping this alias using parameters fails with an error about a missing alias: .Query -[source, cypher, indent=0] +[source, cypher] ---- DROP ALIAS $aliasname FOR DATABASE ---- .Parameters -[source,javascript, indent=0] +[source, javascript] ---- { "aliasname": "foo.bar" @@ -1518,7 +1531,7 @@ DROP ALIAS $aliasname FOR DATABASE ---- .Error message -[source, output, role="noheader", indent=0] +[source, output, role="noheader"] ---- Failed to delete the specified database alias 'foo.bar': Database alias does not exist. ---- From c704e47c83ee8bc6d40da96991f1b06fb5c848b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:36:55 +0100 Subject: [PATCH 069/383] fix link on SHOW PROCEDURES page to ops manual (#331) --- modules/ROOT/pages/clauses/listing-procedures.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/clauses/listing-procedures.adoc b/modules/ROOT/pages/clauses/listing-procedures.adoc index f3540f541..cfdfb28a1 100644 --- a/modules/ROOT/pages/clauses/listing-procedures.adoc +++ b/modules/ROOT/pages/clauses/listing-procedures.adoc @@ -214,7 +214,7 @@ SHOW PROCEDURES |=== The above table only displays the first 15 results of the query. -For a full list of all built-in procedures in Neo4j, visit the {neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures#/#_list_of_procedures[Operations Manual -> List of procedures]. +For a full list of all built-in procedures in Neo4j, visit the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures#/#_list_of_procedures[Operations Manual -> List of procedures]. == Listing procedures with filtering on output columns From 1fc3f95517e23f53d8a24f197363be926bf9f41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 25 Jan 2023 09:29:44 +0100 Subject: [PATCH 070/383] Update the DELETE page (#330) - Update example to align with movie theme - New Arrows.app graph - Re-order examples - editorial update --- modules/ROOT/images/graph_delete_clause.svg | 63 +--------- modules/ROOT/pages/clauses/delete.adoc | 125 ++++++++++---------- 2 files changed, 65 insertions(+), 123 deletions(-) diff --git a/modules/ROOT/images/graph_delete_clause.svg b/modules/ROOT/images/graph_delete_clause.svg index 0ccbc1b16..d24257759 100644 --- a/modules/ROOT/images/graph_delete_clause.svg +++ b/modules/ROOT/images/graph_delete_clause.svg @@ -1,62 +1 @@ - - - - - - -L - - - -N0 - -Person - -age = 36 -name = 'Andy' - - - -N1 - -Person - -age = 25 -name = 'Timothy' - - - -N0->N1 - - -KNOWS - - - -N2 - -Person - -age = 34 -name = 'Peter' - - - -N0->N2 - - -KNOWS - - - -N3 - -Person - -name = 'UNKNOWN' - - - +ACTED_INACTED_INACTED_INPersonname:Keanu ReevesPersonname:Laurence FishburneMovietitle:The MatrixPersonname:Carrie-Anne MossPersonname:Tom Hanks \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/delete.adoc b/modules/ROOT/pages/clauses/delete.adoc index cdd5b739f..9f1e5cd34 100644 --- a/modules/ROOT/pages/clauses/delete.adoc +++ b/modules/ROOT/pages/clauses/delete.adoc @@ -8,119 +8,122 @@ The `DELETE` clause is used to delete nodes, relationships or paths. -- -For removing properties and labels, see xref::clauses/remove.adoc[REMOVE]. -Remember that you cannot delete a node without also deleting relationships that start or end on said node. -Either explicitly delete the relationships, or use `DETACH DELETE`. +For removing properties and labels, see the xref::clauses/remove.adoc[REMOVE] clause. -The examples start out with the following database: +It is not possible to delete nodes with relationships connected to them without also deleting the relationships. +This can be done by either explicitly deleting specific relationships, or by using the `DETACH DELETE` clause. -image:graph_delete_clause.svg[] +== Example graph -//// -CREATE - (a:Person {name: 'Andy', age: 36}), - (p:Person {name: 'Timothy', age: 25}), - (t:Person {name: 'Peter', age: 34}), - (z:Person {name: 'UNKNOWN'}), - (a)-[:KNOWS]->(t), - (a)-[:KNOWS]->(p) -//// +The following graph is used for the examples below. +It shows four actors, three of whom `ACTED_IN` the `Movie` `The Matrix` (`Keanu Reeves`, `Carrie-Anne Moss`, and `Laurence Fishburne`), and one actor who did not act in it (`Tom Hanks`). + +image::graph_delete_clause.svg[width="500",role="middle"] +To recreate the graph, run the following query in an empty Neo4j database: -[[delete-delete-single-node]] +[source, cypher, role=test-setup] +---- +CREATE + (keanu:Person {name: 'Keanu Reever'}), + (laurence:Person {name: 'Laurence Fishburne'}), + (carrie:Person {name: 'Carrie-Anne Moss'}), + (tom:Person {name: 'Tom Hanks'}), + (theMatrix:Movie {title: 'The Matrix'}), + (keanu)-[:ACTED_IN]->(theMatrix), + (laurence)-[:ACTED_IN]->(theMatrix), + (carrie)-[:ACTED_IN]->(theMatrix) +---- + +[[delete-single-node]] == Delete single node -To delete a node, use the `DELETE` clause. +To delete a single node, use the `DELETE` clause: .Query [source, cypher, indent=0] ---- -MATCH (n:Person {name: 'UNKNOWN'}) +MATCH (n:Person {name: 'Tom Hanks'}) DELETE n ---- +This deletes the `Person` node `Tom Hanks`. +This query is only possible to run on nodes without any relationships connected to them. + .Result [role="queryresult",options="footer",cols="1*() +DELETE r ---- +This deletes all outgoing `ACTED_IN` relationships from the `Person` node `Laurence Fishburne`, without deleting the node. + .Result [role="queryresult",options="footer",cols="1* Fine-grained access control]. +The `DETACH DELETE` clause may not be permitted to users with restricted security privileges. +For more information, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/access-control#detach-delete-restricted-user[Operations Manual -> Fine-grained access control]. ==== -[[delete-delete-relationships-only]] -== Delete relationships only +[[delete-all-nodes-and-relationships]] +== Delete all nodes and relationships -It is also possible to delete relationships only, leaving the node(s) otherwise unaffected. +It is possible to delete all nodes and relationships in a graph. .Query [source, cypher, indent=0] ---- -MATCH (n {name: 'Andy'})-[r:KNOWS]->() -DELETE r +MATCH (n) +DETACH DELETE n ---- -This deletes all outgoing `KNOWS` relationships from the node with the name *'Andy'*. - .Result [role="queryresult",options="footer",cols="1* Date: Wed, 25 Jan 2023 11:38:10 +0100 Subject: [PATCH 071/383] Fix examples on WHERE page (#336) --- modules/ROOT/pages/clauses/where.adoc | 80 +++++++-------------------- 1 file changed, 19 insertions(+), 61 deletions(-) diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index 211e6acf5..8b282081f 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -65,21 +65,26 @@ Both results and performance may be impacted if the `WHERE` is put inside the wr xref::indexes-for-search-performance.adoc[Indexes] may be used to optimize queries using `WHERE` in a variety of cases. ==== +== Example graph + The following graph is used for the examples below: image:graph_where_clause.svg[] -//// -CREATE (andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE +(andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), (timothy:Person {name: 'Timothy', age: 25, address: 'Sweden/Malmo'}), (peter:Person {name: 'Peter', age: 35, email: 'peter_n@example.com'}), (andy)-[:KNOWS {since: 2012}]->(timothy), (andy)-[:KNOWS {since: 1999}]->(peter), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) -//// - +(fido)-[:HAS_TOY]->(:Toy {name:'Banana'}) +---- [[query-where-basic]] == Basic usage @@ -421,17 +426,10 @@ The name and age for the *'Peter'* node are returned because his name does not e Cypher supports filtering using regular expressions. The regular expression syntax is inherited from the link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html[Java regular expressions]. -This includes support for flags that change how strings are matched, including case-insensitive `(?i)`, multiline `(?m)` and dotall `(?s)`. -Flags are given at the beginning of the regular expression, for example: +This includes support for flags that change how strings are matched, including case-insensitive `(?i)`, multiline `(?m)`, and dotall `(?s)`. -.Query -[source, cypher, role="noheader"] ----- -MATCH (n) WHERE n.name =~ '(?i)Lon.*' -RETURN n ----- - -will return nodes with name `'London'` or with name `'LonDoN'`. +Flags are given at the beginning of the regular expression. +For an example of a regular expression flag given at the beginning of a pattern, see the xref::clauses/where.adoc#case-insensitive-regular-expressions[case-insensitive regular expression] section. [[matching-using-regular-expressions]] === Matching using regular expressions @@ -834,17 +832,6 @@ The name and age values of nodes having a name property lexicographically betwee .+WHERE+ ====== -//// -CREATE (andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), -(timothy:Person {name: 'Timothy', age: 25, address: 'Sweden/Malmo'}), -(peter:Person {name: 'Peter', age: 35, email: 'peter_n@example.com'}), -(andy)-[:KNOWS {since: 2012}]->(timothy), -(andy)-[:KNOWS {since: 1999}]->(peter), -(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) -//// - .Query [source, cypher] ---- @@ -871,26 +858,19 @@ However, it cannot be used inside of variable length relationships, as this woul For example: -//// -CREATE (andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), -(timothy:Person {name: 'Timothy', age: 25, address: 'Sweden/Malmo'}), -(peter:Person {name: 'Peter', age: 35, email: 'peter_n@example.com'}), -(andy)-[:KNOWS {since: 2012}]->(timothy), -(andy)-[:KNOWS {since: 1999}]->(peter), -(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) -//// - .Query -[source, cypher] +[source, cypher, role=test-fail] ---- WITH 2000 AS minYear MATCH (a:Person)-[r:KNOWS*1..3 WHERE r.since > b.yearOfBirth]->(b:Person) RETURN r.since ---- -// Error +.Error message +[source, output] +---- +Relationship pattern predicates are not supported for variable-length relationships. +---- ====== @@ -902,17 +882,6 @@ Please note that it is strictly equivalent to using a standalone `WHERE` sub-cla .+WHERE+ ====== -//// -CREATE (andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), -(timothy:Person {name: 'Timothy', age: 25, address: 'Sweden/Malmo'}), -(peter:Person {name: 'Peter', age: 35, email: 'peter_n@example.com'}), -(andy)-[:KNOWS {since: 2012}]->(timothy), -(andy)-[:KNOWS {since: 1999}]->(peter), -(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) -//// - .Query [source, cypher] ---- @@ -939,17 +908,6 @@ Relationship pattern predicates can also be used inside pattern comprehensions, .+WHERE+ ====== -//// -CREATE (andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), -(timothy:Person {name: 'Timothy', age: 25, address: 'Sweden/Malmo'}), -(peter:Person {name: 'Peter', age: 35, email: 'peter_n@example.com'}), -(andy)-[:KNOWS {since: 2012}]->(timothy), -(andy)-[:KNOWS {since: 1999}]->(peter), -(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) -//// - .Query [source, cypher] ---- From 20af5f13a5cc813138ec9ba1db8590b91cf2b164 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Wed, 25 Jan 2023 13:27:14 +0100 Subject: [PATCH 072/383] Add note about server commands and composite databases (#333) The `allowedDatabases` and `deniedDatabases` doesn't apply to composite databases. Companion PR to https://github.com/neo4j/docs-operations/pull/386 Co-authored-by: Phil Wright <95368282+phil198@users.noreply.github.com> --- .../ROOT/pages/access-control/manage-servers.adoc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index b892fd9eb..2c69073f0 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -305,6 +305,12 @@ This may not be specified in combination with `deniedDatabases`. This may not be specified in combination with `allowedDatabases`. |=== +[NOTE] +==== +Composite databases are ignored by both `allowedDatabases` and `deniedDatabases`. +The composite databases are available everywhere and hold no data on their own. +==== + [[server-management-alter-server]] == Modifying servers @@ -335,6 +341,12 @@ This may not be specified in combination with `deniedDatabases`. This may not be specified in combination with `allowedDatabases`. |=== +[NOTE] +==== +Composite databases are ignored by both `allowedDatabases` and `deniedDatabases`. +The composite databases are available everywhere and hold no data on their own. +==== + [[server-management-rename-server]] == Renaming servers From 4d4325ba2b9c00af4cb0e94987b64bfd1f7e467c Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Wed, 25 Jan 2023 15:53:42 +0000 Subject: [PATCH 073/383] Update Neo4j version --- antora.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antora.yml b/antora.yml index 08df6a409..a4d5049ed 100644 --- a/antora.yml +++ b/antora.yml @@ -7,5 +7,5 @@ nav: asciidoc: attributes: neo4j-version: '5' - neo4j-version-minor: '5.4' - neo4j-version-exact: '5.4.0' + neo4j-version-minor: '5.5' + neo4j-version-exact: '5.5.0' From 133a0f4a1fff286d85b6bfd14eece667f2742a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 26 Jan 2023 09:13:46 +0100 Subject: [PATCH 074/383] fix examples in SHOW TRANSACTIONS page (#339) --- .../pages/clauses/transaction-clauses.adoc | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/modules/ROOT/pages/clauses/transaction-clauses.adoc b/modules/ROOT/pages/clauses/transaction-clauses.adoc index 20aa69a7c..d7ad742e4 100644 --- a/modules/ROOT/pages/clauses/transaction-clauses.adoc +++ b/modules/ROOT/pages/clauses/transaction-clauses.adoc @@ -216,7 +216,7 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax List transactions on the current server:: -[source, cypher, role="noheader", indent=0] +[source, syntax, role="noheader"] ---- SHOW TRANSACTION[S] [transaction-id[,...]] [YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -242,7 +242,7 @@ To list all available transactions with the default outputs, use the `SHOW TRANS If all outputs are required, use `SHOW TRANSACTIONS YIELD *`. .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- SHOW TRANSACTIONS ---- @@ -265,7 +265,7 @@ The listed transactions can be filtered by using the `WHERE` clause. For example, getting the databases for all transactions where the currently executing query contains `'Mark'`: .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- SHOW TRANSACTIONS YIELD database, currentQuery WHERE currentQuery contains 'Mark' ---- @@ -285,7 +285,7 @@ Several of the outputs have the `duration` type, which can be hard to read. They can instead be returned in a more readable format: .Query -[source, cypher] +[source, cypher, role=test-result-skip] ---- SHOW TRANSACTIONS YIELD transactionId, elapsedTime, cpuTime, waitTime, idleTime, @@ -318,7 +318,7 @@ RETURN It is possible to specify which transactions to return in the list by transaction ID. .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- SHOW TRANSACTIONS "neo4j-transaction-3" ---- @@ -378,7 +378,7 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax Terminate transactions by ID on the current server:: -[source, cypher, role="noheader", indent=0] +[source, syntax, role="noheader"] ---- TERMINATE TRANSACTION[S] transaction_id[, ...] [YIELD { * \| field[, ...] } @@ -407,7 +407,7 @@ All users may terminate their own currently executing transactions. To end running transactions without waiting for them to complete on their own, use the `TERMINATE TRANSACTIONS` command. .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- TERMINATE TRANSACTIONS "neo4j-transaction-1","neo4j-transaction-2" ---- @@ -434,7 +434,7 @@ The output from the `TERMINATE TRANSACTIONS` command can be filtered using the ` For example, returning the transaction IDs and message for the transactions that did not terminate. .Query -[source, cypher] +[source, cypher, role=test-skip] ---- TERMINATE TRANSACTIONS "neo4j-transaction-1","neo4j-transaction-2" YIELD transactionId, message @@ -458,17 +458,15 @@ WHERE message <> "Transaction terminated." In difference to `SHOW TRANSACTIONS`; the `TERMINATE TRANSACTIONS` does not allow `WHERE` without `YIELD`. .Query -[source, cypher] +[source, cypher, role=test-fail] ---- TERMINATE TRANSACTIONS "neo4j-transaction-1","neo4j-transaction-2" WHERE message <> "Transaction terminated." ---- -[source, error, role="noheader"] +.Error message ---- -`WHERE` is not allowed by itself, please use `TERMINATE TRANSACTION ... YIELD ... WHERE ...` instead (line 2, column 1 (offset: 67)) + -"WHERE message <> "Transaction terminated."" + - ^ +`WHERE` is not allowed by itself, please use `TERMINATE TRANSACTION ... YIELD ... WHERE ...` ---- ====== @@ -501,7 +499,7 @@ To terminate all transactions by a user, first find the transactions using `SHOW ====== .Query -[source, cypher] +[source, cypher, role=test-result-skip] ---- SHOW TRANSACTIONS YIELD transactionId AS txId, username AS user @@ -534,7 +532,7 @@ To terminate transactions that have been waiting for more than `30` minutes, fir The following example shows a transaction that has been waiting for `40` minutes. .Query -[source, cypher] +[source, cypher, role=test-result-skip] ---- SHOW TRANSACTIONS YIELD transactionId, waitTime @@ -564,7 +562,7 @@ To list remaining transactions by users whose transactions were terminated, firs ====== .Query -[source, cypher] +[source, cypher, role=test-result-skip] ---- TERMINATE TRANSACTION 'neo4j-transaction-1', 'neo4j-transaction-2' YIELD username AS terminatedUser @@ -596,7 +594,7 @@ To list other transactions by the same user as a given transaction, first find t ====== .Query -[source, cypher] +[source, cypher, role=test-result-skip] ---- SHOW TRANSACTION 'neo4j-transaction-1' YIELD username AS originalUser, transactionId AS originalTxId From 067d13ed25b8452295b3f3f966e17de4f5d65ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:46:50 +0100 Subject: [PATCH 075/383] fix MERGE examples for testing (#340) Co-authored-by: JPryce-Aklundh Co-authored-by: Stefano Ottolenghi --- modules/ROOT/pages/clauses/merge.adoc | 182 +++++++++----------------- 1 file changed, 65 insertions(+), 117 deletions(-) diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index 751bb34f7..e6805ac5a 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -65,20 +65,23 @@ If there are multiple matches, they will all be passed on to later stages of the The last part of `MERGE` is the `ON CREATE` and `ON MATCH`. These allow a query to express additional changes to the properties of a node or relationship, depending on if the element was matched (`MATCH`) in the database or if it was created (`CREATE`). +== Example graph + The following graph is used for the examples below: image:graph_merge_clause.svg[] -//// -CREATE CONSTRAINT FOR (person:Person) REQUIRE person.name IS UNIQUE; -CREATE CONSTRAINT FOR (movie:Movie) REQUIRE movie.title IS UNIQUE; +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- CREATE (charlie:Person {name: 'Charlie Sheen', bornIn: 'New York', chauffeurName: 'John Brown'}), - (martin:Person {name: 'Martin Sheen', bornIn: 'Ohio', chauffeurName: 'Bob Brown'}), + (martin:Person {name: 'Martin Sheen', bornIn: 'Ohio', chauffeurName: 'Bob Brown'}), (michael:Person {name: 'Michael Douglas', bornIn: 'New Jersey', chauffeurName: 'John Brown'}), - (oliver:Person {name: 'Oliver Stone', bornIn: 'New York', chauffeurName: 'Bill White'}), - (rob:Person {name: 'Rob Reiner', bornIn: 'New York', chauffeurName: 'Ted Green'}), - (wallStreet:Movie {title: 'Wall Street'}), + (oliver:Person {name: 'Oliver Stone', bornIn: 'New York', chauffeurName: 'Bill White'}), + (rob:Person {name: 'Rob Reiner', bornIn: 'New York', chauffeurName: 'Ted Green'}), + (wallStreet:Movie {title: 'Wall Street'}), (theAmericanPresident:Movie {title: 'The American President'}), (charlie)-[:ACTED_IN]->(wallStreet), (martin)-[:ACTED_IN]->(wallStreet), @@ -88,8 +91,7 @@ CREATE (oliver)-[:ACTED_IN]->(wallStreet), (rob)-[:ACTED_IN]->(theAmericanPresident), (charlie)-[:FATHER]->(martin) -//// - +---- [[query-merge-node-derived]] == Merge nodes @@ -100,7 +102,7 @@ CREATE Merging a single node with the given label. .Query -[source, cypher, indent=0] +[source, cypher] ---- MERGE (robert:Critic) RETURN robert, labels(robert) @@ -112,10 +114,7 @@ A new node is created because there are no nodes labeled `Critic` in the databas [role="queryresult",options="header,footer",cols="2*+ -3+d|Rows: 1 + -Nodes created: 1 + -Properties set: 2 + -Labels added: 1 +| +"Keanu Reeves"+ | +1655200902354+ | +1674655352124+ |=== @@ -306,7 +279,7 @@ Labels added: 1 If multiple properties should be set, simply separate them with commas. .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- MERGE (person:Person) ON MATCH @@ -325,8 +298,7 @@ RETURN person.name, person.found, person.lastAccessed | +"Michael Douglas"+ | +true+ | +1655200903558+ | +"Oliver Stone"+ | +true+ | +1655200903558+ | +"Rob Reiner"+ | +true+ | +1655200903558+ -3+d|Rows: 5 + -Properties set: 10 +| +"Keanu Reeves"+ | +true+ | +1655200903558+ |=== @@ -339,7 +311,7 @@ Properties set: 10 `MERGE` can be used to match or create a relationship. .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (charlie:Person {name: 'Charlie Sheen'}), @@ -356,7 +328,6 @@ Note that in order to match or create a relationship when using `MERGE`, at leas |=== | +charlie.name+ | +type(r)+ | +wallStreet.title+ | +"Charlie Sheen"+ | +"ACTED_IN"+ | +"Wall Street"+ -3+d|Rows: 1 |=== @@ -364,7 +335,7 @@ Note that in order to match or create a relationship when using `MERGE`, at leas === Merge on multiple relationships .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (oliver:Person {name: 'Oliver Stone'}), @@ -381,11 +352,7 @@ Instead, a new `'movie'` node is created. [role="queryresult",options="header,footer",cols="1*(chauffeur:Chauffeur {name: person.chauffeurName}) @@ -475,16 +436,12 @@ This is in contrast to the example shown above in xref::clauses/merge.adoc#merge [role="queryresult",options="header,footer",cols="3* Date: Thu, 26 Jan 2023 12:39:15 +0100 Subject: [PATCH 076/383] skip test for deprecations page --- .../pages/deprecations-additions-removals-compatibility.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 7e7b9c8c7..fa19084b5 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -1,6 +1,7 @@ [[cypher-deprecations-additions-removals-compatibility]] = Deprecations, additions, and compatibility :description: all of the features that have been removed, deprecated, added, or extended in different Cypher versions. +:test-skip: true // all deprecations would fail. Cypher is a language that is constantly evolving. From 9958f9b84c05f8d21f4c30384dbc3fb56da8c852 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Thu, 26 Jan 2023 13:26:26 +0100 Subject: [PATCH 077/383] Fix syntax sections examples for tests (#344) --- modules/ROOT/pages/syntax/expressions.adoc | 13 ++++--- modules/ROOT/pages/syntax/patterns.adoc | 45 ++++++++++++---------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index b11b906d6..cc648d83c 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -92,6 +92,8 @@ CASE can only be used as part of RETURN or WITH if you want to use the result in The following graph is used for the examples below: //// +[source, cypher, role=test-setup] +---- CREATE (alice:A {name:'Alice', age: 38, eyes: 'brown'}), (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), @@ -103,6 +105,7 @@ CREATE (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) +---- //// image:graph3.svg[] @@ -115,8 +118,7 @@ If no match is found, the expression in the `ELSE` clause is returned. However, if there is no `ELSE` case and no match is found, `null` will be returned. -*Syntax:* -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- CASE test WHEN value THEN result @@ -178,8 +180,7 @@ If no match is found, the expression in the `ELSE` clause is returned. However, if there is no `ELSE` case and no match is found, `null` will be returned. -*Syntax:* -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- CASE WHEN predicate THEN result @@ -2091,8 +2092,8 @@ The following table displays whether the relationship type expression matches th |=== -Label expressions cannot be combined with label syntax. -For example, `:A:B&C` will throw an error. +Label expressions cannot be combined with label syntax. +For example, `:A:B&C` will throw an error. Instead, use either `:A&B&C` or `:A:B:C`. *Examples:* diff --git a/modules/ROOT/pages/syntax/patterns.adoc b/modules/ROOT/pages/syntax/patterns.adoc index 572759cf8..0476f28ce 100644 --- a/modules/ROOT/pages/syntax/patterns.adoc +++ b/modules/ROOT/pages/syntax/patterns.adoc @@ -38,7 +38,7 @@ A node is described using a pair of parentheses, and is typically given a name. For example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a) ---- @@ -55,7 +55,7 @@ A more powerful construct is a pattern that describes multiple nodes and relatio Cypher patterns describe relationships by employing an arrow between two nodes. For example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-->(b) ---- @@ -65,7 +65,7 @@ In this example, the two nodes are both named as `a` and `b` respectively, and t This manner of describing nodes and relationships can be extended to cover an arbitrary number of nodes and the relationships between them, for example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-->(b)<--(c) ---- @@ -75,7 +75,7 @@ Such a series of connected nodes and relationships is called a "path". Note that the naming of the nodes in these patterns is only necessary should one need to refer to the same node again, either later in the pattern or elsewhere in the Cypher query. If this is not necessary, then the name may be omitted, as follows: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-->()<--(c) ---- @@ -88,7 +88,7 @@ In addition to simply describing the shape of a node in the pattern, one can als The most simple attribute that can be described in the pattern is a label that the node must have. For example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a:User)-->(b) ---- @@ -96,7 +96,7 @@ For example: One can also describe a node that has multiple labels: // New in 5.0 -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a:User&Admin)-->(b) ---- @@ -110,14 +110,14 @@ Nodes and relationships are the fundamental structures in a graph. Neo4j uses pr Properties can be expressed in patterns using a map-construct: curly brackets surrounding a number of key-expression pairs, separated by commas. E.g. a node with two properties on it would look like: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a {name: 'Andy', sport: 'Brazilian Ju-Jitsu'}) ---- A relationship with expectations on it is given by: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[{blocked: false}]->(b) ---- @@ -138,7 +138,7 @@ The simplest way to describe a relationship is by using the arrow between two no Using this technique, you can describe that the relationship should exist and the directionality of it. If you don't care about the direction of the relationship, the arrow head can be omitted, as exemplified by: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)--(b) ---- @@ -147,7 +147,7 @@ As with nodes, relationships may also be given names. In this case, a pair of square brackets is used to break up the arrow and the variable is placed between. For example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[r]->(b) ---- @@ -155,7 +155,7 @@ For example: Much like labels on nodes, relationships can have types. To describe a relationship with a specific type, you can specify this as follows: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[r:REL_TYPE]->(b) ---- @@ -163,7 +163,7 @@ To describe a relationship with a specific type, you can specify this as follows Unlike labels, relationships can only have one type. But if we'd like to describe some data such that the relationship could have any one of a set of types, then they can all be listed in the pattern, separating them with the pipe symbol `|` like this: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[r:TYPE1|TYPE2]->(b) ---- @@ -175,7 +175,7 @@ For more information on how to use relationship type expressions, see xref:synta As with nodes, the name of the relationship can always be omitted, as exemplified by: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[:REL_TYPE]->(b) ---- @@ -189,7 +189,7 @@ Using the same variable name for relationships multiple times within a pattern i The following example is therefore not allowed. -[source, cypher, role=noplay] +[source, syntax] ---- ()-[r:REL_TYPE]-()-[r:REL_TYPE]-() ---- @@ -205,7 +205,7 @@ You can specify additional constraints by introducing a xref::clauses/where.adoc Rather than describing a long path using a sequence of many node and relationship descriptions in a pattern, many relationships (and the intermediate nodes) can be described by specifying a length in the relationship description of a pattern. For example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[*2]->(b) ---- @@ -213,7 +213,7 @@ For example: This describes a graph of three nodes and two relationships, all in one path (a path of length 2). This is equivalent to: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-->()-->(b) ---- @@ -221,7 +221,7 @@ This is equivalent to: A range of lengths can also be specified: such relationship patterns are called 'variable length relationships'. For example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[*3..5]->(b) ---- @@ -232,21 +232,21 @@ It describes a graph of either 4 nodes and 3 relationships, 5 nodes and 4 relati Either bound can be omitted. For example, to describe paths of length 3 or more, use: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[*3..]->(b) ---- To describe paths of length 5 or less, use: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[*..5]->(b) ---- Omitting both bounds is equivalent to specifying a minimum of 1, allowing paths of any positive length to be described: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- (a)-[*]->(b) ---- @@ -256,6 +256,8 @@ As a simple example, let's take the graph and query below: image:graph4.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (a {name: 'Anders'}), (b {name: 'Becky'}), (c {name: 'Cesar'}), @@ -269,6 +271,7 @@ CREATE (a {name: 'Anders'}), (b)-[:KNOWS]->(e), (c)-[:KNOWS]->(e), (d)-[:KNOWS]->(f) +----- //// .Query @@ -302,7 +305,7 @@ Under certain circumstances variable length relationships can be planned with an As described above, a series of connected nodes and relationships is called a "path". Cypher allows paths to be named using an identifer, as exemplified by: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- p = (a)-[*3..5]->(b) ---- From 4b1efe3918aaaf61cd194a5ef9b4afe7da9053ab Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Fri, 27 Jan 2023 08:42:05 +0100 Subject: [PATCH 078/383] Update use.adoc for test run (#341) --- modules/ROOT/pages/clauses/match.adoc | 2 +- modules/ROOT/pages/clauses/use.adoc | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index c5d01e365..de816bb06 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -74,7 +74,7 @@ The following graph is used for the examples below: image::graph_match_clause.svg[width="600",role="middle"] -To recreate the graph, run the following query in an empty Neo4j database: +To recreate the graph, run the following query in an empty Neo4j database: [source, cypher, role=test-setup] ---- diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index 5ca0d2169..946b89523 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -55,6 +55,12 @@ When executing queries against a composite database, the `USE` clause must only [[query-use-examples]] == Examples +To create the database `myDatabase`, run the following query in a Neo4j database: +[source, cypher, role=test-setup] +---- +CREATE DATABASE myDatabase; +---- + [[query-use-examples-query-graph]] === Query a graph @@ -62,20 +68,19 @@ When executing queries against a composite database, the `USE` clause must only In this example it is assumed that the DBMS contains a database named `myDatabase`: .Query -[source, cypher, indent=0] +[source, cypher] ---- USE myDatabase MATCH (n) RETURN n ---- - [[query-use-examples-query-composite-database-constituent-graph]] === Query a composite database constituent graph In this example it is assumed that the DBMS contains a composite database named `myComposite`, which includes an alias named `myConstituent`: .Query -[source, cypher, indent=0] +[source, cypher, role="test-skip"] ---- USE myComposite.myConstituent MATCH (n) RETURN n @@ -90,7 +95,7 @@ The built-in function `graph.byName()` can be used in the `USE` clause to resolv This example uses a composite database named `myComposite` that includes an alias named `myConstituent`: .Query -[source, cypher, indent=0] +[source, cypher, role="test-skip"] ---- USE graph.byName('myComposite.myConstituent') MATCH (n) RETURN n From 258bba60b1c24988fc68ba829eced9291e8eb362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:52:19 +0100 Subject: [PATCH 079/383] update SHOW FUNCTIONS examples for testing (#345) --- .../ROOT/pages/clauses/listing-functions.adoc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/ROOT/pages/clauses/listing-functions.adoc b/modules/ROOT/pages/clauses/listing-functions.adoc index 6963594fb..cf8b91d26 100644 --- a/modules/ROOT/pages/clauses/listing-functions.adoc +++ b/modules/ROOT/pages/clauses/listing-functions.adoc @@ -10,7 +10,6 @@ This section explains the `SHOW FUNCTIONS` command. Listing the available functions can be done with `SHOW FUNCTIONS`. - [NOTE] ==== The command `SHOW FUNCTIONS` returns only the default output. @@ -18,7 +17,7 @@ For a full output use the optional `YIELD` command. Full output: `SHOW FUNCTIONS YIELD *`. ==== -This command will produce a table with the following columns: +The `SHOW FUNCTIONS` command will produce a table with the following columns: .List functions output @@ -72,7 +71,7 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax List functions, either all or only built-in or user-defined:: -[source, syntax, role="noheader", indent=0] +[source, syntax, role="noheader"] ---- SHOW [ALL|BUILT IN|USER DEFINED] FUNCTION[S] [YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -87,7 +86,7 @@ When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be List functions that the current user can execute:: -[source, syntax, role="noheader", indent=0] +[source, syntax, role="noheader"] ---- SHOW [ALL|BUILT IN|USER DEFINED] FUNCTION[S] EXECUTABLE [BY CURRENT USER] [YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -125,7 +124,7 @@ If all columns are required, use `SHOW FUNCTIONS YIELD *`. .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- SHOW FUNCTIONS ---- @@ -218,6 +217,8 @@ SHOW FUNCTIONS 3+d|Rows: 20 |=== +The above table only displays the first 20 results of the query. +For a full list of all available functions in Cypher, see the chapter on xref::clauses/index.adoc[Functions]. == Listing functions with filtering on output columns @@ -227,7 +228,7 @@ A more flexible way is to use the `WHERE` clause. For example, getting the name of all built-in functions starting with the letter 'a': .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW BUILT IN FUNCTIONS YIELD name, isBuiltIn WHERE name STARTS WITH 'a' @@ -264,7 +265,7 @@ There are two options, how to use the `EXECUTABLE` clause. The first option, is to filter for the current user: .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- SHOW FUNCTIONS EXECUTABLE BY CURRENT USER YIELD * ---- @@ -352,7 +353,7 @@ Notice that the two `roles` columns are empty due to missing the xref::access-co The second option, is to filter for a specific user: .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- SHOW FUNCTIONS EXECUTABLE BY jake ---- From 12b47693b75281d44fa224a5ccb9a93602e54e77 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Fri, 27 Jan 2023 16:12:42 +0100 Subject: [PATCH 080/383] Fix examples in access control section (for tests) (#346) Skip 3 examples: 1. should not change current user password, or subsequent tests would fail 2. cannot re-create `admin` role. Cannot drop it before (as I did for other roles), because the testing user belongs to it and would crash. 3. cannot assign immutable privileges unless database meets specific requirements. ``` ============================================= short test summary info ============================================= SUBSKIP [1] test-cypher/test-cypher.py:77: Example with role=test-skip ../docs-cypher/modules/ROOT/pages/access-control/manage-users.adoc ALTER CURRENT USER SET PASSWORD FROM 'password1' TO 'password2' SUBSKIP [1] test-cypher/test-cypher.py:77: Example with role=test-skip ../docs-cypher/modules/ROOT/pages/access-control/built-in-roles.adoc CREATE ROLE admin SUBSKIP [1] test-cypher/test-cypher.py:77: Example with role=test-skip ../docs-cypher/modules/ROOT/pages/access-control/privileges-immutable.adoc DENY IMMUTABLE DATABASE MANAGEMENT ON DBMS TO PUBLIC ======================== 12 passed, 3 skipped, 372 warnings, 290 subtests passed in 2.40s ========================= ``` --- .../pages/access-control/built-in-roles.adoc | 37 ++++- .../database-administration.adoc | 14 +- .../access-control/dbms-administration.adoc | 150 ++++++++++++------ .../pages/access-control/limitations.adoc | 38 +++-- .../access-control/manage-privileges.adoc | 14 +- .../pages/access-control/manage-roles.adoc | 17 +- .../pages/access-control/manage-servers.adoc | 14 +- .../pages/access-control/manage-users.adoc | 12 +- .../access-control/privileges-immutable.adoc | 14 +- .../access-control/privileges-reads.adoc | 17 +- .../access-control/privileges-writes.adoc | 7 + 11 files changed, 242 insertions(+), 92 deletions(-) diff --git a/modules/ROOT/pages/access-control/built-in-roles.adoc b/modules/ROOT/pages/access-control/built-in-roles.adoc index 550f96e40..0c3674ae3 100644 --- a/modules/ROOT/pages/access-control/built-in-roles.adoc +++ b/modules/ROOT/pages/access-control/built-in-roles.adoc @@ -106,6 +106,13 @@ a|Rows: 5 [[access-control-built-in-roles-reader-recreate]] === Recreating the `reader` role +//// +[source, cypher, role=test-setup] +---- +DROP ROLE reader; +---- +//// + To restore the role to its original capabilities two steps are needed. First, execute `DROP ROLE reader`. Secondly, run these queries: @@ -168,6 +175,13 @@ a|Rows: 6 [[access-control-built-in-roles-editor-recreate]] === Recreating the `editor` role +//// +[source, cypher, role=test-setup] +---- +DROP ROLE editor; +---- +//// + To restore the role to its original capabilities two steps are needed. First, execute `DROP ROLE editor`. Secondly, run these queries: @@ -237,6 +251,13 @@ a|Rows: 7 [[access-control-built-in-roles-publisher-recreate]] === Recreating the `publisher` role +//// +[source, cypher, role=test-setup] +---- +DROP ROLE publisher; +---- +//// + To restore the role to its original capabilities two steps are needed. First, execute `DROP ROLE publisher`. Secondly, run these queries: @@ -313,10 +334,22 @@ a|Rows: 9 [[access-control-built-in-roles-architect-recreate]] === Recreating the `architect` role +//// +[source, cypher, role=test-setup] +---- +DROP ROLE architect; +---- +//// + To restore the role to its original capabilities two steps are needed. First, execute `DROP ROLE architect`. Secondly, run these queries: +[source, cypher, role=noplay] +---- +CREATE ROLE architect +---- + [source, cypher, role=noplay] ---- GRANT ACCESS ON DATABASE * TO architect @@ -420,7 +453,9 @@ To restore the role to its original capabilities two steps are needed. First, execute `DROP ROLE admin`. Secondly, run these queries: -[source, cypher, role=noplay] +// we cannot test this right now cause we would delete the role the test user is logged with + +[source, cypher, role=noplay test-skip] ---- CREATE ROLE admin ---- diff --git a/modules/ROOT/pages/access-control/database-administration.adoc b/modules/ROOT/pages/access-control/database-administration.adoc index 3f4e6447e..4304748b2 100644 --- a/modules/ROOT/pages/access-control/database-administration.adoc +++ b/modules/ROOT/pages/access-control/database-administration.adoc @@ -1,5 +1,15 @@ :description: How to use Cypher to manage Neo4j database administrative privileges. +//// +[source, cypher, role=test-setup] +---- +CREATE ROLE regularUsers; +CREATE ROLE databaseAdminUsers; +CREATE DATABASE `remote-db`; +CREATE USER jake SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED; +---- +//// + [role=enterprise-edition] [[access-control-database-administration]] = Database administration @@ -19,8 +29,8 @@ The components of the database privilege commands are: ** `REVOKE` – removes granted or denied privileges from roles. * _mutability_: -** `IMMUTABLE` - When used in conjunction with `GRANT` or `DENY`, specifies that a privilege cannot subsequently be removed unless auth is disabled. -Contrastingly, when `IMMUTABLE` is specified in conjunction with a `REVOKE` command, it will act as a filter and only remove matching _immutable_ privileges. +** `IMMUTABLE` - When used in conjunction with `GRANT` or `DENY`, specifies that a privilege cannot subsequently be removed unless auth is disabled. +Contrastingly, when `IMMUTABLE` is specified in conjunction with a `REVOKE` command, it will act as a filter and only remove matching _immutable_ privileges. See also xref:access-control/index.adoc#access-control-privileges-immutable[immutable privileges]. * _database-privilege_ diff --git a/modules/ROOT/pages/access-control/dbms-administration.adoc b/modules/ROOT/pages/access-control/dbms-administration.adoc index c34c8092c..2c8f00e57 100644 --- a/modules/ROOT/pages/access-control/dbms-administration.adoc +++ b/modules/ROOT/pages/access-control/dbms-administration.adoc @@ -1,5 +1,63 @@ :description: How to use Cypher to manage Neo4j DBMS administrative privileges. +//// +[source, cypher, role=test-setup] +---- +CREATE USER jake SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED; +CREATE ROLE roleAdder IF NOT EXISTS; +CREATE ROLE roleNameModifier IF NOT EXISTS; +CREATE ROLE roleDropper IF NOT EXISTS; +CREATE ROLE roleAssigner IF NOT EXISTS; +CREATE ROLE roleRemover IF NOT EXISTS; +CREATE ROLE roleShower IF NOT EXISTS; +CREATE ROLE roleManager IF NOT EXISTS; +CREATE ROLE userAdder IF NOT EXISTS; +CREATE ROLE userNameModifier IF NOT EXISTS; +CREATE ROLE userModifier IF NOT EXISTS; +CREATE ROLE passwordModifier IF NOT EXISTS; +CREATE ROLE statusModifier IF NOT EXISTS; +CREATE ROLE userDropper IF NOT EXISTS; +CREATE ROLE userShower IF NOT EXISTS; +CREATE ROLE userManager IF NOT EXISTS; +CREATE ROLE userImpersonator IF NOT EXISTS; +CREATE ROLE databaseAdder IF NOT EXISTS; +CREATE ROLE compositeDatabaseAdder IF NOT EXISTS; +CREATE ROLE databaseDropper IF NOT EXISTS; +CREATE ROLE compositeDatabaseDropper IF NOT EXISTS; +CREATE ROLE databaseModifier IF NOT EXISTS; +CREATE ROLE accessModifier IF NOT EXISTS; +CREATE ROLE compositeDatabaseManager IF NOT EXISTS; +CREATE ROLE databaseManager IF NOT EXISTS; +CREATE ROLE aliasAdder IF NOT EXISTS; +CREATE ROLE aliasDropper IF NOT EXISTS; +CREATE ROLE aliasModifier IF NOT EXISTS; +CREATE ROLE aliasLister IF NOT EXISTS; +CREATE ROLE aliasManager IF NOT EXISTS; +CREATE ROLE privilegeShower IF NOT EXISTS; +CREATE ROLE privilegeAssigner IF NOT EXISTS; +CREATE ROLE privilegeRemover IF NOT EXISTS; +CREATE ROLE privilegeManager IF NOT EXISTS; +CREATE ROLE procedureExecutor IF NOT EXISTS; +CREATE ROLE deniedProcedureExecutor IF NOT EXISTS; +CREATE ROLE boostedProcedureExecutor IF NOT EXISTS; +CREATE ROLE deniedBoostedProcedureExecutor1 IF NOT EXISTS; +CREATE ROLE deniedBoostedProcedureExecutor2 IF NOT EXISTS; +CREATE ROLE deniedBoostedProcedureExecutor3 IF NOT EXISTS; +CREATE ROLE deniedBoostedProcedureExecutor4 IF NOT EXISTS; +CREATE ROLE adminProcedureExecutor IF NOT EXISTS; +CREATE ROLE functionExecutor IF NOT EXISTS; +CREATE ROLE deniedFunctionExecutor IF NOT EXISTS; +CREATE ROLE boostedFunctionExecutor IF NOT EXISTS; +CREATE ROLE globbing1 IF NOT EXISTS; +CREATE ROLE globbing2 IF NOT EXISTS; +CREATE ROLE globbing3 IF NOT EXISTS; +CREATE ROLE globbing4 IF NOT EXISTS; +CREATE ROLE globbing5 IF NOT EXISTS; +CREATE ROLE globbing6 IF NOT EXISTS; +CREATE ROLE dbmsManager IF NOT EXISTS; +---- +//// + [role=enterprise-edition] [[access-control-dbms-administration]] = DBMS administration @@ -151,43 +209,43 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax |=== | Command | Description -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] CREATE ROLE ON DBMS TO role[, ...] | Enables the specified roles to create new roles. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] RENAME ROLE ON DBMS TO role[, ...] | Enables the specified roles to change the name of roles. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] DROP ROLE ON DBMS TO role[, ...] | Enables the specified roles to delete roles. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] ASSIGN ROLE ON DBMS TO role[, ...] | Enables the specified roles to assign roles to users. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] REMOVE ROLE ON DBMS TO role[, ...] | Enables the specified roles to remove roles from users. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] SHOW ROLE ON DBMS TO role[, ...] | Enables the specified roles to list roles. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] ROLE MANAGEMENT ON DBMS TO role[, ...] @@ -385,55 +443,55 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax |=== | Command | Description -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] CREATE USER ON DBMS TO role[, ...] | Enables the specified roles to create new users. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] RENAME USER ON DBMS TO role[, ...] | Enables the specified roles to change the name of users. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] ALTER USER ON DBMS TO role[, ...] | Enables the specified roles to modify users. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] SET PASSWORD[S] ON DBMS TO role[, ...] | Enables the specified roles to modify users' passwords and whether those passwords must be changed upon first login. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] SET USER HOME DATABASE ON DBMS TO role[, ...] | Enables the specified roles to modify users' home database. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] SET USER STATUS ON DBMS TO role[, ...] | Enables the specified roles to modify the account status of users. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] DROP USER ON DBMS TO role[, ...] | Enables the specified roles to delete users. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] SHOW USER ON DBMS TO role[, ...] | Enables the specified roles to list users. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] USER MANAGEMENT ON DBMS TO role[, ...] @@ -518,7 +576,7 @@ A user that is granted the `ALTER USER` privilege is allowed to run the `ALTER U [source, cypher, role=noplay] ---- -ALTER USER jake SET PASSWORD 'secret' SET STATUS SUSPENDED +ALTER USER jake SET PASSWORD 'verysecret' SET STATUS SUSPENDED ---- The ability to modify users' passwords and whether those passwords must be changed upon first login can be granted via the `SET PASSWORDS` privilege. @@ -549,7 +607,7 @@ A user that is granted the `SET PASSWORDS` privilege is allowed to run the `ALTE [source, cypher, role=noplay] ---- -ALTER USER jake SET PASSWORD 'abc123' CHANGE NOT REQUIRED +ALTER USER jake SET PASSWORD 'abcd5678' CHANGE NOT REQUIRED ---- The ability to modify the account status of users can be granted via the `SET USER STATUS` privilege. @@ -717,13 +775,13 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax |=== | Command | Description -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] IMPERSONATE [(*)] ON DBMS TO role[, ...] | Enables the specified roles to impersonate any user. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] IMPERSONATE (user[, ...]) ON DBMS TO role[, ...] @@ -790,49 +848,49 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax |=== | Command | Description -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] CREATE DATABASE ON DBMS TO role[, ...] | Enables the specified roles to create new standard databases and aliases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] DROP DATABASE ON DBMS TO role[, ...] | Enables the specified roles to delete standard databases and aliases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] ALTER DATABASE ON DBMS TO role[, ...] | Enables the specified roles to modify standard databases and aliases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] SET DATABASE ACCESS ON DBMS TO role[, ...] | Enables the specified roles to modify access to standard databases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT CREATE COMPOSITE DATABASE ON DBMS TO role[, ...] | Enables the specified roles to create new composite databases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT DROP COMPOSITE DATABASE ON DBMS TO role[, ...] | Enables the specified roles to delete composite databases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT COMPOSITE DATABASE MANAGEMENT ON DBMS TO role[, ...] | Enables the specified roles to create and delete composite databases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] DATABASE MANAGEMENT ON DBMS TO role[, ...] @@ -1050,31 +1108,31 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax |=== | Command | Description -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] CREATE ALIAS ON DBMS TO role[, ...] | Enables the specified roles to create new aliases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] DROP ALIAS ON DBMS TO role[, ...] | Enables the specified roles to delete aliases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] ALTER ALIAS ON DBMS TO role[, ...] | Enables the specified roles to modify aliases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] SHOW ALIAS ON DBMS TO role[, ...] | Enables the specified roles to list aliases. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] ALIAS MANAGEMENT ON DBMS TO role[, ...] @@ -1218,13 +1276,13 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax |=== | Command | Description -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT SERVER MANAGEMENT ON DBMS TO role[, ...] | Enables the specified roles to show, enable, rename, alter, reallocate, deallocate, and drop servers. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT SHOW SERVERS ON DBMS TO role[, ...] @@ -1248,25 +1306,25 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax |=== | Command | Description -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] SHOW PRIVILEGE ON DBMS TO role[, ...] | Enables the specified roles to list privileges. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] ASSIGN PRIVILEGE ON DBMS TO role[, ...] | Enables the specified roles to assign privileges using the `GRANT` and `DENY` commands. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] REMOVE PRIVILEGE ON DBMS TO role[, ...] | Enables the specified roles to remove privileges using the `REVOKE` command. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] PRIVILEGE MANAGEMENT ON DBMS TO role[, ...] @@ -1401,31 +1459,31 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax | Command | Description -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] EXECUTE PROCEDURE[S] name-globbing[, ...] ON DBMS TO role[, ...] | Enables the specified roles to execute the given procedures. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] EXECUTE BOOSTED PROCEDURE[S] name-globbing[, ...] ON DBMS TO role[, ...] | Enables the specified roles to execute the given procedures with elevated privileges. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] EXECUTE ADMIN[ISTRATOR] PROCEDURES ON DBMS TO role[, ...] | Enables the specified roles to execute procedures annotated with `@Admin`. The procedures are executed with elevated privileges. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] EXECUTE [USER [DEFINED]] FUNCTION[S] name-globbing[, ...] ON DBMS TO role[, ...] | Enables the specified roles to execute the given user defined functions. -| [source, cypher, role=noplay] +| [source, syntax, role=noheader] GRANT [IMMUTABLE] EXECUTE BOOSTED [USER [DEFINED]] FUNCTION[S] name-globbing[, ...] ON DBMS TO role[, ...] @@ -1513,7 +1571,7 @@ The following query shows an example of how to grant this privilege: [source, cypher, role=noplay] ---- -GRANT EXECUTE PROCEDURE * ON DBMS TO boostedProcedureExecutor +GRANT EXECUTE PROCEDURE * ON DBMS TO boostedProcedureExecutor; GRANT EXECUTE BOOSTED PROCEDURE db.labels, db.relationshipTypes ON DBMS TO boostedProcedureExecutor ---- @@ -1991,7 +2049,7 @@ The right to perform the following privileges can be achieved with a single comm The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. ==== -[source, cypher, role=noplay] +[source, syntax, role=noheader] ---- GRANT [IMMUTABLE] ALL [[DBMS] PRIVILEGES] ON DBMS diff --git a/modules/ROOT/pages/access-control/limitations.adoc b/modules/ROOT/pages/access-control/limitations.adoc index d5114141b..e534e8b1e 100644 --- a/modules/ROOT/pages/access-control/limitations.adoc +++ b/modules/ROOT/pages/access-control/limitations.adoc @@ -1,5 +1,15 @@ :description: Known limitations and implications of Neo4js role-based access control security. +//// +[source, cypher, role=test-setup] +---- +CREATE ROLE users; +CREATE ROLE custom; +CREATE ROLE restricted; +CREATE ROLE free; +---- +//// + [[access-control-limitations]] = Limitations @@ -35,9 +45,9 @@ There are indexes on both properties: [source, cypher] ---- -CREATE INDEX singleProp FOR (n:User) ON (n.name) -CREATE INDEX composite FOR (n:User) ON (n.name, n.surname) -CREATE FULLTEXT INDEX userNames FOR (n:User|Person) ON EACH [n.name, n.surname] +CREATE INDEX singleProp FOR (n:User) ON (n.name); +CREATE INDEX composite FOR (n:User) ON (n.name, n.surname); +CREATE FULLTEXT INDEX userNames FOR (n:User|Person) ON EACH [n.name, n.surname]; ---- [NOTE] @@ -59,18 +69,18 @@ Imagine the following nodes were added to the database: [source, cypher] ---- -CREATE (:User {name: 'Sandy'}) -CREATE (:User {name: 'Mark', surname: 'Andy'}) -CREATE (:User {name: 'Andy', surname: 'Anderson'}) -CREATE (:User:Person {name: 'Mandy', surname: 'Smith'}) -CREATE (:User:Person {name: 'Joe', surname: 'Andy'}) +CREATE (:User {name: 'Sandy'}); +CREATE (:User {name: 'Mark', surname: 'Andy'}); +CREATE (:User {name: 'Andy', surname: 'Anderson'}); +CREATE (:User:Person {name: 'Mandy', surname: 'Smith'}); +CREATE (:User:Person {name: 'Joe', surname: 'Andy'}); ---- Consider denying the label `:Person`: [source, cypher] ---- -DENY TRAVERSE Person ON GRAPH * TO users +DENY TRAVERSE ON GRAPH * NODES Person TO users ---- If the user runs a query that uses the native single property index on `name`: @@ -277,16 +287,18 @@ The rules of a security model may impact some of the database operations. This means extra security checks are necessary to incur additional data accesses, especially in the case of count store operations. These are, however, usually very fast lookups and the difference might be noticeable. -See the following security rules that set up a `restricted` and a `free` role as an example: +See the following security rules that use a `restricted` and a `free` role as an example: +[source, cypher] ---- -GRANT TRAVERSE ON GRAPH * NODES Person TO restricted -DENY TRAVERSE ON GRAPH * NODES Customer TO restricted -GRANT TRAVERSE ON GRAPH * ELEMENTS * TO free +GRANT TRAVERSE ON GRAPH * NODES Person TO restricted; +DENY TRAVERSE ON GRAPH * NODES Customer TO restricted; +GRANT TRAVERSE ON GRAPH * ELEMENTS * TO free; ---- Now, let's look at what the database needs to do in order to execute the following query: +[source, cypher] ---- MATCH (n:Person) RETURN count(n) diff --git a/modules/ROOT/pages/access-control/manage-privileges.adoc b/modules/ROOT/pages/access-control/manage-privileges.adoc index eae6b3aaa..8fdcca9f3 100644 --- a/modules/ROOT/pages/access-control/manage-privileges.adoc +++ b/modules/ROOT/pages/access-control/manage-privileges.adoc @@ -297,8 +297,7 @@ When omitting the `AS COMMANDS` clause, results will include multiple columns de Available privileges can be displayed using the different `SHOW PRIVILEGE[S]` commands. -.Command syntax -[source, cypher, role=noplay] +[source, syntax] ---- SHOW [ALL] PRIVILEGE[S] [AS [REVOKE] COMMAND[S]] [WHERE expression] @@ -1017,7 +1016,7 @@ For more info about revoking privileges, please see xref::access-control/manage- Available privileges for specific roles can be displayed using `SHOW ROLE name PRIVILEGE[S]`: -[source, cypher, role=noplay] +[source, syntax] ---- SHOW ROLE[S] name[, ...] PRIVILEGE[S] [AS [REVOKE] COMMAND[S]] [WHERE expression] @@ -1096,6 +1095,11 @@ Lists all privileges for roles `regularUsers` and `noAccessUsers`. Similar to the other `SHOW PRIVILEGES` commands, the available privileges for roles can also be listed as Cypher commands with the optional `AS COMMAND[S]`. +[source, cypher, role=noplay] +---- +SHOW ROLES regularUsers, noAccessUsers PRIVILEGES AS COMMANDS +---- + .Result [options="header,footer", width="100%", cols="m"] |=== @@ -1161,7 +1165,7 @@ Note that if a non-native auth provider like LDAP is in use, `SHOW USER PRIVILEG Other users' privileges cannot be listed when using a non-native auth provider. ==== -[source, cypher, role=noplay] +[source, syntax] ---- SHOW USER[S] [name[, ...]] PRIVILEGE[S] [AS [REVOKE] COMMAND[S]] [WHERE expression] @@ -1382,7 +1386,7 @@ a|Rows: 2 Privileges that were granted or denied earlier can be revoked using the `REVOKE` command: -[source, cypher, role=noplay] +[source, syntax] ---- REVOKE [ IMMUTABLE ] diff --git a/modules/ROOT/pages/access-control/manage-roles.adoc b/modules/ROOT/pages/access-control/manage-roles.adoc index e23581161..edc504436 100644 --- a/modules/ROOT/pages/access-control/manage-roles.adoc +++ b/modules/ROOT/pages/access-control/manage-roles.adoc @@ -4,6 +4,19 @@ [[access-control-manage-roles]] = Managing roles +//// +[source, cypher, role=test-setup] +---- +CREATE USER bob SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED; +CREATE USER user1 SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED; +CREATE USER user2 SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED; +CREATE USER user3 SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED; +CREATE ROLE myrole IF NOT EXISTS; +CREATE ROLE role1 IF NOT EXISTS; +CREATE ROLE role2 IF NOT EXISTS; +---- +//// + [abstract] -- This section explains how to use Cypher to manage roles in Neo4j. @@ -445,14 +458,14 @@ The `SHOW ROLE name PRIVILEGES` command is found in xref::access-control/manage- Roles can be created using `CREATE ROLE`: -[source, cypher, role=noplay] +[source, syntax] ---- CREATE ROLE name [IF NOT EXISTS] [AS COPY OF otherName] ---- Roles can be created or replaced by using `CREATE OR REPLACE ROLE`: -[source, cypher, role=noplay] +[source, syntax] ---- CREATE OR REPLACE ROLE name [AS COPY OF otherName] ---- diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index 2c69073f0..799d7f9e7 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -24,7 +24,7 @@ m| ENABLE SERVER | Syntax a| -[source, cyper, role=noplay] +[source, syntax, role=noheader] ---- ENABLE SERVER 'serverId' [OPTIONS "{" option: value[,...] "}"] ---- @@ -46,7 +46,7 @@ m| ALTER SERVER | Syntax a| -[source, cyper, role=noplay] +[source, syntax, role=noheader] ---- ALTER SERVER 'name' SET OPTIONS "{" option: value[,...] "}" ---- @@ -68,7 +68,7 @@ m| RENAME SERVER | Syntax a| -[source, cyper, role=noplay] +[source, syntax, role=noheader] ---- RENAME SERVER 'name' TO 'newName' ---- @@ -90,7 +90,7 @@ m| REALLOCATE DATABASES | Syntax a| -[source, cyper, role=noplay] +[source, syntax, role=noheader] ---- [DRYRUN] REALLOCATE DATABASE[S] ---- @@ -112,7 +112,7 @@ m| DEALLOCATE DATABASES | Syntax a| -[source, cyper, role=noplay] +[source, syntax, role=noheader] ---- [DRYRUN] DEALLOCATE DATABASE[S] FROM SERVER[S] 'name'[, ...] ---- @@ -134,7 +134,7 @@ m| DROP SERVER | Syntax a| -[source, cyper, role=noplay] +[source, syntax, role=noheader] ---- DROP SERVER 'name' ---- @@ -156,7 +156,7 @@ m| SHOW SERVERS | Syntax a| -[source, cyper, role=noplay] +[source, syntax, role=noheader] ---- SHOW SERVER[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/access-control/manage-users.adoc index 30e800d27..17f6d764b 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/access-control/manage-users.adoc @@ -453,7 +453,7 @@ SHOW USERS ---- .Result -[options="header,footer", width="100%", cols="2m,3m,3m,2m,2m"] +[role="queryresult" options="header,footer", width="100%", cols="2m,3m,3m,2m,2m"] |=== |user |roles @@ -463,7 +463,7 @@ SHOW USERS |"neo4j" |["admin","PUBLIC"] -|true +|false |false | @@ -579,11 +579,11 @@ SET HOME DATABASE anotherDb .Create user ====== -Or you can recreate the user `jake` in an active state, with an encrypted password (taken from the _data/scripts/databasename/restore_metadata.cypher_ of a database backup), and the requirement to not change the password by running: +Or you can create the user `Jake` in an active state, with an encrypted password (taken from the _data/scripts/databasename/restore_metadata.cypher_ of a database backup), and the requirement to not change the password by running: [source,cypher,role=noplay] ---- -CREATE USER jake +CREATE USER Jake SET ENCRYPTED PASSWORD '1,6d57a5e0b3317055454e455f96c98c750c77fb371f3f0634a1b8ff2a55c5b825,190ae47c661e0668a0c8be8a21ff78a4a34cdf918cae3c407e907b73932bd16c' CHANGE NOT REQUIRED SET STATUS ACTIVE ---- @@ -717,7 +717,7 @@ For example, you can modify the user `bob` with a new password and active status [source, cypher, role=noplay] ---- ALTER USER bob -SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED +SET PASSWORD 'abcd5678' CHANGE NOT REQUIRED SET STATUS ACTIVE ---- @@ -793,7 +793,7 @@ Users can change their password using `ALTER CURRENT USER SET PASSWORD`. The old password is required in addition to the new one, and either or both can be a string value or a string parameter. When a user executes this command it will change their password as well as set the `CHANGE NOT REQUIRED` flag. -[source, cypher, role=noplay] +[source, cypher, role=test-skip] ---- ALTER CURRENT USER SET PASSWORD FROM 'password1' TO 'password2' diff --git a/modules/ROOT/pages/access-control/privileges-immutable.adoc b/modules/ROOT/pages/access-control/privileges-immutable.adoc index c9511b27a..444d8bd6d 100644 --- a/modules/ROOT/pages/access-control/privileges-immutable.adoc +++ b/modules/ROOT/pages/access-control/privileges-immutable.adoc @@ -1,7 +1,7 @@ [role=enterprise-edition] [[access-control-privileges-immutable]] = Immutable privileges -:description: This section explains how to use Cypher to manage immutable privileges. +:description: This section explains how to use Cypher to manage immutable privileges. Unlike regular privileges, having xref:access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[privilege management] privileges is not sufficient to enable immutable privileges to be added or removed. They can only be administered when auth is disabled -- that is, when the configuration setting <> is set to `false`. @@ -13,19 +13,23 @@ Immutable privileges are useful for restricting the actions of users who themsel For example, you may want to prevent all users from performing Database Management, even the `admin` user (who are themselves able to add or remove privileges). To do so, you could run: -[source, cypher, role=noplay] +[source, cypher] ---- DENY DATABASE MANAGEMENT ON DBMS TO PUBLIC ---- + However, this would not be adequate. In case the `admin` user subsequently runs this: -[source, cypher, role=noplay] + +[source, cypher] ---- -REVOKE DENY DATABASE MANAGEMENT ON DBMS TO PUBLIC +REVOKE DENY DATABASE MANAGEMENT ON DBMS FROM PUBLIC ---- + They would effectively regain Database Management privileges. Instead, run the following query to prevent this scenario: -[source, cypher, role=noplay] + +[source, cypher, role=test-skip] ---- DENY IMMUTABLE DATABASE MANAGEMENT ON DBMS TO PUBLIC ---- diff --git a/modules/ROOT/pages/access-control/privileges-reads.adoc b/modules/ROOT/pages/access-control/privileges-reads.adoc index 1e0007415..c6dee748c 100644 --- a/modules/ROOT/pages/access-control/privileges-reads.adoc +++ b/modules/ROOT/pages/access-control/privileges-reads.adoc @@ -1,5 +1,12 @@ :description: How to use Cypher to manage read privileges on graphs. +//// +[source, cypher, role=test-setup] +---- +CREATE ROLE regularUsers; +---- +//// + [role=enterprise-edition] [[access-control-privileges-reads]] = Read privileges @@ -37,7 +44,7 @@ GRANT [IMMUTABLE] TRAVERSE TO role[, ...] ---- -For example, we can enable the user `jake`, who has the role 'regularUsers' to find all nodes with the label `Post`: +For example, we can enable users with the role `regularUsers` to find all nodes with the label `Post` in the database `neo4j`: [source, cypher, role=noplay] ---- @@ -58,7 +65,7 @@ DENY [IMMUTABLE] TRAVERSE TO role[, ...] ---- -For example, we can disable the user `jake`, who has the role 'regularUsers' from finding all nodes with the label `Payments`: +For example, we can disable users with the role `regularUsers` from finding all nodes with the label `Payments`: [source, cypher, role=noplay] ---- @@ -84,7 +91,7 @@ GRANT [IMMUTABLE] READ "{" { * | property[, ...] } "}" TO role[, ...] ---- -For example, we can enable the user `jake`, who has the role 'regularUsers' to read all properties on nodes with the label `Post`. +For example, we can enable user with the role `regularUsers` to read all properties on nodes with the label `Post` in the database `neo4j`. The `+*+` implies that the ability to read all properties also extends to properties that might be added in the future. [source, cypher, role=noplay] @@ -112,7 +119,7 @@ DENY [IMMUTABLE] READ "{" { * | property[, ...] } "}" TO role[, ...] ---- -Although we just granted the user `jake` the right to read all properties, we may want to hide the `secret` property. +Although we just granted the role `regularUsers` the right to read all properties, we may want to hide the `secret` property. The following example shows how to do that: [source, cypher, role=noplay] @@ -174,7 +181,7 @@ Although not being able to read this specific property, nodes with that label ca DENY MATCH { content } ON GRAPH neo4j NODES Message TO regularUsers ---- -The following query exemplifies how it would look if you wanted to deny both reading all properties and traversing nodes labeled with `Account`: +The following query exemplifies how it would look if you wanted to deny both reading all properties and traversing nodes labeled with `Account` in the database `neo4j`: [source, cypher, role=noplay] ---- diff --git a/modules/ROOT/pages/access-control/privileges-writes.adoc b/modules/ROOT/pages/access-control/privileges-writes.adoc index 90a16c0ba..1604b21fe 100644 --- a/modules/ROOT/pages/access-control/privileges-writes.adoc +++ b/modules/ROOT/pages/access-control/privileges-writes.adoc @@ -1,5 +1,12 @@ :description: How to use Cypher to manage write privileges on graphs. +//// +[source, cypher, role=test-setup] +---- +CREATE ROLE regularUsers; +---- +//// + [role=enterprise-edition] [[access-control-privileges-writes]] = Write privileges From 751cfe300b1143c51e7d4cec1464d6b37a5b3b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 27 Jan 2023 16:26:29 +0100 Subject: [PATCH 081/383] Update Graph functions page for testing (#352) --- modules/ROOT/pages/functions/graph.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/functions/graph.adoc b/modules/ROOT/pages/functions/graph.adoc index b707512ee..17a178e13 100644 --- a/modules/ROOT/pages/functions/graph.adoc +++ b/modules/ROOT/pages/functions/graph.adoc @@ -1,7 +1,7 @@ [[query-functions-graph]] = Graph functions :description: Graph functions provide information about the constituent graphs in composite databases - +:test-skip: true [[functions-graph-names]] == graph.names() From 9aadd0a7dc7a7e6159bc51416f7eb0f6a027bb52 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Mon, 30 Jan 2023 08:54:29 +0100 Subject: [PATCH 082/383] update call.adoc page for testing (#347) Remove extra '' in the query replace dbms.security.createrUser with dbms.checkConfigValue, for reason check here: https://neo4j.slack.com/archives/C02JR6LSJ/p1674746354391619 --------- Co-authored-by: Stefano Ottolenghi --- modules/ROOT/pages/clauses/call.adoc | 138 +++++++++++++-------------- 1 file changed, 64 insertions(+), 74 deletions(-) diff --git a/modules/ROOT/pages/clauses/call.adoc b/modules/ROOT/pages/clauses/call.adoc index 7c4635f6c..53f91fe0f 100644 --- a/modules/ROOT/pages/clauses/call.adoc +++ b/modules/ROOT/pages/clauses/call.adoc @@ -59,12 +59,8 @@ See link:{neo4j-docs-base-uri}/java-reference/{page-version}/extending-neo4j/pro This calls the built-in procedure `db.labels`, which lists all labels used in the database. -//// -CREATE (a:User:Administrator {name: 'Adrian'}) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CALL db.labels() ---- @@ -85,12 +81,8 @@ Cypher allows the omission of parentheses on procedures of arity-0 (no arguments Best practice is to use parentheses for procedures. ==== -//// -CREATE (a:User:Administrator {name: 'Adrian'}) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CALL db.labels ---- @@ -120,7 +112,7 @@ To get the signature, make sure to use the `YIELD` clause. The following query return the signature for a particular procedure: .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW PROCEDURES YIELD name, signature WHERE name = 'dbms.listConfig' @@ -138,7 +130,7 @@ The result shows that: [role="queryresult",options="header,footer",cols="1* Date: Mon, 30 Jan 2023 08:57:59 +0100 Subject: [PATCH 083/383] Update CALL subquery examples for testing (#348) --- modules/ROOT/pages/clauses/call-subquery.adoc | 183 +++++++++--------- 1 file changed, 89 insertions(+), 94 deletions(-) diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/clauses/call-subquery.adoc index 3e15b6631..34dd53746 100644 --- a/modules/ROOT/pages/clauses/call-subquery.adoc +++ b/modules/ROOT/pages/clauses/call-subquery.adoc @@ -36,7 +36,10 @@ The following graph is used for the examples below: image:graph_call_subquery_clause.svg[] -//// +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- CREATE (a:Person:Child {age: 20, name: 'Alice'}), (b:Person {age: 27, name: 'Bob'}), @@ -45,7 +48,7 @@ CREATE CREATE (a)-[:FRIEND_OF]->(b) CREATE (a)-[:CHILD_OF]->(c) CREATE (:Counter {count: 0}) -//// +---- [[call-semantics]] @@ -54,13 +57,13 @@ CREATE (:Counter {count: 0}) A `CALL` clause is executed once for each incoming row. -.Execute for each incomming row +.Execute for each incoming row ====== The `CALL` clause executes three times, one for each row that the `UNWIND` clause outputs. .Query -[source, cypher, role="noplay"] +[source, cypher] ---- UNWIND [0, 1, 2] AS x CALL { @@ -70,22 +73,16 @@ RETURN innerReturn ---- .Result -[source, result, role="noheader"] ----- -Rows: 3 - -+-------------+ -| innerReturn | -+-------------+ -| 'hello' | -| 'hello' | -| 'hello' | -+-------------+ ----- - +[role="queryresult",options="header,footer",cols="m"] +|=== +| +innerReturn+ +| +'hello'+ +| +'hello'+ +| +'hello'+ +d|Rows:3 +|=== ====== - Each execution of a `CALL` clause can observe changes from previous executions. @@ -93,7 +90,7 @@ Each execution of a `CALL` clause can observe changes from previous executions. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- UNWIND [0, 1, 2] AS x CALL { @@ -109,19 +106,19 @@ RETURN ---- .Result -[source, result, role="noheader"] ----- -Set Properties: 3 -Rows: 3 +[role="queryresult",options="header,footer",cols=""2*(b), + (a)-[:CHILD_OF]->(c) +---- +//// [[subquery-correlated-aggregation]] == Aggregation on imported variables @@ -379,7 +385,7 @@ Aggregations in subqueries are scoped to the subquery evaluation, also for impor The following example counts the number of younger persons for each person in the graph: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (p:Person) CALL { @@ -414,7 +420,7 @@ The following example uses a CSV file and the `LOAD CSV` clause to import more d It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+`: .friends.csv -[source, csv, role="noheader"] +[source, csv] ---- 1,Bill,26 2,Max,27 @@ -424,7 +430,7 @@ It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+` ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- LOAD CSV FROM 'file:///friends.csv' AS line CALL { @@ -461,19 +467,8 @@ Using `+CALL { ... } IN TRANSACTIONS+` is the recommended way of deleting a larg .+DETACH DELETE+ ====== -//// -CREATE - (a:Person:Child {age: 20, name: 'Alice'}), - (b:Person {age: 27, name: 'Bob'}), - (c:Person:Parent {age: 65, name: 'Charlie'}), - (d:Person {age: 30, name: 'Dora'}), - (:Counter {count: 0}) - CREATE (a)-[:FRIEND_OF]->(b) - CREATE (a)-[:CHILD_OF]->(c) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) CALL { @@ -501,26 +496,15 @@ Modifying the subquery may result in `OutOfMemory` exceptions for sufficiently l ====== -.+DETACH DELTE+ +.+DETACH DELETE+ ====== The `+CALL { ... } IN TRANSACTIONS+` subquery should not be modified. Any necessary filtering can be done before the subquery. -//// -CREATE - (a:Person:Child {age: 20, name: 'Alice'}), - (b:Person {age: 27, name: 'Bob'}), - (c:Person:Parent {age: 65, name: 'Charlie'}), - (d:Person {age: 30, name: 'Dora'}), - (:Counter {count: 0}) - CREATE (a)-[:FRIEND_OF]->(b) - CREATE (a)-[:CHILD_OF]->(c) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Label) WHERE n.prop > 100 CALL { @@ -558,7 +542,7 @@ The following is the same example but with one transaction every `2` input rows: ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- LOAD CSV FROM 'file:///friends.csv' AS line CALL { @@ -591,7 +575,7 @@ You can also use `+CALL { ... } IN TRANSACTIONS OF n ROWS+` to delete all your d For example: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) CALL { @@ -632,7 +616,7 @@ In the following example, the last subquery execution in the second inner transa due to division by zero. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- UNWIND [4, 2, 1, 0] AS i CALL { @@ -642,8 +626,8 @@ CALL { RETURN i ---- -.Error -[source, error, role="noheader"] +.Error message +[source, error] ---- / by zero (Transactions committed: 1) ---- @@ -651,7 +635,7 @@ RETURN i When the failure occurred, the first transaction had already been committed, so the database contains two example nodes. .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (e:Example) RETURN e.num @@ -685,7 +669,7 @@ Attempting any of the above, will throw an error. For example, the following query using a `WHERE` clause after an importing `WITH` clause will throw an error: .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- UNWIND [[1,2],[1,2,3,4],[1,2,3,4,5]] AS l CALL { @@ -697,7 +681,7 @@ RETURN largeLists ---- .Error message -[source, output, role="noheader", indent=0] +[source, error] ---- Importing WITH should consist only of simple references to outside variables. WHERE is not allowed. @@ -708,7 +692,7 @@ This second `WITH` clause will act as a regular `WITH` clause. For example, the following query will not throw an error: .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [[1,2],[1,2,3,4],[1,2,3,4,5]] AS l CALL { @@ -720,3 +704,14 @@ CALL { RETURN largeLists ---- +.Result +[role="queryresult",options="header,footer",cols="1* Date: Mon, 30 Jan 2023 09:21:28 +0100 Subject: [PATCH 084/383] Update list functions examples for testing (#351) --- modules/ROOT/pages/functions/list.adoc | 38 +++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/modules/ROOT/pages/functions/list.adoc b/modules/ROOT/pages/functions/list.adoc index 4fea1b211..b9b59741b 100644 --- a/modules/ROOT/pages/functions/list.adoc +++ b/modules/ROOT/pages/functions/list.adoc @@ -25,9 +25,16 @@ Functions: * xref::functions/list.adoc#functions-tointegerlist[toIntegerList()] * xref::functions/list.adoc#functions-tostringlist[toStringList()] +== Example graph + +The following graph is used for the examples below: + image:graph_list_functions.svg[] -//// +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- CREATE (alice:Person:Developer {name:'Alice', age: 38, eyes: 'brown'}), (bob {name: 'Bob', age: 25, eyes: 'blue'}), @@ -39,8 +46,7 @@ CREATE (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) -//// - +---- [[functions-keys]] == keys() @@ -86,7 +92,7 @@ keys(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a) WHERE a.name = 'Alice' RETURN keys(a) @@ -99,7 +105,7 @@ A list containing the names of all the properties on the node bound to `a` is re |=== | +keys(a)+ -| +["name","age","eyes"]+ +| +["eyes","name","age"]+ 1+d|Rows: 1 |=== @@ -151,7 +157,7 @@ labels(node) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a) WHERE a.name = 'Alice' RETURN labels(a) @@ -216,7 +222,7 @@ nodes(path) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH p = (a)-->(b)-->(c) WHERE a.name = 'Alice' AND c.name = 'Eskil' @@ -230,7 +236,7 @@ A list containing all the nodes in the path `p` is returned. |=== | +nodes(p)+ -| +[Node[0]{name:"Alice",age:38,eyes:"brown"},Node[1]{name:"Bob",age:25,eyes:"blue"},Node[4]{eyes:"blue",array:["one","two","three"],name:"Eskil",age:41}]+ +| +[{"name":"Alice","eyes":"brown","age":38},{"name":"Bob","eyes":"blue","age":25},{"array":['one', 'two', 'three']},{"name":"Eskil","eyes":"blue","age":41}]+ 1+d|Rows: 1 |=== @@ -284,7 +290,7 @@ range(start, end [, step]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10), range(2, 18, 3), range(0, 5, -1) ---- @@ -354,7 +360,7 @@ reduce(accumulator = initial, variable IN list | expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH p = (a)-->(b)-->(c) WHERE a.name = 'Alice' AND b.name = 'Bob' AND c.name = 'Daniel' @@ -420,7 +426,7 @@ relationships(path) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH p = (a)-->(b)-->(c) WHERE a.name = 'Alice' AND c.name = 'Eskil' @@ -485,7 +491,7 @@ reverse(original) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH [4923,'abc',521, null, 487] AS ids RETURN reverse(ids) @@ -539,7 +545,7 @@ tail(list) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a) WHERE a.name = 'Eskil' RETURN a.array, tail(a.array) @@ -676,7 +682,7 @@ toFloatList(list) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN toFloatList(null) as noList, toFloatList([null, null]) as nullsInList, @@ -744,7 +750,7 @@ toIntegerList(list) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN toIntegerList(null) as noList, toIntegerList([null, null]) as nullsInList, @@ -813,7 +819,7 @@ toStringList(list) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN toStringList(null) as noList, toStringList([null, null]) as nullsInList, From 428f70e160eeb2d284105c44cfefe46be0c8dca4 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Mon, 30 Jan 2023 09:54:57 +0100 Subject: [PATCH 085/383] more testing fixes to aliases page --- modules/ROOT/pages/aliases.adoc | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index ddf944b16..befdf6a60 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -244,17 +244,17 @@ This prevents issues such as a transaction executing against multiple target dat //// [source, cypher, role=test-setup] ---- -CREATE DATABASE `movies`; +CREATE DATABASE `movies` WAIT; CREATE ALIAS `films` FOR DATABASE `movies`; CREATE ALIAS `motion pictures` FOR DATABASE `movies` PROPERTIES { nameContainsSpace: true }; CREATE DATABASE `northwind-graph-2020` WAIT; // wait, so aliases get `currentStatus: online` CREATE DATABASE `northwind-graph-2021` WAIT; CREATE DATABASE `northwind-graph-2022` WAIT; -CREATE DATABASE `sci-fi-books`; -CREATE COMPOSITE DATABASE `library`; +CREATE DATABASE `sci-fi-books` WAIT; +CREATE COMPOSITE DATABASE `library` WAIT; CREATE ALIAS `library`.`sci-fi` FOR DATABASE `sci-fi-books`; -CREATE COMPOSITE DATABASE garden; -CREATE DATABASE `perennial-flowers`; +CREATE COMPOSITE DATABASE garden WAIT; +CREATE DATABASE `perennial-flowers` WAIT; ---- CREATE ALIAS `library`.`romance` FOR DATABASE `romance-books` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password'; @@ -823,7 +823,7 @@ Rows: 0 ---- .Query -[source, cypher] +[source, cypher, role=test-skip] ---- CREATE ALIAS garden.trees FOR DATABASE trees AT 'neo4j+s://location:7687' @@ -895,12 +895,8 @@ Failed to create the specified database alias 'yard': Database 'garden' is compo == Altering database aliases //// -[source, cypher, role=test-setup] ----- -CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020`; CREATE ALIAS garden.flowers FOR DATABASE `perennial-flowers`; ----- - +CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020`; // created in the replace alias example CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER alice PASSWORD 'password'; CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" USER alice PASSWORD 'password' DRIVER { @@ -1051,7 +1047,7 @@ In this case, by not repeating the driver setting `connection_pool_max_size` the Example of altering a remote database alias to remove all custom driver settings. .Query -[source, cypher] +[source, cypher, role=test-skip] ---- ALTER ALIAS `movie scripts` SET DATABASE DRIVER {} @@ -1081,7 +1077,7 @@ Rows: 0 ---- .Query -[source, cypher] +[source, cypher, role=test-skip] ---- ALTER ALIAS `movie scripts` SET DATABASE PROPERTIES { nameContainsSpace: true } ---- @@ -1112,7 +1108,7 @@ Rows: 0 ---- .Query -[source, cypher] +[source, cypher, role=test-skip] ---- ALTER ALIAS garden.trees SET DATABASE TARGET updatedTrees AT 'neo4j+s://location:7687' PROPERTIES { treeVersion: 2 } ---- @@ -1182,15 +1178,10 @@ ALTER ALIAS `no-alias` IF EXISTS SET DATABASE TARGET `northwind-graph-2021` == Deleting database aliases //// -[source, cypher, role=test-setup] ----- -CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2021`; +CREATE ALIAS garden.flowers FOR DATABASE `perennial-flowers` PROPERTIES { perennial: true }; CREATE ALIAS `northwind-2022` FOR DATABASE `northwind-graph-2022` PROPERTIES { newestNorthwind: true, index: 3 }; +CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2021`; CREATE ALIAS `remote-northwind-2021` -CREATE ALIAS garden.flowers FOR DATABASE `perennial-flowers` -PROPERTIES { perennial: true }; ----- - FOR DATABASE `northwind-graph-2021` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password' PROPERTIES { newestNorthwind: false, index: 6 }; CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://other-location:7687" USER alice PASSWORD 'password'; From 912d188f80d6daa49423aab9d078cb3a412066b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 30 Jan 2023 11:04:23 +0100 Subject: [PATCH 086/383] Update Spatial Functions examples for testing (#358) --- modules/ROOT/pages/functions/spatial.adoc | 74 ++++++++++------------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/modules/ROOT/pages/functions/spatial.adoc b/modules/ROOT/pages/functions/spatial.adoc index 449478084..015b52e4b 100644 --- a/modules/ROOT/pages/functions/spatial.adoc +++ b/modules/ROOT/pages/functions/spatial.adoc @@ -21,13 +21,15 @@ The following graph is used for some of the examples below. image:graph_spatial_functions.svg[] -//// +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- CREATE (copenhagen:TrainStation {longitude: 12.564590, latitude: 55.672874, city: 'Copenhagen'}), (malmo:Office {longitude: 12.994341, latitude: 55.611784, city: 'Malmo'}), (copenhagen)-[:TRAVEL_ROUTE]->(malmo) -//// - +---- [[functions-distance]] == point.distance() @@ -43,9 +45,7 @@ CREATE ** This formula works well for points close to the earth's surface; for instance, it is well-suited for calculating the distance of an airplane flight. It is less suitable for greater heights, however, such as when calculating the distance between two satellites. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- point.distance(point1, point2) ---- @@ -87,7 +87,7 @@ point.distance(point1, point2) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH point({x: 2.3, y: 4.5, crs: 'cartesian'}) AS p1, @@ -114,7 +114,7 @@ The distance between two 2D points in the _Cartesian_ CRS is returned. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH point({longitude: 12.78, latitude: 56.7, height: 100}) AS p1, @@ -141,7 +141,7 @@ The distance between two 3D points in the _WGS 84_ CRS is returned. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (t:TrainStation)-[:TRAVEL_ROUTE]->(o:Office) WITH @@ -169,7 +169,7 @@ The distance between the train station in Copenhagen and the Neo4j office in Mal ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN point.distance(null, point({longitude: 56.7, latitude: 12.78})) AS d ---- @@ -200,9 +200,7 @@ If `null` is provided as one or both of the arguments, `null` is returned. The return value will be true if the provided point is contained in the bounding box (boundary included), otherwise the return value will be false. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- point.withinBBox(point, lowerLeft, upperRight) ---- @@ -249,7 +247,7 @@ point.withinBBox(point, lowerLeft, upperRight) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH point({x: 0, y: 0, crs: 'cartesian'}) AS lowerLeft, @@ -276,7 +274,7 @@ Checking if a point in _Cartesian_ CRS is contained in the bounding box. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH point({longitude: 12.53, latitude: 55.66}) AS lowerLeft, @@ -305,7 +303,7 @@ Finds all train stations contained in a bounding box around Copenhagen. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH point({longitude: 179, latitude: 55.66}) AS lowerLeft, @@ -332,7 +330,7 @@ A bounding box that crosses the 180th meridian. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN point.withinBBox( @@ -362,9 +360,7 @@ If `null` is provided as any of the arguments, `null` is returned. `point({longitude | x, latitude | y [, crs][, srid]})` returns a 2D point in the _WGS 84_ CRS corresponding to the given coordinate values. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- point({longitude | x, latitude | y [, crs][, srid]}) ---- @@ -415,7 +411,7 @@ point({longitude | x, latitude | y [, crs][, srid]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN point({longitude: 56.7, latitude: 12.78}) AS point ---- @@ -427,7 +423,7 @@ A 2D point with a `longitude` of `56.7` and a `latitude` of `12.78` in the _WGS |=== | +point+ -| +point({x: 56.7, y: 12.78, crs: 'wgs-84'})+ +| +point({srid:4326, x:56.7, y:12.78})+ 1+d|Rows: 1 |=== @@ -439,7 +435,7 @@ A 2D point with a `longitude` of `56.7` and a `latitude` of `12.78` in the _WGS ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN point({x: 2.3, y: 4.5, crs: 'WGS-84'}) AS point ---- @@ -451,7 +447,7 @@ RETURN point({x: 2.3, y: 4.5, crs: 'WGS-84'}) AS point |=== | +point+ -| +point({x: 2.3, y: 4.5, crs: 'wgs-84'})+ +| +point({srid:4326, x:2.3, y:4.5})+ 1+d|Rows: 1 |=== @@ -463,7 +459,7 @@ RETURN point({x: 2.3, y: 4.5, crs: 'WGS-84'}) AS point ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (p:Office) RETURN point({longitude: p.longitude, latitude: p.latitude}) AS officePoint @@ -476,7 +472,7 @@ A 2D point representing the coordinates of the city of Malmo in the _WGS 84_ CRS |=== | +officePoint+ -| +point({x: 12.994341, y: 55.611784, crs: 'wgs-84'})+ +| +point({srid:4326, x:12.994341, y:55.611784})+ 1+d|Rows: 1 |=== @@ -488,7 +484,7 @@ A 2D point representing the coordinates of the city of Malmo in the _WGS 84_ CRS ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN point(null) AS p ---- @@ -513,9 +509,7 @@ If `null` is provided as the argument, `null` is returned. `point({longitude | x, latitude | y, height | z, [, crs][, srid]})` returns a 3D point in the _WGS 84_ CRS corresponding to the given coordinate values. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- point({longitude | x, latitude | y, height | z, [, crs][, srid]}) ---- @@ -570,7 +564,7 @@ point({longitude | x, latitude | y, height | z, [, crs][, srid]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN point({longitude: 56.7, latitude: 12.78, height: 8}) AS point ---- @@ -582,7 +576,7 @@ A 3D point with a `longitude` of `56.7`, a `latitude` of `12.78` and a height of |=== | +point+ -| +point({x: 56.7, y: 12.78, z: 8.0, crs: 'wgs-84-3d'})+ +| +point({srid:4979, x:56.7, y:12.78, z:8})+ 1+d|Rows: 1 |=== @@ -595,9 +589,7 @@ A 3D point with a `longitude` of `56.7`, a `latitude` of `12.78` and a height of `point({x, y [, crs][, srid]})` returns a 2D point in the _Cartesian_ CRS corresponding to the given coordinate values. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- point({x, y [, crs][, srid]}) ---- @@ -647,7 +639,7 @@ point({x, y [, crs][, srid]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN point({x: 2.3, y: 4.5}) AS point ---- @@ -659,7 +651,7 @@ A 2D point with an `x` coordinate of `2.3` and a `y` coordinate of `4.5` in the |=== | +point+ -| +point({x: 2.3, y: 4.5, crs: 'cartesian'})+ +| +point({srid:7203, x:2.3, y:4.5})+ 1+d|Rows: 1 |=== @@ -672,9 +664,7 @@ A 2D point with an `x` coordinate of `2.3` and a `y` coordinate of `4.5` in the `point({x, y, z, [, crs][, srid]})` returns a 3D point in the _Cartesian_ CRS corresponding to the given coordinate values. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- point({x, y, z, [, crs][, srid]}) ---- @@ -728,7 +718,7 @@ point({x, y, z, [, crs][, srid]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN point({x: 2.3, y: 4.5, z: 2}) AS point ---- @@ -740,7 +730,7 @@ A 3D point with an `x` coordinate of `2.3`, a `y` coordinate of `4.5` and a `z` |=== | +point+ -| +point({x: 2.3, y: 4.5, z: 2.0, crs: 'cartesian-3d'})+ +| +point({srid:9157, x:2.3, y:4.5, z:2})+ 1+d|Rows: 1 |=== From 677fc8c31eb5f0442ca2dbd0c1ed3ceaed324987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 30 Jan 2023 11:37:17 +0100 Subject: [PATCH 087/383] Update Aggregating Functions examples for testing (#356) --- modules/ROOT/pages/functions/aggregating.adoc | 173 +++++++----------- 1 file changed, 71 insertions(+), 102 deletions(-) diff --git a/modules/ROOT/pages/functions/aggregating.adoc b/modules/ROOT/pages/functions/aggregating.adoc index 58e08abb0..ddee351fe 100644 --- a/modules/ROOT/pages/functions/aggregating.adoc +++ b/modules/ROOT/pages/functions/aggregating.adoc @@ -28,7 +28,7 @@ Grouping keys are non-aggregate expressions, that are used to group the values g Assume we have the following return statement: -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- RETURN n, count(*) ---- @@ -44,7 +44,7 @@ However, not all expressions can be composed with aggregation functions. The example below will throw an error since we compose `n.x`, which is not a grouping key, with the aggregation expression `+count(*)+`. For more information see xref:functions/aggregating.adoc#grouping-keys[Grouping keys]. -[source, cypher] +[source, cypher, role=test-skip] ---- RETURN n.x + count(*) ---- @@ -59,7 +59,10 @@ The following graph is used for the examples below: image:graph_aggregating_functions.svg[] -//// +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- CREATE (a:Person {name: 'A', age: 13}), (b:Person {name: 'B', age: 33, eyes: 'blue'}), @@ -73,17 +76,14 @@ CREATE (a)-[:KNOWS]->(b), (c)-[:KNOWS]->(d2), (b)-[:KNOWS]->(d2) -//// - +---- [[functions-avg]] == avg() - Numeric values The function `avg()` returns the average of a set of numeric values. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- avg(expression) ---- @@ -121,7 +121,7 @@ avg(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) RETURN avg(n.age) @@ -147,9 +147,7 @@ The average of all the values in the property `age` is returned. The function `avg()` returns the average of a set of Durations. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- avg(expression) ---- @@ -189,7 +187,7 @@ avg(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [duration('P2DT3H'), duration('PT1H45S')] AS dur RETURN avg(dur) @@ -215,9 +213,7 @@ The average of the two supplied Durations is returned. The function `collect()` returns a single aggregated list containing the values returned by an expression. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- collect(expression) ---- @@ -257,7 +253,7 @@ collect(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) RETURN collect(n.age) @@ -286,9 +282,7 @@ The function `count()` returns the number of values or rows, and appears in two `count(*)`:: returns the number of matching rows. `count(expr)`:: returns the number of non-`null` values returned by an expression. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- count(expression) ---- @@ -331,7 +325,7 @@ The function `count(*)` can be used to return the number of nodes; for example, ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n {name: 'A'})-->(x) RETURN labels(n), n.age, count(*) @@ -360,7 +354,7 @@ The function `count(*)` can be used to group the type of matched relationships a ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n {name: 'A'})-[r]->() RETURN type(r), count(*) @@ -373,8 +367,8 @@ The type of matched relationships are grouped and the group count are returned. |=== | +type(r)+ | +count(*)+ -| +"KNOWS"+ | +3+ | +"READS"+ | +1+ +| +"KNOWS"+ | +3+ 2+d|Rows: 2 |=== @@ -390,7 +384,7 @@ Instead of simply returning the number of rows with `count(*)`, the function `co ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) RETURN count(n.age) @@ -422,7 +416,7 @@ In this example we are trying to find all our friends of friends, and count them ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (me:Person)-->(friend:Person)-->(friend_of_friend:Person) WHERE me.name = 'A' @@ -449,9 +443,7 @@ Both `B` and `C` know `D` and thus `D` will get counted twice when not using `DI The function `max()` returns the maximum value in a set of values. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- max(expression) ---- @@ -491,7 +483,7 @@ max(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [1, 'a', null, 0.2, 'b', '1', '99'] AS val RETURN max(val) @@ -519,7 +511,7 @@ The value `'99'` (a string), is considered to be a lower value than `1` (an inte ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [[1, 'a', 89], [1, 2]] AS val RETURN max(val) @@ -544,7 +536,7 @@ The highest of all the lists in the set -- in this case, the list `[1, 2]` -- is ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) RETURN max(n.age) @@ -570,9 +562,7 @@ The highest of all the values in the property `age` is returned. The function `min()` returns the minimum value in a set of values. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- min(expression) ---- @@ -613,7 +603,7 @@ min(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [1, 'a', null, 0.2, 'b', '1', '99'] AS val RETURN min(val) @@ -637,7 +627,7 @@ Note that the (numeric) value `0.2`, which may _appear_ at first glance to be th ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND ['d', [1, 2], ['a', 'c', 23]] AS val RETURN min(val) @@ -662,7 +652,7 @@ The lowest of all the values in the set -- in this case, the list `['a', 'c', 23 ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) RETURN min(n.age) @@ -690,9 +680,7 @@ The function `percentileCont()` returns the percentile of the given value over a It uses a linear interpolation method, calculating a weighted average between two values if the desired percentile lies between them. For nearest values using a rounding method, see `percentileDisc`. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- percentileCont(expression, percentile) ---- @@ -732,7 +720,7 @@ percentileCont(expression, percentile) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) RETURN percentileCont(n.age, 0.4) @@ -760,9 +748,7 @@ The function `percentileDisc()` returns the percentile of the given value over a It uses a rounding method and calculates the nearest value to the percentile. For interpolated values, see `percentileCont`. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- percentileDisc(expression, percentile) ---- @@ -803,7 +789,7 @@ percentileDisc(expression, percentile) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) RETURN percentileDisc(n.age, 0.5) @@ -831,9 +817,7 @@ The function `stDev()` returns the standard deviation for the given value over a It uses a standard two-pass method, with `N - 1` as the denominator, and should be used when taking a sample of the population for an unbiased estimate. When the standard variation of the entire population is being calculated, `stdDevP` should be used. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- stDev(expression) ---- @@ -870,7 +854,7 @@ stDev(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE n.name IN ['A', 'B', 'C'] @@ -899,9 +883,7 @@ The function `stDevP()` returns the standard deviation for the given value over It uses a standard two-pass method, with `N` as the denominator, and should be used when calculating the standard deviation for an entire population. When the standard variation of only a sample of the population is being calculated, `stDev` should be used. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- stDevP(expression) ---- @@ -939,7 +921,7 @@ stDevP(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE n.name IN ['A', 'B', 'C'] @@ -966,9 +948,7 @@ The population standard deviation of the values in the property `age` is returne The function `sum()` returns the sum of a set of numeric values. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- sum(expression) ---- @@ -1006,7 +986,7 @@ sum(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) RETURN sum(n.age) @@ -1032,9 +1012,7 @@ The sum of all the values in the property `age` is returned. The function `sum()` returns the sum of a set of durations. -*Syntax:* - -[source, syntax, role="noheader"] +[source, syntax] ---- sum(expression) ---- @@ -1070,7 +1048,7 @@ sum(expression) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [duration('P2DT3H'), duration('PT1H45S')] AS dur RETURN sum(dur) @@ -1145,7 +1123,8 @@ MATCH (p: Person) RETURN max(p.age) .Query [source, cypher] ---- -MATCH (p: Person) RETURN max(p.age) + 1 +MATCH (p: Person) +RETURN max(p.age) + 1 ---- .Result @@ -1167,14 +1146,15 @@ Note that `n` is a grouping key: .Query [source, cypher] ---- -MATCH (n: Person{name:"A"})-[:KNOWS]-(f:Person) RETURN n, n.age - max(f.age) +MATCH (n: Person{name:"A"})-[:KNOWS]-(f:Person) +RETURN n, n.age - max(f.age) ---- .Result [role="queryresult",options="header,footer",cols="2* -Try this query live -(book), - (a)-[:KNOWS]->(d1), - (a)-[:KNOWS]->(c), - (a)-[:KNOWS]->(b), - (c)-[:KNOWS]->(d2), - (b)-[:KNOWS]->(d2) - -]]> -++++ -endif::nonhtmloutput[] - -The above query could be rewritten to: +.Error message +[source, error] +---- +Aggregation column contains implicit grouping expressions. For example, in 'RETURN n.a, n.a + n.b + count(*)' the aggregation expression 'n.a + n.b + count(*)' includes the implicit grouping key 'n.b'. It may be possible to rewrite the query by extracting these grouping/aggregation expressions into a preceding WITH clause. Illegal expression(s): n.age +---- +The latter query would, however, work if rewritten to: .Query [source, cypher] ---- -MATCH (n: Person{name:"A"})-[:KNOWS]-(f:Person) WITH n.age + n.age AS groupingKey, f RETURN groupingKey, groupingKey - max(f.age) +MATCH (n: Person{name:"A"})-[:KNOWS]-(f:Person) +WITH n.age + n.age AS groupingKey, f +RETURN groupingKey, groupingKey - max(f.age) ---- +.Result +[role="queryresult",options="header,footer",cols="2* Date: Mon, 30 Jan 2023 13:27:47 +0100 Subject: [PATCH 088/383] Update User Defined functions page for testing (#362) --- modules/ROOT/pages/functions/user-defined.adoc | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/modules/ROOT/pages/functions/user-defined.adoc b/modules/ROOT/pages/functions/user-defined.adoc index f21f155c9..b62f7d750 100644 --- a/modules/ROOT/pages/functions/user-defined.adoc +++ b/modules/ROOT/pages/functions/user-defined.adoc @@ -1,4 +1,5 @@ :description: User-defined functions are written in Java, deployed into the database and are called in the same way as any other Cypher function. +:test-skip: true [[query-functions-user-defined]] = User-defined functions @@ -42,13 +43,8 @@ This example shows how you invoke a user-defined function called `join` from Cyp This calls the user-defined function `org.neo4j.procedure.example.join()`. -//// -UNWIND ['John', 'Paul', 'George', 'Ringo'] AS name -CREATE (:Member {name: name}) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Member) RETURN org.neo4j.function.example.join(collect(n.name)) AS members @@ -80,13 +76,8 @@ This example shows how you invoke a user-defined aggregation function called `lo This calls the user-defined function `org.neo4j.function.example.longestString()`. -//// -UNWIND ['John', 'Paul', 'George', 'Ringe'] AS beatle -CREATE (:Member {name: beatle}) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Member) RETURN org.neo4j.function.example.longestString(n.name) AS member From 1e9c6f89e99309fac5aece85ee26efa303f16d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 30 Jan 2023 15:47:20 +0100 Subject: [PATCH 089/383] Fix Operator page for testing (#364) - Update .eager example to use DETACH DELETE --- .../ROOT/pages/execution-plans/operators.adoc | 100 ++++++++++++++++-- 1 file changed, 91 insertions(+), 9 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index b280f1836..50463a7ae 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -11,6 +11,74 @@ All executing plan operators are listed here, grouped by the similarity of their Certain operators are only used by a subset of the xref::query-tuning/index.adoc#cypher-runtime[runtimes] that Cypher can choose from. If that is the case, the example queries will be prefixed with an option to choose one of these runtimes. +//// +[source, cypher, role=test-setup] +---- +CREATE (me:Person {name: 'me'}); +CREATE (andy:Person {name: 'Andy'}); +CREATE (bob:Person {name: 'Bob'}); +CREATE (mattias:Person {name: 'Mattias'}); +CREATE (lovis:Person {name: 'Lovis'}); +CREATE (pontus:Person {name: 'Pontus'}); +CREATE (max:Person {name: 'Max'}); +CREATE (konstantin:Person {name: 'Konstantin'}); +CREATE (stefan:Person {name: 'Stefan'}); +CREATE (mats:Person {name: 'Mats'}); +CREATE (petra:Person {name: 'Petra'}); +CREATE (craig:Person {name: 'Craig'}); +CREATE (steven:Person {name: 'Steven'}); +CREATE (chris:Person {name: 'Chris'}); +CREATE (london:Location {name: 'London'}); +CREATE (malmo:Location {name: 'Malmo'}); +CREATE (sf:Location {name: 'San Francisco'}); +CREATE (berlin:Location {name: 'Berlin'}); +CREATE (newyork:Location {name: 'New York'}); +CREATE (kuala:Location {name: 'Kuala Lumpur'}); +CREATE (stockholm:Location {name: 'Stockholm'}); +CREATE (paris:Location {name: 'Paris'}); +CREATE (madrid:Location {name: 'Madrid'}); +CREATE (rome:Location {name: 'Rome'}); +CREATE (england:Country {name: 'England'}); +CREATE (field:Team {name: 'Field'}); +CREATE (engineering:Team {name: 'Engineering', id:42}); +CREATE (sales:Team {name: 'Sales'}); +CREATE (monads:Team {name: 'Team Monads'}); +CREATE (birds:Team {name: 'Team Enlightened Birdmen'}); +CREATE (quality:Team {name: 'Team Quality'}); +CREATE (rassilon:Team {name: 'Team Rassilon'}); +CREATE (executive:Team {name: 'Team Executive'}); +CREATE (remoting:Team {name: 'Team Remoting'}); +CREATE (other:Team {name: 'Other'}); +CREATE (me)-[:WORKS_IN {duration: 190, title: 'senior sales engineer'}]->(london); +CREATE (bob)-[:WORKS_IN {duration: 187, title: 'junior developer'}]->(london); +CREATE (andy)-[:WORKS_IN {duration: 150, title: ''}]->(london); +CREATE (mattias)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(london); +CREATE (lovis)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(sf); +CREATE (pontus)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(malmo); +CREATE (max)-[:WORKS_IN {duration: 230, title: 'field engineer'}]->(newyork); +CREATE (konstantin)-[:WORKS_IN {duration: 230, title: 'frontend developer'}]->(london); +CREATE (stefan)-[:WORKS_IN {duration: 230, title: 'chief architect'}]->(london); +CREATE (stefan)-[:WORKS_IN {duration: 230, title: 'language architect'}]->(berlin); +CREATE (mats)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(malmo); +CREATE (petra)-[:WORKS_IN {duration: 230, title: 'language architect'}]->(london); +CREATE (craig)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(malmo); +CREATE (steven)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(malmo); +CREATE (chris)-[:WORKS_IN {duration: 230, title: 'field engineer'}]->(madrid); +CREATE (london)-[:IN]->(england); +CREATE (me)-[:FRIENDS_WITH]->(andy); +CREATE (andy)-[:FRIENDS_WITH]->(bob); +CREATE (mattias)-[:FRIENDS_WITH]->(max); +CREATE (pontus)-[:FRIENDS_WITH]->(mats); +CREATE (konstantin)-[:FRIENDS_WITH]->(steven); +CREATE (craig)-[:FRIENDS_WITH]->(stefan); +CREATE (petra)-[:FRIENDS_WITH]->(lovis); +CREATE (me)-[:FRIENDS_WITH]->(chris); +CREATE (chris)-[:FRIENDS_WITH]->(stefan); +CREATE (bob)-[:FRIENDS_WITH]->(andy); +CREATE (steven)-[:FRIENDS_WITH]->(mats); +CREATE (mattias)-[:FRIENDS_WITH]->(me); +---- +//// // --- scan and seek operators --- @@ -3334,7 +3402,7 @@ Alternatively, the records to be updated can be returned, followed by an update ---- PROFILE MATCH (a), (b) -DELETE a, b +DETACH DELETE a, b MERGE () ---- @@ -3365,9 +3433,9 @@ Batch size 1024 | +Eager | 5 | read/delete conflict for variable: anon_0, read/delete conflict for variable: a, | 15129 | 15129 | 0 | 243608 | 0/0 | 0.182 | In Pipeline 4 | | | | | read/delete conflict for variable: b | | | | | | | | | | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +Delete | 6 | b | 15129 | 15129 | 122 | | | | | +| +DetachDelete | 6 | b | 15129 | 15129 | 122 | | | | | | | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +Delete | 7 | a | 15129 | 15129 | 1 | | | | | +| +DetachDelete | 7 | a | 15129 | 15129 | 1 | | | | | | | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | | +Eager | 8 | read/delete conflict for variable: b | 15129 | 15129 | 0 | 243608 | 0/0 | 8.515 | Fused in Pipeline 3 | | | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ @@ -5002,6 +5070,13 @@ Runtime version {neo4j-version-minor} Total database accesses: ? ---- +//// +[source, cypher, role=test-setup] +---- +DROP CONSTRAINT uniqueness +---- +//// + ====== [[query-plan-drop-constraint]] @@ -5010,6 +5085,13 @@ Total database accesses: ? The `DropConstraint` operator removes a constraint using the name of the constraint, no matter the type. +//// +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT name IF NOT EXISTS +FOR (book:Book) REQUIRE book.isbn IS UNIQUE +---- +//// .DropConstraint ====== @@ -5183,7 +5265,7 @@ The `DropIndex` operator removes an index using the name of the index. [source, cypher, role="noplay"] ---- PROFILE -DROP INDEX name +DROP INDEX my_index ---- .Query Plan @@ -5195,11 +5277,11 @@ Runtime SCHEMA Runtime version {neo4j-version-minor} -+------------+------------+ -| Operator | Details | -+------------+------------+ -| +DropIndex | INDEX name | -+------------+------------+ ++------------+---------------+ +| Operator | Details | ++------------+---------------+ +| +DropIndex | INDEX my_index| ++------------+---------------+ Total database accesses: ? ---- From 6051fc22f3ffd60a82a575ddb71af959e764f2d2 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Tue, 31 Jan 2023 08:44:34 +0100 Subject: [PATCH 090/383] update syntax/lists.adoc page for test (#365) --- modules/ROOT/pages/syntax/lists.adoc | 54 ++++++++++------------------ 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/modules/ROOT/pages/syntax/lists.adoc b/modules/ROOT/pages/syntax/lists.adoc index 63e748cc8..0db547521 100644 --- a/modules/ROOT/pages/syntax/lists.adoc +++ b/modules/ROOT/pages/syntax/lists.adoc @@ -21,7 +21,7 @@ The behavior of the `IN` and `[]` operators with respect to `null` is detailed x A literal list is created by using brackets and separating the elements in the list with commas. .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] AS list ---- @@ -42,7 +42,7 @@ To access individual elements in the list, you can use the square brackets again This extracts from the start index and up to, but not including, the end index. .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10)[3] ---- @@ -58,7 +58,7 @@ RETURN range(0, 10)[3] You can also use negative numbers, to start from the end of the list instead. .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10)[-3] ---- @@ -74,7 +74,7 @@ RETURN range(0, 10)[-3] Finally, you can use ranges inside the brackets to return ranges of the list. .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10)[0..3] ---- @@ -88,7 +88,7 @@ RETURN range(0, 10)[0..3] |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10)[0..-5] ---- @@ -102,7 +102,7 @@ RETURN range(0, 10)[0..-5] |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10)[-5..] ---- @@ -116,7 +116,7 @@ RETURN range(0, 10)[-5..] |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10)[..4] ---- @@ -135,7 +135,7 @@ Out-of-bound slices are simply truncated, but out-of-bound single elements retur ==== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10)[15] ---- @@ -149,7 +149,7 @@ RETURN range(0, 10)[15] |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN range(0, 10)[5..15] ---- @@ -165,7 +165,7 @@ RETURN range(0, 10)[5..15] You can get the xref::functions/scalar.adoc#functions-size[`size`] of a list as follows: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN size(range(0, 10)[0..3]) ---- @@ -186,7 +186,7 @@ List comprehension is a syntactic construct available in Cypher for creating a l It follows the form of the mathematical set-builder notation (set comprehension) instead of the use of map and filter functions. .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN [x IN range(0,10) WHERE x % 2 = 0 | x^3 ] AS result ---- @@ -202,7 +202,7 @@ RETURN [x IN range(0,10) WHERE x % 2 = 0 | x^3 ] AS result Either the `WHERE` part, or the expression, can be omitted, if you only want to filter or map respectively. .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN [x IN range(0,10) WHERE x % 2 = 0 ] AS result ---- @@ -216,7 +216,7 @@ RETURN [x IN range(0,10) WHERE x % 2 = 0 ] AS result |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN [x IN range(0,10) | x^3 ] AS result ---- @@ -244,6 +244,8 @@ This example returns a list that contains the year when the movies was released. The pattern matching in the pattern comprehension looks for `Matrix` in the movie title and that the node `a` (`Person` node with the name `Keanu Reeves`) has a relationship with the movie. //// +[source, cypher, role=test-setup] +---- CREATE (keanu:Person {name: 'Keanu Reeves'}), (johnnymnemonic:Movie {title: 'Johnny Mnemonic', released: 1995}), @@ -262,10 +264,11 @@ CREATE (keanu)-[:ACTED_IN]->(thematrix), (keanu)-[:ACTED_IN]->(thedevilsadvocate), (keanu)-[:ACTED_IN]->(matrix4) +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:Person {name: 'Keanu Reeves'}) RETURN [(a)-->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.released] AS years @@ -284,29 +287,8 @@ The whole predicate, including the `WHERE` keyword, is optional and may be omitt This example returns a sorted list that contains years. The pattern matching in the pattern comprehension looks for movie nodes that has a relationship with the node `a` (`Person` node with the name `Keanu Reeves`). -//// -CREATE - (keanu:Person {name: 'Keanu Reeves'}), - (johnnymnemonic:Movie {title: 'Johnny Mnemonic', released: 1995}), - (somethingsgottagive:Movie {title: 'Somethings Gotta Give', released: 2003}), - (thematrixrevolutions:Movie {title: 'The Matrix Revolutions', released: 2003}), - (thematrixreloaded:Movie {title: 'The Matrix Reloaded', released: 2003}), - (thereplacements:Movie {title: 'The Replacements', released: 2000}), - (thematrix:Movie {title: 'The Matrix', released: 1999}), - (thedevilsadvocate:Movie {title: 'The Devils Advocate', released: 1997}), - (matrix4:Movie {title: 'The Matrix Resurrections', released: 2021}), - (keanu)-[:ACTED_IN]->(johnnymnemonic), - (keanu)-[:ACTED_IN]->(somethingsgottagive), - (keanu)-[:ACTED_IN]->(thematrixrevolutions), - (keanu)-[:ACTED_IN]->(thematrixreloaded), - (keanu)-[:ACTED_IN]->(thereplacements), - (keanu)-[:ACTED_IN]->(thematrix), - (keanu)-[:ACTED_IN]->(thedevilsadvocate), - (keanu)-[:ACTED_IN]->(matrix4) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:Person {name: 'Keanu Reeves'}) WITH [(a)-->(b:Movie) | b.released] AS years From 3755000408a27907679ec6615bf4086cdd0c2fa6 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Tue, 31 Jan 2023 09:14:18 +0100 Subject: [PATCH 091/383] update syntax/operator.adoc for test (#359) Co-authored-by: Stefano Ottolenghi --- modules/ROOT/pages/syntax/operators.adoc | 79 +++++++++++++----------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/modules/ROOT/pages/syntax/operators.adoc b/modules/ROOT/pages/syntax/operators.adoc index c789794e6..5443017fc 100644 --- a/modules/ROOT/pages/syntax/operators.adoc +++ b/modules/ROOT/pages/syntax/operators.adoc @@ -77,7 +77,7 @@ The aggregation operators comprise: Retrieve the unique eye colors from `Person` nodes. .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE (a:Person {name: 'Anne', eyeColor: 'blue'}), @@ -119,8 +119,15 @@ The property operators pertain to a node or a relationship, and comprise: [[syntax-accessing-the-property-of-a-node-or-relationship]] === Statically accessing a property of a node or relationship using the `.` operator +//// +[source, cypher, role=test-setup] +---- +Match (a:Person) delete a; +---- +//// + .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE (a:Person {name: 'Jane', livesIn: 'London'}), @@ -147,7 +154,7 @@ Labels added: 2 === Filtering on a dynamically-computed property key using the `[]` operator .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE (a:Restaurant {name: 'Hungry Jo', rating_hygiene: 10, rating_food: 7}), @@ -183,16 +190,16 @@ The behavior of the `[]` operator with respect to `null` is detailed xref::synta === Replacing all properties of a node or relationship using the `=` operator .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE (a:Person {name: 'Jane', age: 20}) +CREATE (a:Person {name: 'Sofia', age: 20}) WITH a -MATCH (p:Person {name: 'Jane'}) +MATCH (p:Person {name: 'Sofia'}) SET p = {name: 'Ellen', livesIn: 'London'} RETURN p.name, p.age, p.livesIn ---- -All the existing properties on the node are replaced by those provided in the map; i.e. the `name` property is updated from `Jane` to `Ellen`, the `age` property is deleted, and the `livesIn` property is added. +All the existing properties on the node are replaced by those provided in the map; i.e. the `name` property is updated from `Sofia` to `Ellen`, the `age` property is deleted, and the `livesIn` property is added. .Result [role="queryresult",options="header,footer",cols="3* two AS result @@ -336,7 +343,7 @@ See xref::syntax/operators.adoc#cypher-comparison[] for more details on the beha === Using `STARTS WITH` to filter names .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH ['John', 'Mark', 'Jonathan', 'Bill'] AS somenames UNWIND somenames AS names @@ -457,14 +464,14 @@ Note that `a op1 b op2 c` does not imply any kind of comparison between `a` and The example: -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE 21 < n.age <= 30 RETURN n ---- is equivalent to -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE 21 < n.age AND n.age <= 30 RETURN n ---- @@ -482,14 +489,14 @@ This means that `1=1=true` is equivalent to `1=1 AND 1=true` and not to `(1=1)=t For example: -[source, cypher, role=noplay, indent=0] +[source, syntax, role=noplay] ---- a < b = c <= d <> e ---- Is equivalent to: -[source, cypher, role=noplay, indent=0] +[source, syntax, role=noplay] ---- a < b AND b = c AND c <= d AND d <> e ---- @@ -499,7 +506,7 @@ a < b AND b = c AND c <= d AND d <> e === Using a regular expression with `=~` to filter words .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH ['mouse', 'chair', 'door', 'house'] AS wordlist UNWIND wordlist AS word @@ -551,7 +558,7 @@ Here is the truth table for `AND`, `OR`, `XOR` and `NOT`. === Using boolean operators to filter numbers .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH [2, 4, 7, 9, 12] AS numberlist UNWIND numberlist AS number @@ -660,7 +667,7 @@ The following table shows -- for each combination of operation and operand type === Adding and subtracting a _Duration_ to or from a temporal instant .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH localdatetime({year:1984, month:10, day:11, hour:12, minute:31, second:14}) AS aDateTime, @@ -680,7 +687,7 @@ xref::syntax/temporal.adoc#cypher-temporal-duration-component[Components of a _D For example, when adding a _Duration_ to a _Date_, the _hours_, _minutes_, _seconds_ and _nanoseconds_ of the _Duration_ are ignored (_Time_ behaves in an analogous manner): .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH date({year:1984, month:10, day:11}) AS aDate, @@ -700,7 +707,7 @@ Adding two durations to a temporal instant is not an associative operation. This is because non-existing dates are truncated to the nearest existing date: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN (date("2011-01-31") + duration("P1M")) + duration("P12M") AS date1, @@ -720,7 +727,7 @@ RETURN === Adding and subtracting a _Duration_ to or from another _Duration_ .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH duration({years: 12, months: 5, days: 14, hours: 16, minutes: 12, seconds: 70, nanoseconds: 1}) as duration1, @@ -743,7 +750,7 @@ RETURN duration1, duration2, duration1 + duration2, duration1 - duration2 These operations are interpreted simply as component-wise operations with overflow to smaller units based on an average length of units in the case of division (and multiplication with fractions). .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH duration({days: 14, minutes: 12, seconds: 70, nanoseconds: 1}) AS aDuration RETURN aDuration, aDuration * 2, aDuration / 3 @@ -777,7 +784,7 @@ The behavior of the `[]` operator with respect to `null` is detailed in xref::sy === Statically accessing the value of a nested map by key using the `.` operator .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH {person: {name: 'Anne', age: 25}} AS p RETURN p.person.name @@ -806,7 +813,7 @@ A parameter may be used to specify the key of the value to access: ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH {name: 'Anne', age: 25} AS a RETURN a[$myKey] AS result @@ -842,7 +849,7 @@ The behavior of the `IN` and `[]` operators with respect to `null` is detailed x === Concatenating two lists using `+` .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN [1,2,3,4,5] + [6,7] AS myList ---- @@ -860,7 +867,7 @@ RETURN [1,2,3,4,5] + [6,7] AS myList === Using `IN` to check if a number is in a list .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH [2, 3, 4, 5] AS numberlist UNWIND numberlist AS number @@ -888,7 +895,7 @@ Lists are only comparable to other lists, and elements of a list `innerList` are The following query checks whether or not the list `[2, 1]` is an element of the list `[1, [2, 1], 3]`: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN [2, 1] IN [1, [2, 1], 3] AS inList ---- @@ -907,7 +914,7 @@ If the left-hand operator had been `[1, 2]` instead of `[2, 1]`, the query would At first glance, the contents of the left-hand operand and the right-hand operand _appear_ to be the same in the following query: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN [1, 2] IN [1, 2] AS inList ---- @@ -924,7 +931,7 @@ However, `IN` evaluates to `false` as the right-hand operand does not contain an The following query can be used to ascertain whether or not a list -- obtained from, say, the xref::functions/list.adoc#functions-labels[labels()] function -- contains at least one element that is also present in another list: -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE size([label IN labels(n) WHERE label IN ['Person', 'Employee'] | 1]) > 0 @@ -938,7 +945,7 @@ As long as `labels(n)` returns either `Person` or `Employee` (or both), the quer === Accessing elements in a list using the `[]` operator .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH ['Anne', 'John', 'Bill', 'Diane', 'Eve'] AS names RETURN names[1..3] AS result @@ -969,7 +976,7 @@ A parameter may be used to specify the index of the element to access: ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH ['Anne', 'John', 'Bill', 'Diane', 'Eve'] AS names RETURN names[$myIndex] AS result @@ -998,7 +1005,7 @@ RETURN names[$myIndex] AS result ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH [[1, 2, 3]] AS l RETURN 3 IN l[0] AS result From 88f65783cf98979e10d12452b8a867fdc32d9e4d Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Tue, 31 Jan 2023 10:20:48 +0100 Subject: [PATCH 092/383] Update `Database management` examples (testing) (#366) - Some columns were just missing from current docs output tables. - Only show relevant columns in `SHOW DATABASES` later examples. The first one displays them all, but later on when we use that to show that our commands had the intended effect, it's really an overkill to get 13 columns of output ... and it's painful to maintain for us. - Correct results with missing databases. --- modules/ROOT/pages/databases.adoc | 269 +++++++++++++----------------- 1 file changed, 113 insertions(+), 156 deletions(-) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 7fea50ce3..315887178 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -1,5 +1,14 @@ :description: How to use Cypher to manage databases in Neo4j DBMS: creating, modifying, deleting, starting, and stopping individual databases within a single server. +//// +[source, cypher, role=test-setup] +---- +CREATE DATABASE `movies` WAIT; +CREATE ALIAS `films` FOR DATABASE `movies`; +CREATE ALIAS `motion pictures` FOR DATABASE `movies`; +---- +//// + [[administration-databases]] = Database management @@ -188,7 +197,7 @@ Not returned by `SHOW HOME DATABASE` or `SHOW DEFAULT DATABASE`. ==== | `currentPrimariesCount` -| Number of primaries for this database reported as running currently. +| Number of primaries for this database reported as running currently. It is the same as the number of rows where `role=primary` and `name=this database`. | `currentSecondariesCount` @@ -196,11 +205,11 @@ It is the same as the number of rows where `role=primary` and `name=this databas It is the same as the number of rows where `role=secondary` and `name=this database`. | `requestedPrimariesCount` -| The requested number of primaries for this database. +| The requested number of primaries for this database. May be lower than current if the DBMS is currently reducing the number of copies of the database, or higher if it is currently increasing the number of copies. | `requestedSecondariesCount` -| The requested number of secondaries for this database. +| The requested number of secondaries for this database. May be lower than current if the DBMS is currently reducing the number of copies of the database, or higher if it is currently increasing the number of copies. | creationTime @@ -242,27 +251,21 @@ The lag is expressed in negative integers. In standalone environments, the value A summary of all available databases can be displayed using the command `SHOW DATABASES`. -//// -CREATE DATABASE `movies` -CREATE ALIAS `films` FOR DATABASE `movies` -CREATE ALIAS `motion pictures` FOR DATABASE `movies` -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW DATABASES ---- .Result -[role="queryresult",options="header,footer",cols="10* Date: Tue, 31 Jan 2023 11:19:40 +0100 Subject: [PATCH 093/383] Update Full Text Search Indexes examples for testing (#372) Co-authored-by: JPryce-Aklundh --- .../pages/indexes-for-full-text-search.adoc | 175 ++++++------------ 1 file changed, 55 insertions(+), 120 deletions(-) diff --git a/modules/ROOT/pages/indexes-for-full-text-search.adoc b/modules/ROOT/pages/indexes-for-full-text-search.adoc index 6dccf83ef..cd321827a 100644 --- a/modules/ROOT/pages/indexes-for-full-text-search.adoc +++ b/modules/ROOT/pages/indexes-for-full-text-search.adoc @@ -111,7 +111,7 @@ The syntax descriptions use xref:access-control/index.adoc#access-control-syntax |=== | Command | Description -| [source, cypher, role=noplay, indent=0] +| [source, syntax, role=noplay, indent=0] ---- CREATE FULLTEXT INDEX [index_name] [IF NOT EXISTS] FOR (n:LabelName["\|" ...]) @@ -120,7 +120,7 @@ ON EACH "[" n.propertyName[, ...] "]" ---- | Create a full-text index on nodes. -| [source, cypher, role=noplay, indent=0] +| [source, syntax, role=noplay, indent=0] ---- CREATE FULLTEXT INDEX [index_name] [IF NOT EXISTS] FOR ()-"["r:TYPE_NAME["\|" ...]"]"-() @@ -154,15 +154,8 @@ It may still throw an error should a constraint with the same name exist. For instance, if we have a movie with a title. -//// -CREATE (m:Movie {title: "The Matrix"}) RETURN m.title -CREATE (:Movie {title: "Full Metal Jacket"}), (:Movie {title: "The Jacket"}), (:Movie {title: "Yellow Jacket"}), (:Movie {title: "Full Moon High"}), (:Movie {title: "Metallica Through The Never", description: "The movie follows the young roadie Trip through his surreal adventure with the band."}) -CREATE FULLTEXT INDEX titlesAndDescriptions FOR (n:Movie|Book) ON EACH [n.title, n.description] -CALL db.awaitIndexes(1000) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE (m:Movie {title: "The Matrix"}) RETURN m.title ---- @@ -173,39 +166,27 @@ CREATE (m:Movie {title: "The Matrix"}) RETURN m.title | +m.title+ | +"The Matrix"+ -1+d|Rows: 1 + -Nodes created: 1 + -Properties set: 1 + -Labels added: 1 |=== And we have a full-text index on the `title` and `description` properties of movies and books. -//// -CREATE (m:Movie {title: "The Matrix"}) RETURN m.title -CREATE (:Movie {title: "Full Metal Jacket"}), (:Movie {title: "The Jacket"}), (:Movie {title: "Yellow Jacket"}), (:Movie {title: "Full Moon High"}), (:Movie {title: "Metallica Through The Never", description: "The movie follows the young roadie Trip through his surreal adventure with the band."}) -CREATE FULLTEXT INDEX titlesAndDescriptions FOR (n:Movie|Book) ON EACH [n.title, n.description] -CALL db.awaitIndexes(1000) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE FULLTEXT INDEX titlesAndDescriptions FOR (n:Movie|Book) ON EACH [n.title, n.description] ---- -Then our movie node from above will be included in the index, even though it only has one of the indexed labels, and only one of the indexed properties: +.Result +[queryresult] +---- +Added 1 index. +---- -//// -CREATE (m:Movie {title: "The Matrix"}) RETURN m.title -CREATE (:Movie {title: "Full Metal Jacket"}), (:Movie {title: "The Jacket"}), (:Movie {title: "Yellow Jacket"}), (:Movie {title: "Full Moon High"}), (:Movie {title: "Metallica Through The Never", description: "The movie follows the young roadie Trip through his surreal adventure with the band."}) -CREATE FULLTEXT INDEX titlesAndDescriptions FOR (n:Movie|Book) ON EACH [n.title, n.description] -CALL db.awaitIndexes(1000) -//// +Then our movie node from above will be included in the index, even though it only has one of the indexed labels, and only one of the indexed properties: .Query -[source, cypher, indent=0] +[source, cypher] ---- CALL db.index.fulltext.queryNodes("titlesAndDescriptions", "matrix") YIELD node, score RETURN node.title, node.description, score @@ -216,7 +197,7 @@ RETURN node.title, node.description, score |=== | +node.title+ | +node.description+ | +score+ -| +"The Matrix"+ | ++ | +0.7799721956253052+ +| +"The Matrix"+ | ++ | +0.13076457381248474+ 3+d|Rows: 1 |=== @@ -241,15 +222,8 @@ This means that updates will be applied in a background thread "as soon as possi .+CREATE FULLTEXT INDEX+ ====== -//// -CREATE (m:Movie {title: "The Matrix"}) RETURN m.title -CREATE (:Movie {title: "Full Metal Jacket"}), (:Movie {title: "The Jacket"}), (:Movie {title: "Yellow Jacket"}), (:Movie {title: "Full Moon High"}), (:Movie {title: "Metallica Through The Never", description: "The movie follows the young roadie Trip through his surreal adventure with the band."}) -CREATE FULLTEXT INDEX titlesAndDescriptions FOR (n:Movie|Book) ON EACH [n.title, n.description] -CALL db.awaitIndexes(1000) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE FULLTEXT INDEX taggedByRelationshipIndex FOR ()-[r:TAGGED_AS]-() ON EACH [r.taggedByUser] OPTIONS { @@ -265,14 +239,10 @@ This could, for instance, be a system where people are assigning tags to documen Had it not been for the relationship index, one would have had to add artificial connective nodes between the tags and the documents in the data model, just so these nodes could be indexed. .Result -[role="queryresult",options="footer",cols="1* Date: Tue, 31 Jan 2023 15:36:42 +0100 Subject: [PATCH 094/383] update syntax/expression page for test (#357) Co-authored-by: Stefano Ottolenghi --- modules/ROOT/pages/syntax/expressions.adoc | 656 +-------------------- 1 file changed, 24 insertions(+), 632 deletions(-) diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index cc648d83c..2f36a1928 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -148,7 +148,7 @@ END .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) RETURN @@ -204,22 +204,8 @@ END | If no match is found, `default` is returned. |=== -//// -CREATE - (alice:A {name:'Alice', age: 38, eyes: 'brown'}), - (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel:D {name: 'Daniel', eyes: 'brown'}), - (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), - (alice)-[:KNOWS]->(bob), - (alice)-[:KNOWS]->(charlie), - (bob)-[:KNOWS]->(daniel), - (charlie)-[:KNOWS]->(daniel), - (bob)-[:MARRIED]->(eskil) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) RETURN @@ -249,22 +235,8 @@ END AS result Owing to the close similarity between the syntax of the two forms, sometimes it may not be clear at the outset as to which form to use. We illustrate this scenario by means of the following query, in which there is an expectation that `age_10_years_ago` is `-1` if `n.age` is `null`: -//// -CREATE - (alice:A {name:'Alice', age: 38, eyes: 'brown'}), - (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel:D {name: 'Daniel', eyes: 'brown'}), - (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), - (alice)-[:KNOWS]->(bob), - (alice)-[:KNOWS]->(charlie), - (bob)-[:KNOWS]->(daniel), - (charlie)-[:KNOWS]->(daniel), - (bob)-[:MARRIED]->(eskil) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) RETURN n.name, @@ -293,22 +265,8 @@ This results in the `ELSE n.age - 10` branch being taken instead, returning `nul The corrected query, behaving as expected, is given by the following generic `CASE` form: -//// -CREATE - (alice:A {name:'Alice', age: 38, eyes: 'brown'}), - (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel:D {name: 'Daniel', eyes: 'brown'}), - (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), - (alice)-[:KNOWS]->(bob), - (alice)-[:KNOWS]->(charlie), - (bob)-[:KNOWS]->(daniel), - (charlie)-[:KNOWS]->(daniel), - (bob)-[:MARRIED]->(eskil) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) RETURN n.name, @@ -339,22 +297,9 @@ We now see that the `age_10_years_ago` correctly returns `-1` for the node named You can use the result of `CASE` to set properties on a node or relationship. For example, instead of specifying the node directly, you can set a property for a node selected by an expression: -//// -CREATE - (alice:A {name:'Alice', age: 38, eyes: 'brown'}), - (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel:D {name: 'Daniel', eyes: 'brown'}), - (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), - (alice)-[:KNOWS]->(bob), - (alice)-[:KNOWS]->(charlie), - (bob)-[:KNOWS]->(daniel), - (charlie)-[:KNOWS]->(daniel), - (bob)-[:MARRIED]->(eskil) -//// .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WITH n, @@ -386,20 +331,6 @@ When using the simple `CASE` form, it is useful to remember that in Cypher `null .+CASE+ ====== -//// -CREATE - (alice:A {name:'Alice', age: 38, eyes: 'brown'}), - (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel:D {name: 'Daniel', eyes: 'brown'}), - (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), - (alice)-[:KNOWS]->(bob), - (alice)-[:KNOWS]->(charlie), - (bob)-[:KNOWS]->(daniel), - (charlie)-[:KNOWS]->(daniel), - (bob)-[:MARRIED]->(eskil) -//// - For example, you might expect `age_10_years_ago` to be `-1` for the node named `Daniel`: .Query @@ -442,6 +373,9 @@ Variables introduced inside the subquery are not part of the outside scope and t The following graph is used for the examples below: //// +[source, cypher, role=test-setup] +---- +MATCH (n:A|B|C|D|E) DETACH DELETE n; CREATE (andy:Swedish:Person {name: 'Andy', age: 36}), (timothy:Person {name: 'Timothy', age: 25}), @@ -450,6 +384,7 @@ CREATE (timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) +---- //// image:graph_expression_subqueries.svg[] @@ -496,31 +431,6 @@ RETURN person.name AS name 1+d|Rows: 2 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) -} -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] - [[existential-subquery-with-where]] ==== `EXISTS` subquery with `WHERE` clause @@ -547,31 +457,6 @@ RETURN person.name AS name 1+d|Rows: 1 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(dog:Dog) - WHERE person.name = dog.name -} -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] [[existential-subquery-nesting]] ==== Nesting `EXISTS` subqueries @@ -603,35 +488,6 @@ RETURN person.name AS name 1+d|Rows: 1 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(dog:Dog) - WHERE EXISTS { - MATCH (dog)-[:HAS_TOY]->(toy:Toy) - WHERE toy.name = 'Banana' - } -} -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] - [[existential-subquery-outside-where]] ==== `EXISTS` subquery outside of a `WHERE` clause @@ -658,29 +514,6 @@ RETURN person.name AS name, EXISTS { 2+d|Rows: 3 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) -} AS hasDog -]]> -++++ -endif::nonhtmloutput[] [[existential-subquery-with-union]] ==== `EXISTS` subquery with a `UNION` @@ -712,33 +545,6 @@ RETURN 2+d|Rows: 3 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) - UNION - MATCH (person)-[:HAS_CAT]->(:Cat) - } AS hasPet -]]> -++++ -endif::nonhtmloutput[] [[existential-subquery-with-with]] ==== `EXISTS` subquery with `WITH` @@ -769,32 +575,6 @@ RETURN person.name AS name 1+d|Rows: 1 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(d:Dog) - WHERE d.name = dogName -} -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] [[existential-subquery-with-return]] ==== `EXISTS` subquery with `RETURN` @@ -823,31 +603,6 @@ RETURN person.name AS name 1+d|Rows: 2 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) - RETURN person.name -} -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] [[count-subqueries]] === `COUNT` subqueries @@ -884,28 +639,6 @@ RETURN person.name AS name 1+d|Rows: 1 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) } > 1 -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] [[count-subquery-with-where]] ==== `COUNT` subquery with `WHERE` clause @@ -933,32 +666,6 @@ RETURN person.name AS name 1+d|Rows: 1 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(dog:Dog) - WHERE person.name = dog.name -} = 1 -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] - [[count-subquery-with-union]] ==== `COUNT` subquery with a `UNION` @@ -991,35 +698,6 @@ RETURN 2+d|Rows: 3 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(dog:Dog) - RETURN dog.name AS petName - UNION - MATCH (person)-[:HAS_CAT]->(cat:Cat) - RETURN cat.name AS petName - } AS numPets -]]> -++++ -endif::nonhtmloutput[] [[count-subquery-with-with]] ==== `COUNT` subquery with `WITH` @@ -1050,32 +728,6 @@ RETURN person.name AS name 1+d|Rows: 1 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(d:Dog) - WHERE d.name = dogName -} = 1 -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] [[count-subqueries-other-clauses]] ==== Using `COUNT` subqueries inside other clauses @@ -1105,28 +757,6 @@ RETURN person.name, COUNT { (person)-[:HAS_DOG]->(:Dog) } as howManyDogs 2+d|Rows: 3 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) } as howManyDogs - -]]> -++++ -endif::nonhtmloutput[] [[count-subqueries-with-set]] ===== Using `COUNT` in `SET` @@ -1150,29 +780,6 @@ RETURN person.howManyDogs as howManyDogs Properties set: 1 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) } -RETURN person.howManyDogs as howManyDogs - -]]> -++++ -endif::nonhtmloutput[] [[count-subqueries-with-case]] ===== Using `COUNT` in `CASE` @@ -1200,32 +807,6 @@ RETURN 1+d|Rows: 3 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) } > 1 THEN "Doglover " + person.name - ELSE person.name - END AS result - -]]> -++++ -endif::nonhtmloutput[] [[count-subqueries-as-grouping-key]] ===== Using `COUNT` as a grouping key @@ -1254,30 +835,6 @@ RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs, 2+d|Rows: 3 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) } AS numDogs, - avg(person.age) AS averageAge - ORDER BY numDogs - -]]> -++++ -endif::nonhtmloutput[] [[count-subquery-with-return]] ==== `COUNT` subquery with `RETURN` @@ -1305,31 +862,6 @@ RETURN person.name AS name 1+d|Rows: 1 |=== -ifndef::nonhtmloutput[] -[subs="none"] -++++ - -Try this query live -(:Dog {name:'Andy'}), -(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) - -]]>(:Dog) - RETURN person.name -} = 1 -RETURN person.name AS name -]]> -++++ -endif::nonhtmloutput[] [[label-expressions]] == Label expressions @@ -1563,6 +1095,10 @@ RETURN The following graph is used for the examples below: //// +[source, cypher, role=test-setup] +---- +MATCH (n:Toy|Cat|Dog|Person|Swedish) DETACH DELETE n; + CREATE (:A {name:'Alice'}), (:B {name:'Bob'}), @@ -1572,6 +1108,7 @@ CREATE (:B:C {name:'Frank'}), (:A:B:C {name:'George'}), ({name:'Henry'}) +---- //// image:graph_label_expressions.svg[] @@ -1595,17 +1132,6 @@ A node pattern without a label expression returns all nodes in the graph, includ .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// .Query [source, cypher] @@ -1641,17 +1167,6 @@ A node pattern with a single label returns the nodes that contain the specified .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// .Query [source, cypher] @@ -1684,18 +1199,6 @@ A node pattern with an `AND` expression for the node label returns the nodes tha .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// - .Query [source, cypher] ---- @@ -1725,18 +1228,6 @@ A match with `OR` expressions for the node label returns the nodes that contain .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// - .Query [source, cypher] ---- @@ -1770,18 +1261,6 @@ A node pattern with a `NOT` expression for the node label returns the nodes that .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// - .Query [source, cypher] ---- @@ -1813,18 +1292,6 @@ A node pattern with a `Wildcard` expression for the node label returns all the n .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// - .Query [source, cypher] ---- @@ -1859,18 +1326,6 @@ A node pattern with nested label expressions returns the nodes for which the ful .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// - .Query [source, cypher] ---- @@ -1903,18 +1358,6 @@ A label expression can also be used as a predicate in the `WHERE` clause. .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// - .Query [source, cypher] ---- @@ -1949,18 +1392,6 @@ A label expression can also be used in a `WITH` or a `RETURN` clause. .+Label expression+ ====== -//// -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) -//// - .Query [source, cypher] ---- @@ -2112,6 +1543,16 @@ The following graph is used for the examples below: image:graph_relationship_type_expressions.svg[] +//// +[source, cypher, role=test-setup] +---- +MATCH (_) DETACH DELETE _; +CREATE + (:A:B)-[:R1 {name:'Teaches'}]->(:B), + (:C)-[:R2 {name:'Studies'}]->(:D), + (:E)-[:R3 {name:'Parents'}]->(:F) +---- +//// [discrete] [[relationship-type-expressions-pattern-without-relationship-type-expression]] @@ -2123,13 +1564,6 @@ A relationship pattern without a relationship type expression returns all relati .Relationship type expressions ====== -//// -CREATE - (:A:B)-[:R1 {name:'Teaches'}]->(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) -//// - .Query [source, cypher] ---- @@ -2160,13 +1594,6 @@ A relationship pattern with a single relationship type returns the relationships .Relationship type expression ====== -//// -CREATE - (:A:B)-[:R1 {name:'Teaches'}]->(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) -//// - .Query [source, cypher] ---- @@ -2224,13 +1651,6 @@ A relationship pattern with a `NOT` expression for the relationship type returns .Relationship type expression ====== -//// -CREATE - (:A:B)-[:R1 {name:'Teaches'}]->(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) -//// - .Query [source, cypher] ---- @@ -2260,13 +1680,6 @@ A relationship pattern with a nested relationship type expression returns all re .Relationship type expression ====== -//// -CREATE - (:A:B)-[:R1 {name:'Teaches'}]->(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) -//// - .Query [source, cypher] ---- @@ -2295,13 +1708,6 @@ A relationship type expression can also be used as a predicate in the `WHERE` cl .Relationship type expression ====== -//// -CREATE - (:A:B)-[:R1 {name:'Teaches'}]->(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) -//// - .Query [source, cypher] ---- @@ -2332,13 +1738,6 @@ A relationship type expression can also be used in the `WITH` or `RETURN` clause .Relationship type expression ====== -//// -CREATE - (:A:B)-[:R1 {name:'Teaches'}]->(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) -//// - .Query [source, cypher] ---- @@ -2369,13 +1768,6 @@ A relationship type expression and a label expression can also be used in `CASE` .Relationship type expression ====== -//// -CREATE - (:A:B)-[:R1 {name:'Teaches'}]->(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) -//// - .Query [source, cypher] ---- From f3ce5713c0199e0d54c4bb516312070c3fdec393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 31 Jan 2023 16:31:01 +0100 Subject: [PATCH 095/383] Update Indexes for search-performance examples (testing) (#369) Co-authored-by: JPryce-Aklundh --- .../pages/indexes-for-search-performance.adoc | 382 ++++++------------ 1 file changed, 122 insertions(+), 260 deletions(-) diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index 59a2511ff..0b3fa99b8 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -358,14 +358,14 @@ However, they are always planned as an existence check and a filter and any pred For example, an index on nodes with `:Label(prop1,prop2,prop3,prop4,prop5,prop6)` and predicates: -[source, cypher, role=noplay, indent=0] +[source, cypher, role=test-skip] ---- WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 < 'e' AND n.prop5 = true AND n.prop6 IS NOT NULL ---- will be planned as: -[source, cypher, role=noplay, indent=0] +[source, cypher, role=test-skip] ---- WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 IS NOT NULL AND n.prop5 IS NOT NULL AND n.prop6 IS NOT NULL ---- @@ -374,14 +374,14 @@ with filters on `n.prop4 < 'e'` and `n.prop5 = true`, since `n.prop3` has a `ran And an index on nodes with `:Label(prop1,prop2)` with predicates: -[source, cypher, role=noplay, indent=0] +[source, cypher, role=test-skip] ---- WHERE n.prop1 ENDS WITH 'x' AND n.prop2 = false ---- will be planned as: -[source, cypher, role=noplay, indent=0] +[source, cypher, role=test-skip] ---- WHERE n.prop1 IS NOT NULL AND n.prop2 IS NOT NULL ---- @@ -441,13 +441,16 @@ Note that the index is not immediately available, but is created in the backgrou ====== //// -Set-up to get expected behavior: -CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) -CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) +[source, cypher, role=test-setup] +---- +CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}); +CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}); +CREATE (_0)-[:`KNOWS` {`lastMet`:2021, `lastMetIn`:"Stockholm", `metIn`:"Malmo", `since`:1992}]->(_1) +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE INDEX node_range_index_name FOR (n:Person) ON (n.surname) ---- @@ -460,10 +463,7 @@ The index name must be unique. .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -486,13 +486,6 @@ Note that the index is not immediately available, but is created in the backgrou .+CREATE INDEX+ ====== -//// -Set-up to get expected behavior: -CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) -CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) -CREATE (_0)-[:`KNOWS` {`lastMet`:2021, `lastMetIn`:"Stockholm", `metIn`:"Malmo", `since`:1992}]->(_1) -//// - .Query [source, cypher, indent=0] ---- @@ -507,10 +500,7 @@ The index name must be unique. .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -526,16 +516,8 @@ If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure .+CREATE RANGE INDEX+ ====== -//// -Set-up to get expected behavior: -CREATE RANGE index `node_range_index_name` for (n:`Person`) ON (n.`surname`) - -CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) -CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE INDEX node_range_index_name IF NOT EXISTS FOR (n:Person) ON (n.surname) @@ -549,9 +531,7 @@ The index will not be created if there already exists an index with the same sch .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== @@ -581,10 +561,7 @@ OPTIONS { .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -612,15 +589,8 @@ Note that the composite index is not immediately available, but is created in th The following statement will create a named composite range index on all nodes labeled with `Person` and which have both an `age` and `country` property: -//// -Set-up to get expected behavior: -CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) -CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) ----- -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE INDEX composite_range_node_index_name FOR (n:Person) ON (n.age, n.country) ---- @@ -633,10 +603,7 @@ The index name must be unique. .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -662,15 +629,8 @@ Note that the composite index is not immediately available, but is created in th The following statement will create a named composite range index on all relationships labeled with `PURCHASED` and which have both a `date` and `amount` property: -//// -Set-up to get expected behavior: -CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) -CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}) -CREATE (_1)-[:`KNOWS`]->(_0) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE INDEX composite_range_rel_index_name FOR ()-[r:PURCHASED]-() ON (r.date, r.amount) ---- @@ -683,10 +643,7 @@ The index name must be unique. .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -713,10 +670,12 @@ The index is not immediately available, but is created in the background. ====== //// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') +[source, cypher, role=test-setup] +---- +CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green'}); +CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink'}); +CREATE (n2:Label1 {prop1: 7, prop2: 'Blue'}); +---- //// .Query @@ -733,10 +692,7 @@ Note that a node label lookup index can only be created once and that the index .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -766,16 +722,15 @@ The index is not immediately available, but is created in the background. ====== //// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') -CREATE (n0)-[TYPE1]->(n1) -CREATE (n0)-[TYPE2]->(n2) +[source, cypher, role=test-setup] +---- +CREATE (n0)-[:TYPE1]->(n1); +CREATE (n0)-[:TYPE2]->(n2); +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE LOOKUP INDEX rel_type_lookup_index FOR ()-[r]-() ON EACH type(r) ---- @@ -788,10 +743,7 @@ Note that a relationship type lookup index can only be created once and that the .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -817,10 +769,10 @@ Only one valid value exists for the index provider, `token-lookup-1.0`, which is // hence the `node label lookup index` and `relationship type lookup index` variations above. //// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') +[source, cypher, role=test-setup] +---- +drop index node_label_lookup_index +---- //// @@ -842,10 +794,7 @@ Note that the above command will fail if any node label lookup index already exi .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -871,7 +820,7 @@ Note that the index is not immediately available, but is created in the backgrou ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE POINT INDEX node_index_name FOR (n:Person) ON (n.sublocation) ---- @@ -884,10 +833,7 @@ The index name must be unique. .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -903,7 +849,7 @@ Note that point indexes only recognize point values and do not support multiple A named point index on a single property for all relationships with a particular relationship type can be created with: -[source, syntax, role="noheader"] +[source, syntax] ---- CREATE POINT INDEX index_name FOR ()-[r:TYPE]-() ON (r.property) ---- @@ -915,7 +861,7 @@ Note that the index is not immediately available, but is created in the backgrou ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE POINT INDEX rel_index_name FOR ()-[r:STREET]-() ON (r.intersection) ---- @@ -928,10 +874,7 @@ The index name must be unique. .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -950,13 +893,8 @@ If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure .+CREATE POINT INDEX+ ====== -//// -Set-up to get expected behavior: -CREATE POINT index for (n:`Person`) ON (n.`sublocation`) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE POINT INDEX node_index_name IF NOT EXISTS FOR (n:Person) ON (n.sublocation) @@ -970,9 +908,7 @@ Note that the index will not be created if there already exists an index with th .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== @@ -989,7 +925,7 @@ Only one valid value exists for the index provider, `point-1.0`, which is the de ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE POINT INDEX index_with_provider FOR (n:Label) ON (n.prop1) @@ -1001,10 +937,7 @@ OPTIONS { .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -1051,10 +984,7 @@ OPTIONS { .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -1086,7 +1016,7 @@ Non-specified settings have their respective default values. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE POINT INDEX index_with_options FOR ()-[r:TYPE]-() ON (r.prop1) @@ -1102,10 +1032,7 @@ OPTIONS { .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -1132,17 +1059,10 @@ The index is not immediately available, but is created in the background. .+CREATE TEXT INDEX+ ====== -//// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') -//// - .Query [source, cypher, indent=0] ---- -CREATE TEXT INDEX node_index_name FOR (n:Person) ON (n.nickname) +CREATE TEXT INDEX node_index_nickname FOR (n:Person) ON (n.nickname) ---- [NOTE] @@ -1153,10 +1073,7 @@ The index name must be unique. .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -1185,18 +1102,10 @@ The index is not immediately available, but is created in the background. .+CREATE TEXT INDEX+ ====== -//// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') -CREATE (n0)-[:KNOWS {interest: 'tennis'}]->(n1) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE TEXT INDEX rel_index_name FOR ()-[r:KNOWS]-() ON (r.interest) +CREATE TEXT INDEX relprop_index_name FOR ()-[r:KNOWS]-() ON (r.interest) ---- [NOTE] @@ -1207,10 +1116,7 @@ The index name must be unique. .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -1230,17 +1136,8 @@ If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure .+CREATE TEXT INDEX+ ====== -//// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') - -CREATE TEXT INDEX node_index_name IF NOT EXISTS FOR (n:Person) ON (n.name) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE TEXT INDEX node_index_name IF NOT EXISTS FOR (n:Person) ON (n.nickname) ---- @@ -1253,9 +1150,7 @@ Note that the index will not be created if there already exists an index with th .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== @@ -1271,28 +1166,17 @@ The valid values for the index provider are `text-2.0` and `text-1.0` (deprecate .+CREATE TEXT INDEX+ ====== -//// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') -CREATE (n0)-[:TYPE1 {prop1: 'tennis'}]->(n1) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE TEXT INDEX index_with_provider FOR ()-[r:TYPE]-() ON (r.prop1) +CREATE TEXT INDEX index_with_indexprovider FOR ()-[r:TYPE]-() ON (r.prop1) OPTIONS {indexProvider: 'text-2.0'} ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes added: 1 +Added 1 index. ---- ====== @@ -1309,16 +1193,14 @@ Create an index on the property `title` on nodes with the `Book` label, when tha ====== //// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') - +[source, cypher, role=test-setup] +---- CREATE INDEX example_index FOR (n:Book) ON (n.title) +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- CREATE INDEX bookTitleIndex FOR (book:Book) ON (book.title) ---- @@ -1326,7 +1208,7 @@ CREATE INDEX bookTitleIndex FOR (book:Book) ON (book.title) In this case the index can not be created because it already exists. .Error message -[source, role=nocopy, indent=0] +[source, error] ---- There already exists an index (:Book {title}). ---- @@ -1345,16 +1227,14 @@ Create a named index on the property `numberOfPages` on nodes with the `Book` la ====== //// -Set-up to get expected behavior: -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green') -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink') -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue') - -CREATE INDEX indexOnBooks FOR (n:Label1) ON (b.prop1) +[source, cypher, role=test-setup] +---- +CREATE INDEX indexOnBooks FOR (b:Label1) ON (b.prop1) +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- CREATE INDEX indexOnBooks FOR (book:Book) ON (book.numberOfPages) ---- @@ -1362,7 +1242,7 @@ CREATE INDEX indexOnBooks FOR (book:Book) ON (book.numberOfPages) In this case the index can't be created because there already exists an index with the given name. .Error message -[source, role=nocopy, indent=0] +[source, error] ---- There already exists an index called 'indexOnBooks'. ---- @@ -1381,12 +1261,14 @@ Create an index on the property `isbn` on nodes with the `Book` label, when an i ====== //// -Set-up to get expected behavior: +[source, cypher, role=test-setup] +---- CREATE CONSTRAINT FOR (book:Book) REQUIRE (book.isbn) IS UNIQUE +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- CREATE INDEX bookIsbnIndex FOR (book:Book) ON (book.isbn) ---- @@ -1394,7 +1276,7 @@ CREATE INDEX bookIsbnIndex FOR (book:Book) ON (book.isbn) In this case the index can not be created because a index-backed constraint already exists on that label and property combination. .Error message -[source, role=nocopy, indent=0] +[source, error] ---- There is a uniqueness constraint on (:Book {isbn}), so an index is already created that matches this. ---- @@ -1413,12 +1295,14 @@ Create a named index on the property `numberOfPages` on nodes with the `Book` la ====== //// -Set-up to get expected behavior: +[source, cypher, role=test-setup] +---- CREATE CONSTRAINT bookRecommendations FOR (book:Book) REQUIRE (book.recommend) IS NOT NULL +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- CREATE INDEX bookRecommendations FOR (book:Book) ON (book.recommendations) ---- @@ -1426,7 +1310,7 @@ CREATE INDEX bookRecommendations FOR (book:Book) ON (book.recommendations) In this case the index can not be created because there already exists a constraint with the given name. .Error message -[source, role=nocopy, indent=0] +[source, error] ---- There already exists a constraint called 'bookRecommendations'. ---- @@ -1515,20 +1399,8 @@ If all columns are required, use `SHOW INDEXES YIELD *`. .+SHOW INDEXES+ ====== -//// -Set-up to get expected behavior: -CREATE RANGE INDEX `index_664b28a2` for (n:`Person`) ON (n.`middlename`); -CREATE RANGE INDEX `index_58a1c03e` for (n:`Person`) ON (n.`location`); -CREATE RANGE INDEX `index_8a688dca` for (n:`Person`) ON (n.`highScore`); -CREATE RANGE INDEX `index_b87724c3` for (n:`Person`) ON (n.`firstname`); -CREATE TEXT INDEX `index_763f72db` for (n:`Person`) ON (n.`middlename`); -CREATE TEXT INDEX `index_eadb868e` for (n:`Person`) ON (n.`surname`); -CREATE POINT INDEX `index_c3493fe4` for (n:`Person`) ON (n.`location`); -CREATE CONSTRAINT `constraint_1bc95fcb` for (n:`Person`) require (n.`name`) is unique; -//// - .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- SHOW INDEXES ---- @@ -1543,21 +1415,29 @@ This can be used to drop the index with the xref::indexes-for-search-performance .Result [queryresult] ---- -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 10 | "constraint_1bc95fcb" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["name"] | "range-1.0" | "constraint_1bc95fcb" | -| 4 | "index_58a1c03e" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["location"] | "point-1.0" | | -| 3 | "index_664b28a2" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["middlename"] | "range-1.0" | | -| 7 | "index_763f72db" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["middlename"] | "text-1.0" | | -| 5 | "index_8a688dca" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["highScore"] | "range-1.0" | | -| 6 | "index_b87724c3" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["firstname"] | "range-1.0" | | -| 9 | "index_c3493fe4" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Person"] | ["location"] | "point-1.0" | | -| 1 | "index_d7c12ba3" | "ONLINE" | 100.0 | "LOOKUP" | "NODE" | | | "token-lookup-1.0" | | -| 2 | "index_deeafdb2" | "ONLINE" | 100.0 | "LOOKUP" | "RELATIONSHIP" | | | "token-lookup-1.0" | | -| 8 | "index_eadb868e" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["surname"] | "text-1.0" | | -+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -7 rows ++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | ++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 20 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | +| 2 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | +| 14 | "constraint_1bc95fcb" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["isbn"] | "range-1.0" | "constraint_1bc95fcb" | +| 4 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | +| 7 | "indexOnBooks" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Label1"] | ["prop1"] | "range-1.0" | NULL | +| 10 | "index_with_config" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop2"] | "point-1.0" | NULL | +| 6 | "index_with_indexprovider" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "text-2.0" | NULL | +| 9 | "index_with_options" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "point-1.0" | NULL | +| 8 | "index_with_provider" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop1"] | "point-1.0" | NULL | +| 15 | "node_index_name" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Person"] | ["sublocation"] | "point-1.0" | NULL | +| 3 | "node_index_nickname" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["nickname"] | "text-2.0" | NULL | +| 11 | "node_label_lookup_index_2" | "ONLINE" | 100.0 | "LOOKUP" | "NODE" | NULL | NULL | "token-lookup-1.0" | NULL | +| 16 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | +| 19 | "range_index_with_provider" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "range-1.0" | NULL | +| 13 | "rel_index_name" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["intersection"] | "point-1.0" | NULL | +| 18 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | +| 17 | "rel_type_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "RELATIONSHIP" | NULL | NULL | "token-lookup-1.0" | NULL | +| 1 | "relprop_index_name" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["KNOWS"] | ["interest"] | "text-2.0" | NULL | ++-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +18 rows ---- ====== @@ -1578,19 +1458,8 @@ An example is to only show indexes not belonging to constraints. .+SHOW RANGE INDEXES+ ====== -//// -Set-up to get expected behavior: -CREATE RANGE INDEX `index_664b28a2` for (n:`Person`) ON (n.`middlename`); -CREATE RANGE INDEX `index_8a688dca` for (n:`Person`) ON (n.`highScore`); -CREATE RANGE INDEX `index_b87724c3` for (n:`Person`) ON (n.`firstname`); -CREATE RANGE INDEX `index_6e62c571` for ()-[r:`KNOWS`]-() ON (r.`since`); -CREATE CONSTRAINT `constraint_1bc95fcb` for (n:`Person`) require (n.`name`) is unique; -CREATE TEXT INDEX `index_763f72db` for (n:`Person`) ON (n.`middlename`); -CREATE TEXT INDEX `index_eadb868e` for (n:`Person`) ON (n.`surname`); -//// - .Query -[source, cypher, indent=0] +[source, cypher, role=test-result-skip] ---- SHOW RANGE INDEXES WHERE owningConstraint IS NULL ---- @@ -1607,15 +1476,18 @@ SHOW INDEXES YIELD * WHERE ... .Result [queryresult] ---- -+-----------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | -+-----------------------------------------------------------------------------------------------------------------------------------------------------+ -| 3 | "index_664b28a2" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["middlename"] | "range-1.0" | | -| 6 | "index_6e62c571" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | | -| 4 | "index_8a688dca" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["highScore"] | "range-1.0" | | -| 5 | "index_b87724c3" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["firstname"] | "range-1.0" | | -+-----------------------------------------------------------------------------------------------------------------------------------------------------+ -4 rows ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 20 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | +| 2 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | +| 4 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | +| 7 | "indexOnBooks" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Label1"] | ["prop1"] | "range-1.0" | NULL | +| 16 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | +| 19 | "range_index_with_provider" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "range-1.0" | NULL | +| 18 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +7 rows ---- ====== @@ -1646,24 +1518,16 @@ The name of the index can be found using the xref::indexes-for-search-performanc .+DROP INDEX+ ====== -//// -Set-up to get expected behavior: -CREATE index `index_example` for (n:`Example`) ON (n.`example`); -//// - .Query [source, cypher, indent=0] ---- -DROP INDEX index_name +DROP INDEX example_index ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Indexes removed: 1 +Removed 1 index. ---- ====== @@ -1688,9 +1552,7 @@ DROP INDEX missing_index_name IF EXISTS .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== From 9fe06298cd22b78e58137c76a6e865a22416f1cc Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 1 Feb 2023 13:02:43 +0100 Subject: [PATCH 096/383] Fix formatting in query-tuning/indexes page (#379) --- modules/ROOT/pages/query-tuning/indexes.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index ce3038366..0cb7f8d93 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -42,11 +42,11 @@ See xref::indexes-for-full-text-search.adoc[]. ==== -=== LOOKUP indexes +== LOOKUP indexes `LOOKUP` indexes are present by default and solve only node label and relationship type predicates: -[cols="2", options="header"] +[cols="2, 2a", options="header"] |=== | Predicate | Syntax (example) From 8a7ccf5367fb8c3132bc4f9f9c76936a19741aab Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 1 Feb 2023 13:15:40 +0100 Subject: [PATCH 097/383] Fix `query-tuning` section for tests (#375) - exclude examples pages that rely on csv files (if loaded from URL, we can test them) - add `PROFILE` before all queries because a) output doesn't match the query otherwise; b) testing doesn't otherwise know it should look at query plan. --- .../pages/query-tuning/advanced-example.adoc | 1 + .../pages/query-tuning/basic-example.adoc | 1 + .../pages/query-tuning/query-options.adoc | 4 +- modules/ROOT/pages/query-tuning/using.adoc | 328 +++--------------- 4 files changed, 56 insertions(+), 278 deletions(-) diff --git a/modules/ROOT/pages/query-tuning/advanced-example.adoc b/modules/ROOT/pages/query-tuning/advanced-example.adoc index 94e185d45..9d0cd2b90 100644 --- a/modules/ROOT/pages/query-tuning/advanced-example.adoc +++ b/modules/ROOT/pages/query-tuning/advanced-example.adoc @@ -1,4 +1,5 @@ :description: Example of some more subtle optimizations based on native index capabilities. +:test-skip: true [[advanced-query-tuning-example]] = Advanced query tuning example diff --git a/modules/ROOT/pages/query-tuning/basic-example.adoc b/modules/ROOT/pages/query-tuning/basic-example.adoc index 70ab92d78..fcc029d07 100644 --- a/modules/ROOT/pages/query-tuning/basic-example.adoc +++ b/modules/ROOT/pages/query-tuning/basic-example.adoc @@ -1,4 +1,5 @@ :description: Example how to profile a query, by using optimizations based on native index capabilities. +:test-skip: true [[cypherdoc-basic-query-tuning-example]] = Basic query tuning example diff --git a/modules/ROOT/pages/query-tuning/query-options.adoc b/modules/ROOT/pages/query-tuning/query-options.adoc index a9b5aeae4..4c756d198 100644 --- a/modules/ROOT/pages/query-tuning/query-options.adoc +++ b/modules/ROOT/pages/query-tuning/query-options.adoc @@ -292,7 +292,7 @@ The replan option is prepended to queries. For example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- CYPHER replan=force MATCH ... ---- @@ -303,7 +303,7 @@ Using `EXPLAIN` will make sure the query is only planned, but not executed. For example: -[source, cypher, role=noplay, indent=0] +[source, syntax] ---- CYPHER replan=force EXPLAIN MATCH ... ---- diff --git a/modules/ROOT/pages/query-tuning/using.adoc b/modules/ROOT/pages/query-tuning/using.adoc index 579d28580..03ccfd299 100644 --- a/modules/ROOT/pages/query-tuning/using.adoc +++ b/modules/ROOT/pages/query-tuning/using.adoc @@ -31,28 +31,32 @@ There are three types of planner hints: * Join hints. //// +[source, cypher, role=test-setup] +---- FOREACH(i IN range(1, 100) | CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> (:City)-[:PART_OF]-> (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes +); + +CREATE RANGE INDEX FOR (s:Scientist) ON (s.born); +CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born); +CREATE RANGE INDEX FOR (c:Country) ON (c.formed); +CREATE RANGE INDEX FOR (c:Country) ON (c.name); +CREATE TEXT INDEX FOR (c:Country) ON (c.name); +CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year); +CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location); +CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location); +CALL db.awaitIndexes; +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850})-[:RESEARCHED]-> (sc:Science)<-[i:INVENTED_BY {year: 560}]- @@ -66,7 +70,7 @@ The query above will be used in some of the examples on this page. Without any hints, one index and no join is used. .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -152,29 +156,10 @@ will require the use of a potentially expensive join later in the query plan. The query above can be tuned to pick a different index as the starting point. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850})-[:RESEARCHED]-> (sc:Science)<-[i:INVENTED_BY {year: 560}]- @@ -186,7 +171,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -228,29 +213,10 @@ Total database accesses: 11, total allocated memory: 208 The following query can be tuned to pick a text index. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (c:Country) USING TEXT INDEX c:Country(name) WHERE c.name = 'Country7' @@ -258,7 +224,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -284,29 +250,10 @@ Total database accesses: 2, total allocated memory: 184 The query above can be tuned to pick a relationship index as the starting point. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850})-[:RESEARCHED]-> (sc:Science)<-[i:INVENTED_BY {year: 560}]- @@ -318,7 +265,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -358,29 +305,10 @@ Total database accesses: 7, total allocated memory: 208 The following query can be tuned to pick a text index. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH ()-[i:INVENTED_BY]->() USING TEXT INDEX i:INVENTED_BY(location) WHERE i.location = 'Location7' @@ -388,7 +316,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -416,29 +344,10 @@ Supplying one index hint changed the starting point of the query, but the plan i only has one starting point. If we give the planner yet another index hint, we force it to use two starting points, one at each end of the match. It will then join these two branches using a join operator. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850})-[:RESEARCHED]-> (sc:Science)<-[i:INVENTED_BY {year: 560}]- @@ -451,7 +360,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -496,29 +405,10 @@ Total database accesses: 11, total allocated memory: 768 Supplying multiple index hints can also be useful if the query contains a disjunction (`OR`) in the `WHERE` clause. This makes sure that all hinted indexes are used and the results are joined together with a `Union` and a `Distinct` afterwards. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query [source, cypher, indent=0] ---- +PROFILE MATCH (country:Country) USING INDEX country:Country(name) USING INDEX country:Country(formed) @@ -527,7 +417,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -570,29 +460,10 @@ You can use the same hint to enforce a starting point where no index is applicab === Hinting a label scan -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850})-[:RESEARCHED]-> (sc:Science)<-[i:INVENTED_BY {year: 560}]- @@ -604,7 +475,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -646,29 +517,10 @@ Total database accesses: 309, total allocated memory: 216 === Hinting a relationship type scan -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850})-[:RESEARCHED]-> (sc:Science)<-[i:INVENTED_BY {year: 560}]- @@ -680,7 +532,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -721,29 +573,10 @@ Total database accesses: 305, total allocated memory: 208 Supplying multiple scan hints can also be useful if the query contains a disjunction (`OR`) in the `WHERE` clause. This makes sure that all involved label predicates are solved by a `UnionNodeByLabelsScan`. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person) USING SCAN person:Pioneer USING SCAN person:Scientist @@ -752,7 +585,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -793,29 +626,10 @@ This will negatively affect query performance. In other cases, the hint might fo In the example above using multiple index hints, we saw that the planner chose to do a join, but not on the `p` node. By supplying a join hint in addition to the index hints, we can enforce the join to happen on the `p` node. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850})-[:RESEARCHED]-> (sc:Science)<-[i:INVENTED_BY {year: 560}]- @@ -829,7 +643,7 @@ RETURN * ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -876,29 +690,10 @@ Total database accesses: 10, total allocated memory: 768 A join hint can also be used to force the planner to pick a `NodeLeftOuterHashJoin` or `NodeRightOuterHashJoin` to solve an `OPTIONAL MATCH`. In most cases, the planner will rather use an `OptionalExpand`. -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850}) OPTIONAL MATCH (s)-[:RESEARCHED]->(sc:Science) RETURN * @@ -907,7 +702,7 @@ RETURN * Without any hint, the planner did not use a join to solve the `OPTIONAL MATCH`. .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST @@ -931,29 +726,10 @@ Total database accesses: 6, total allocated memory: 184 ---- -//// -FOREACH(i IN range(1, 100) | - CREATE (:Scientist {born: 1800 + i})-[:RESEARCHED]-> - (:Science)<-[:INVENTED_BY {year: 530 + (i % 50), location: 'Location' + i}]- - (:Pioneer {born: 500 + (i % 50)})-[:LIVES_IN]-> - (:City)-[:PART_OF]-> - (:Country {formed: 400 + i, name:'Country' + i}) -) - -CREATE RANGE INDEX FOR (s:Scientist) ON (s.born) -CREATE RANGE INDEX FOR (p:Pioneer) ON (p.born) -CREATE RANGE INDEX FOR (c:Country) ON (c.formed) -CREATE RANGE INDEX FOR (c:Country) ON (c.name) -CREATE TEXT INDEX FOR (c:Country) ON (c.name) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.year) -CREATE RANGE INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CREATE TEXT INDEX FOR ()-[i:INVENTED_BY]-() ON (i.location) -CALL db.awaitIndexes -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (s:Scientist {born: 1850}) OPTIONAL MATCH (s)-[:RESEARCHED]->(sc:Science) USING JOIN ON s @@ -963,7 +739,7 @@ RETURN * Now the planner uses a join to solve the `OPTIONAL MATCH`. .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryresult"] ---- Planner COST From 37b0e8b0015f7fa8b8c29995e0c0e97bc7fdae63 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Thu, 2 Feb 2023 08:58:54 +0100 Subject: [PATCH 098/383] Update /clauses doc to avoid empty result from driver (#374) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the test code in this PR: https://github.com/neo4j/docs-testing/pull/3 we notice there it more page to fix I fix some of them here as example --------- Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- modules/ROOT/pages/clauses/call-subquery.adoc | 12 +-- modules/ROOT/pages/clauses/call.adoc | 8 ++ modules/ROOT/pages/clauses/create.adoc | 11 ++- modules/ROOT/pages/clauses/limit.adoc | 7 +- .../pages/clauses/listing-procedures.adoc | 8 +- .../ROOT/pages/clauses/optional-match.adoc | 3 + modules/ROOT/pages/clauses/order-by.adoc | 3 + modules/ROOT/pages/clauses/remove.adoc | 3 + modules/ROOT/pages/clauses/return.adoc | 3 + modules/ROOT/pages/clauses/set.adoc | 90 +++++++++++++++++++ modules/ROOT/pages/clauses/skip.adoc | 3 + modules/ROOT/pages/clauses/union.adoc | 7 +- modules/ROOT/pages/clauses/with.adoc | 3 + modules/ROOT/pages/databases.adoc | 1 - 14 files changed, 143 insertions(+), 19 deletions(-) diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/clauses/call-subquery.adoc index 34dd53746..50a3dbb8e 100644 --- a/modules/ROOT/pages/clauses/call-subquery.adoc +++ b/modules/ROOT/pages/clauses/call-subquery.adoc @@ -111,11 +111,11 @@ RETURN | +innerReturn+ | +totalCount+ -|+1+ |+3+ +| +1+ | +3+ -|+2+ |+3+ +| +2+ | +3+ -|+3+ |+3+ +| +3+ | +3+ 2+d|Rows:3 |=== @@ -708,10 +708,10 @@ RETURN largeLists [role="queryresult",options="header,footer",cols="1*(neo)<-[:WORKS_AT]-(michael {name: 'Michael'}) RETURN p @@ -284,7 +287,7 @@ In this case we add a `Person` label to the node as well. ---- .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE (n:Person $props) RETURN n diff --git a/modules/ROOT/pages/clauses/limit.adoc b/modules/ROOT/pages/clauses/limit.adoc index 3f8ae2f2a..6ad743a03 100644 --- a/modules/ROOT/pages/clauses/limit.adoc +++ b/modules/ROOT/pages/clauses/limit.adoc @@ -13,6 +13,8 @@ image:graph_limit_clause.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (a {name: 'A'}), (b {name: 'B'}), @@ -23,6 +25,7 @@ CREATE (a)-[:KNOWS]->(c), (a)-[:KNOWS]->(d), (a)-[:KNOWS]->(e) +---- //// @@ -127,7 +130,7 @@ Properties set: 1 If we want to limit the number of updates we can split the query using the `WITH` clause: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WITH n LIMIT 1 @@ -141,7 +144,7 @@ Writes `locked` property on one node and return that node: [role="queryresult",options="header,footer",cols="1*(thePresident), (rob)-[:DIRECTED]->(thePresident), (charlie)-[:FATHER]->(martin) +---- //// [[optional-relationships]] diff --git a/modules/ROOT/pages/clauses/order-by.adoc b/modules/ROOT/pages/clauses/order-by.adoc index 15e033981..7a5d28780 100644 --- a/modules/ROOT/pages/clauses/order-by.adoc +++ b/modules/ROOT/pages/clauses/order-by.adoc @@ -29,12 +29,15 @@ The following graph is used for the examples below: image:graph_order_by_clause.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (a {name: 'A', age: 34, length: 170}), (b {name: 'B', age: 36}), (c {name: 'C', age: 32, length: 185}), (a)-[:KNOWS]->(b), (b)-[:KNOWS]->(c) +---- //// [NOTE] diff --git a/modules/ROOT/pages/clauses/remove.adoc b/modules/ROOT/pages/clauses/remove.adoc index 51f4fda9f..17f81c3f5 100644 --- a/modules/ROOT/pages/clauses/remove.adoc +++ b/modules/ROOT/pages/clauses/remove.adoc @@ -24,12 +24,15 @@ The examples use the following database: image:graph_remove_clause.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (a:Swedish {name: 'Andy', age: 36}), (t:Swedish {name: 'Timothy', age: 25}), (p:German:Swedish {name: 'Peter', age: 34}), (a)-[:KNOWS]->(t), (a)-[:KNOWS]->(p) +---- //// diff --git a/modules/ROOT/pages/clauses/return.adoc b/modules/ROOT/pages/clauses/return.adoc index 22ebe051f..ada80b933 100644 --- a/modules/ROOT/pages/clauses/return.adoc +++ b/modules/ROOT/pages/clauses/return.adoc @@ -20,11 +20,14 @@ This will improve performance. image:graph_return_clause.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (a {name: 'A', happy: 'Yes!', age: 55}), (b {name: 'B'}), (a)-[:KNOWS]->(b), (a)-[:BLOCKS]->(b) +---- //// diff --git a/modules/ROOT/pages/clauses/set.adoc b/modules/ROOT/pages/clauses/set.adoc index 5c6a57245..1e3cebc74 100644 --- a/modules/ROOT/pages/clauses/set.adoc +++ b/modules/ROOT/pages/clauses/set.adoc @@ -21,6 +21,8 @@ The examples use this graph as a starting point: image:graph_set_clause.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (a:Swedish {name: 'Andy', age: 36, hungry: true}), (b {name: 'Stefan'}), @@ -29,6 +31,7 @@ CREATE (a)-[:KNOWS]->(c), (b)-[:KNOWS]->(a), (d)-[:KNOWS]->(c) +---- //// @@ -78,6 +81,22 @@ Properties set: 1 No action will be taken if the node expression evaluates to `null`, as shown in this example: +//// +[source, cypher, role=test-setup] +---- +MATCH(n) DETACH DELETE n; +CREATE + (a:Swedish {name: 'Andy', age: 36, hungry: true}), + (b {name: 'Stefan'}), + (c {name: 'Peter', age: 34}), + (d {name: 'George'}), + (a)-[:KNOWS]->(c), + (b)-[:KNOWS]->(a), + (d)-[:KNOWS]->(c) +---- +//// + + .Query [source, cypher, indent=0] ---- @@ -149,6 +168,20 @@ The `name` property is now missing. Properties set: 1 |=== +//// +[source, cypher, role=test-setup] +---- +MATCH(n) DETACH DELETE n; +CREATE + (a:Swedish {name: 'Andy', age: 36, hungry: true}), + (b {name: 'Stefan'}), + (c {name: 'Peter', age: 34}), + (d {name: 'George'}), + (a)-[:KNOWS]->(c), + (b)-[:KNOWS]->(a), + (d)-[:KNOWS]->(c) +---- +//// [[set-copying-properties-between-nodes-and-relationships]] == Copy properties between nodes and relationships @@ -177,6 +210,20 @@ The `'Andy'` node has had all its properties replaced by the properties of the ` Properties set: 3 |=== +//// +[source, cypher, role=test-setup] +---- +MATCH (n) DETACH DELETE n; +CREATE + (a:Swedish {name: 'Andy', age: 36, hungry: true}), + (b {name: 'Stefan'}), + (c {name: 'Peter', age: 34}), + (d {name: 'George'}), + (a)-[:KNOWS]->(c), + (b)-[:KNOWS]->(a), + (d)-[:KNOWS]->(c) +---- +//// [[set-replace-properties-using-map]] == Replace all properties using a map and `=` @@ -202,6 +249,20 @@ This query updated the `name` property from `Peter` to `Peter Smith`, deleted th Properties set: 3 |=== +//// +[source, cypher, role=test-setup] +---- +MATCH (n) DETACH DELETE n; +CREATE + (a:Swedish {name: 'Andy', age: 36, hungry: true}), + (b {name: 'Stefan'}), + (c {name: 'Peter', age: 34}), + (d {name: 'George'}), + (a)-[:KNOWS]->(c), + (b)-[:KNOWS]->(a), + (d)-[:KNOWS]->(c) +---- +//// [[set-remove-properties-using-empty-map]] == Remove all properties using an empty map and `=` @@ -227,6 +288,20 @@ This query removed all the existing properties -- namely, `name` and `age` -- fr Properties set: 2 |=== +//// +[source, cypher, role=test-setup] +---- +MATCH (n) DETACH DELETE n; +CREATE + (a:Swedish {name: 'Andy', age: 36, hungry: true}), + (b {name: 'Stefan'}), + (c {name: 'Peter', age: 34}), + (d {name: 'George'}), + (a)-[:KNOWS]->(c), + (b)-[:KNOWS]->(a), + (d)-[:KNOWS]->(c) +---- +//// [[set-setting-properties-using-map]] == Mutate specific properties using a map and `+=` @@ -257,6 +332,21 @@ This query left the `name` property unchanged, updated the `age` property from ` Properties set: 3 |=== +//// +[source, cypher, role=test-setup] +---- +MATCH (n) DETACH DELETE n; +CREATE + (a:Swedish {name: 'Andy', age: 36, hungry: true}), + (b {name: 'Stefan'}), + (c {name: 'Peter', age: 34}), + (d {name: 'George'}), + (a)-[:KNOWS]->(c), + (b)-[:KNOWS]->(a), + (d)-[:KNOWS]->(c) +---- +//// + xref::clauses/set.adoc#set-remove-properties-using-empty-map[In contrast to the property replacement operator `=`], providing an empty map as the right operand to `+=` will not remove any existing properties from a node or relationship. In line with the semantics detailed above, passing in an empty map with `+=` will have no effect: diff --git a/modules/ROOT/pages/clauses/skip.adoc b/modules/ROOT/pages/clauses/skip.adoc index 9e2d3ba13..f6729ff4f 100644 --- a/modules/ROOT/pages/clauses/skip.adoc +++ b/modules/ROOT/pages/clauses/skip.adoc @@ -15,6 +15,8 @@ Please note that no guarantees are made on the order of the result unless the qu image:graph_skip_clause.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (a {name: 'A'}), (b {name: 'B'}), @@ -25,6 +27,7 @@ CREATE (a)-[:KNOWS]->(c), (a)-[:KNOWS]->(d), (a)-[:KNOWS]->(e) +---- //// diff --git a/modules/ROOT/pages/clauses/union.adoc b/modules/ROOT/pages/clauses/union.adoc index 25221e6aa..0493b752c 100644 --- a/modules/ROOT/pages/clauses/union.adoc +++ b/modules/ROOT/pages/clauses/union.adoc @@ -28,6 +28,8 @@ For details see xref::introduction/clause_composition.adoc#cypher-clause-composi image:graph_union_clause.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (ah:Actor {name: 'Anthony Hopkins'}), (hm:Actor {name: 'Helen Mirren'}), @@ -36,6 +38,7 @@ CREATE (ah)-[:KNOWS]->(hm), (ah)-[:ACTS_IN]->(hitchcockMovie), (hm)-[:ACTS_IN]->(hitchcockMovie) +---- //// @@ -45,7 +48,7 @@ CREATE Combining the results from two queries is done using `UNION ALL`. .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Actor) RETURN n.name AS name @@ -74,7 +77,7 @@ The combined result is returned, including duplicates. By not including `ALL` in the `UNION`, duplicates are removed from the combined result set. .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Actor) RETURN n.name AS name diff --git a/modules/ROOT/pages/clauses/with.adoc b/modules/ROOT/pages/clauses/with.adoc index 0ad6b03ed..c02ba7bbe 100644 --- a/modules/ROOT/pages/clauses/with.adoc +++ b/modules/ROOT/pages/clauses/with.adoc @@ -35,6 +35,8 @@ When going from a writing part to a reading part, the switch must be done with a image:graph_with_clause.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (a {name: 'Anders'}), (b {name: 'Bossman'}), @@ -47,6 +49,7 @@ CREATE (b)-[:KNOWS]->(e), (c)-[:KNOWS]->(e), (b)-[:BLOCKS]->(d) +---- //// diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 315887178..8ce597d89 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -1134,4 +1134,3 @@ A command with a `WAIT` clause may be interrupted whilst it is waiting to comple In this event the command will continue to execute in the background and will not be aborted. ====== - From c349fe5458dd74a521788255c2dd87232187ca78 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Thu, 2 Feb 2023 12:42:07 +0100 Subject: [PATCH 099/383] update functions/*.adoc for avoid empty result (#378) --- .../ROOT/pages/functions/mathematical-numeric.adoc | 3 +++ modules/ROOT/pages/functions/predicate.adoc | 11 +++++++---- modules/ROOT/pages/functions/scalar.adoc | 3 +++ modules/ROOT/pages/syntax/maps.adoc | 3 +++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/functions/mathematical-numeric.adoc b/modules/ROOT/pages/functions/mathematical-numeric.adoc index 728126905..a636474f0 100644 --- a/modules/ROOT/pages/functions/mathematical-numeric.adoc +++ b/modules/ROOT/pages/functions/mathematical-numeric.adoc @@ -26,6 +26,8 @@ The following graph is used for the examples below: image:graph_numeric_functions.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (alice:A {name:'Alice', age: 38, eyes: 'brown'}), (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), @@ -37,6 +39,7 @@ CREATE (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) +---- //// diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 2e1034125..7d2fdc062 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -21,6 +21,8 @@ Functions: image:graph_predicate_functions.svg[] //// +[source, cypher, role=test-setup] +---- CREATE (alice {name:'Alice', age: 38, eyes: 'brown'}), (bob {name: 'Bob', age: 25, eyes: 'blue'}), @@ -34,6 +36,7 @@ CREATE (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) +---- //// @@ -171,7 +174,7 @@ The query returns nodes with the property `liked_colors` (as a list), where at l |=== | +n+ -| +Node[4]{eyes:"blue",liked_colors:["pink","yellow","black"],name:"Eskil",age:41}+ +| +Node[4]{eyes:'blue',liked_colors:['pink', 'yellow', 'black'],name:'Eskil',age:41}+ 1+d|Rows: 1 |=== @@ -293,7 +296,7 @@ isEmpty(list) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE NOT isEmpty(n.liked_colors) @@ -306,8 +309,8 @@ The nodes with the property `liked_colors` being non-empty are returned. [role="queryresult",options="header,footer",cols="1*(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) +---- //// diff --git a/modules/ROOT/pages/syntax/maps.adoc b/modules/ROOT/pages/syntax/maps.adoc index 0fdca4a31..91814da8d 100644 --- a/modules/ROOT/pages/syntax/maps.adoc +++ b/modules/ROOT/pages/syntax/maps.adoc @@ -27,6 +27,8 @@ If returned through an link:{neo4j-docs-base-uri}/http-api/{page-version}/index# If returned in Java, an object of type `java.util.Map` will be returned. //// +[source, cypher, role=test-setup] +---- CREATE (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), (martin:Person {name: 'Martin Sheen'}), @@ -38,6 +40,7 @@ CREATE (charlie)-[:ACTED_IN]->(apocalypsenow), (martin)-[:ACTED_IN]->(wallstreet), (martin)-[:ACTED_IN]->(apocalypsenow) +---- //// .Query From cc764cce8b23742eb06f0c73b6a81079bd1a9f60 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Thu, 2 Feb 2023 15:10:32 +0100 Subject: [PATCH 100/383] skip styleguide.adoc for test (#380) --- modules/ROOT/pages/styleguide.adoc | 106 ++++++++++++++--------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/modules/ROOT/pages/styleguide.adoc b/modules/ROOT/pages/styleguide.adoc index 0441df7ac..9dc513891 100644 --- a/modules/ROOT/pages/styleguide.adoc +++ b/modules/ROOT/pages/styleguide.adoc @@ -39,13 +39,13 @@ Arguments should normally not be included. * Start a new clause on a new line. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE n.name CONTAINS 's' RETURN n.name ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE n.name CONTAINS 's' @@ -56,7 +56,7 @@ RETURN n.name Put `ON CREATE` before `ON MATCH` if both are present. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MERGE (n) ON CREATE SET n.prop = 0 MERGE (a:A)-[:T]-(b:B) @@ -66,7 +66,7 @@ RETURN a.prop ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MERGE (n) ON CREATE SET n.prop = 0 @@ -80,7 +80,7 @@ RETURN a.prop Leave the closing brace on its own line. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:A) WHERE @@ -89,7 +89,7 @@ RETURN a.foo ---- + .Also bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:A) WHERE EXISTS @@ -99,7 +99,7 @@ RETURN a.foo ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:A) WHERE EXISTS { @@ -112,7 +112,7 @@ RETURN a.foo * Do not break the line if the simplified subquery form is used. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:A) WHERE EXISTS { @@ -122,7 +122,7 @@ RETURN a.prop ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:A) WHERE EXISTS { (a)-->(b:B) } @@ -136,7 +136,7 @@ RETURN a.prop * Write keywords in upper case. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- match (p:Person) where p.name starts with 'Ma' @@ -144,7 +144,7 @@ return p.name ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (p:Person) WHERE p.name STARTS WITH 'Ma' @@ -154,14 +154,14 @@ RETURN p.name * Write the value `null` in lower case. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- WITH NULL AS n1, Null AS n2 RETURN n1 IS NULL AND n2 IS NOT NULL ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- WITH null AS n1, null AS n2 RETURN n1 IS NULL AND n2 IS NOT NULL @@ -170,14 +170,14 @@ RETURN n1 IS NULL AND n2 IS NOT NULL * Write boolean literals (`true` and `false`) in lower case. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- WITH TRUE AS b1, False AS b2 RETURN b1 AND b2 ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- WITH true AS b1, false AS b2 RETURN b1 AND b2 @@ -190,7 +190,7 @@ RETURN b1 AND b2 ** parameters + .Bad -[source, cypher, indent=0] +[source, cypher] ---- CREATE (N {Prop: 0}) WITH RAND() AS Rand, $pArAm AS MAP @@ -198,7 +198,7 @@ RETURN Rand, MAP.property_key, Count(N) ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- CREATE (n {prop: 0}) WITH rand() AS rand, $param AS map @@ -218,14 +218,14 @@ RETURN rand, map.propertyKey, count(n) ** No space between the last value and the closing brace + .Bad -[source, cypher, indent=0] +[source, cypher] ---- WITH { key1 :'value' ,key2 : 42 } AS map RETURN map ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- WITH {key1: 'value', key2: 42} AS map RETURN map @@ -234,14 +234,14 @@ RETURN map * One space between label/type predicates and property predicates in patterns. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (p:Person{property: -1})-[:KNOWS {since: 2016}]->() RETURN p.name ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (p:Person {property: -1})-[:KNOWS {since: 2016}]->() RETURN p.name @@ -250,14 +250,14 @@ RETURN p.name * No space in patterns. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Person) --> (:Vehicle) RETURN count(*) ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Person)-->(:Vehicle) RETURN count(*) @@ -266,7 +266,7 @@ RETURN count(*) * Use a wrapping space around operators. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH p=(s)-->(e) WHERE s.name<>e.name @@ -274,7 +274,7 @@ RETURN length(p) ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH p = (s)-->(e) WHERE s.name <> e.name @@ -284,14 +284,14 @@ RETURN length(p) * No space in label predicates. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (person : Person : Owner ) RETURN person.name ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (person:Person:Owner) RETURN person.name @@ -300,7 +300,7 @@ RETURN person.name * Use a space after each comma in lists and enumerations. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (),() WITH ['a','b',3.14] AS list @@ -308,7 +308,7 @@ RETURN list,2,3,4 ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (), () WITH ['a', 'b', 3.14] AS list @@ -318,13 +318,13 @@ RETURN list, 2, 3, 4 * No padding space within function call parentheses. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- RETURN split( 'original', 'i' ) ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- RETURN split('original', 'i') ---- @@ -332,7 +332,7 @@ RETURN split('original', 'i') * Use padding space within simple subquery expressions. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:A) WHERE EXISTS {(a)-->(b:B)} @@ -340,7 +340,7 @@ RETURN a.prop ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:A) WHERE EXISTS { (a)-->(b:B) } @@ -354,7 +354,7 @@ RETURN a.prop * When patterns wrap lines, break after arrows, not before. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Person)-->(vehicle:Car)-->(:Company) <--(:Country) @@ -362,7 +362,7 @@ RETURN count(vehicle) ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Person)-->(vehicle:Car)-->(:Company)<-- (:Country) @@ -372,7 +372,7 @@ RETURN count(vehicle) * Use anonymous nodes and relationships when the variable would not be used. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- CREATE (a:End {prop: 42}), (b:End {prop: 3}), @@ -380,7 +380,7 @@ CREATE (a:End {prop: 42}), ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- CREATE (a:End {prop: 42}), (:End {prop: 3}), @@ -390,14 +390,14 @@ CREATE (a:End {prop: 42}), * Chain patterns together to avoid repeating variables. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Person)-->(vehicle:Car), (vehicle:Car)-->(:Company) RETURN count(vehicle) ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Person)-->(vehicle:Car)-->(:Company) RETURN count(vehicle) @@ -406,7 +406,7 @@ RETURN count(vehicle) * Put named nodes before anonymous nodes. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH ()-->(vehicle:Car)-->(manufacturer:Company) WHERE manufacturer.foundedYear < 2000 @@ -414,7 +414,7 @@ RETURN vehicle.mileage ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (manufacturer:Company)<--(vehicle:Car)<--() WHERE manufacturer.foundedYear < 2000 @@ -424,7 +424,7 @@ RETURN vehicle.mileage * Keep anchor nodes at the beginning of the `MATCH` clause. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Person)-->(vehicle:Car)-->(manufacturer:Company) WHERE manufacturer.foundedYear < 2000 @@ -432,7 +432,7 @@ RETURN vehicle.mileage ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (manufacturer:Company)<--(vehicle:Car)<--(:Person) WHERE manufacturer.foundedYear < 2000 @@ -442,14 +442,14 @@ RETURN vehicle.mileage * Prefer outgoing (left to right) pattern relationships to incoming pattern relationships. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Country)-->(:Company)<--(vehicle:Car)<--(:Person) RETURN vehicle.mileage ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (:Person)-->(vehicle:Car)-->(:Company)<--(:Country) RETURN vehicle.mileage @@ -462,13 +462,13 @@ RETURN vehicle.mileage * Use single quotes, `'`, for literal string values. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- RETURN "Cypher" ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- RETURN 'Cypher' ---- @@ -478,13 +478,13 @@ If the string has both, use the form that creates the fewest escapes. In the case of a tie, prefer single quotes. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- RETURN 'Cypher\'s a nice language', "Mats' quote: \"statement\"" ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- RETURN "Cypher's a nice language", 'Mats\' quote: "statement"' ---- @@ -492,14 +492,14 @@ RETURN "Cypher's a nice language", 'Mats\' quote: "statement"' * Avoid having to use back-ticks to escape characters and keywords. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- MATCH (`odd-ch@racter$`:`Spaced Label` {`&property`: 42}) RETURN labels(`odd-ch@racter$`) ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- MATCH (node:NonSpacedLabel {property: 42}) RETURN labels(node) @@ -508,13 +508,13 @@ RETURN labels(node) * Do not use a semicolon at the end of the statement. + .Bad -[source, cypher, indent=0] +[source, cypher] ---- RETURN 1; ---- + .Good -[source, cypher, indent=0] +[source, cypher] ---- RETURN 1 ---- From 89812b6202029cc31e61d86b6760edc3ec6af3bf Mon Sep 17 00:00:00 2001 From: Balazs Lendvai <56266523+gfx54b@users.noreply.github.com> Date: Thu, 2 Feb 2023 14:27:49 +0000 Subject: [PATCH 101/383] It is possible now to go 1->n with primaries. (#383) Co-authored-by: Jessica Wright <49636617+AlexicaWright@users.noreply.github.com> --- modules/ROOT/pages/databases.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 8ce597d89..475201f25 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -823,7 +823,7 @@ SET TOPOLOGY 3 PRIMARY 0 SECONDARIES [NOTE] ==== -It is not possible to automatically transition to or from a topology with a single primary host. +It is not possible to automatically transition *from* a topology with _multiple_ primary hosts *to* a topology with a _single_ primary host, but it is possible to increase the number of primaries from one to more. See the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/clustering/databases#_alter_topology[Operations Manual -> Alter topology] for more information. ==== From d2c4ec1f3e83cf0dac789745f0260cfc6c8fe10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 3 Feb 2023 09:39:25 +0100 Subject: [PATCH 102/383] Update Constraints examples for testing (#367) - Queries updated for test to be able to run sequentially on the whole page. - Changed the "Set-up to get expected behavior" comments to test-setup queries where necessary. (Do we need the "Set-up to get expected behavior" comments now?) - All queries behind the feature flag currently have `role=test-skip` to be removed once the feature is introduced. The TODO comments have been updated accordingly. --------- Co-authored-by: JPryce-Aklundh --- modules/ROOT/pages/constraints/examples.adoc | 626 ++++++++----------- 1 file changed, 247 insertions(+), 379 deletions(-) diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc index 246d35387..fbbc1a784 100644 --- a/modules/ROOT/pages/constraints/examples.adoc +++ b/modules/ROOT/pages/constraints/examples.adoc @@ -35,19 +35,16 @@ When creating a property uniqueness constraint, a name can be provided. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name +CREATE CONSTRAINT book_isbn FOR (book:Book) REQUIRE book.isbn IS UNIQUE ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Unique constraints added: 1 +Added 1 constraint. ---- //// @@ -65,7 +62,8 @@ The statistics will be updated to say `Node uniqueness constraints` in Neo4j ver [[constraints-create-a-node-uniqueness-constraint-if-not-exist]] === Handling existing constraints when creating a constraint -Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. +Creating an already existing constraint will fail. +To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and no constraint is created if any other constraint with the given name or another node property uniqueness constraint on the same schema already exists. @@ -73,21 +71,18 @@ This will ensure that no error is thrown and no constraint is created if any oth ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name IF NOT EXISTS -FOR (book:Book) REQUIRE book.isbn IS UNIQUE +CREATE CONSTRAINT book_isbn2 IF NOT EXISTS +FOR (book:Book) REQUIRE book.isbn2 IS UNIQUE ---- -Assuming no constraint with the given name or other node property uniqueness constraint on the same schema exists: +Assuming no constraint with the given name or other node property uniqueness constraint on the same schema already exists, the query will return: .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Unique constraints added: 1 +Added 1 constraint. ---- //// @@ -120,22 +115,19 @@ The only valid value for the index provider is: ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE CONSTRAINT constraint_with_options -FOR (n:Label) REQUIRE (n.prop1, n.prop2) IS UNIQUE +FOR (book:Book) REQUIRE (book.prop1, book.prop2) IS UNIQUE OPTIONS { - indexProvider: 'range-1.0', + indexProvider: 'range-1.0' } ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Unique constraints added: 1 +Added 1 constraint. ---- //// @@ -159,26 +151,28 @@ There is no valid index configuration values for the constraint-backing range in .+CREATE CONSTRAINT+ ====== -Create a property uniqueness constraint on the property `title` on nodes with the `Book` label, when that constraint already exists. +Create a property uniqueness constraint on the property `published` on nodes with the `Book` label, when that constraint already exists. //// -Set-up to get expected behavior: -CREATE CONSTRAINT preExistingUnique FOR (book:Book) REQUIRE book.title IS UNIQUE +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT preExisting_book_published FOR (book:Book) REQUIRE book.published IS UNIQUE +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.title IS UNIQUE +CREATE CONSTRAINT book_published FOR (book:Book) REQUIRE book.published IS UNIQUE ---- In this case the constraint can not be created because it already exists. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Constraint already exists: -Constraint( id=4, name='preExistingUnique', type='UNIQUENESS', schema=(:Book {title}), ownedIndex=3 ) +Constraint( id=4, name='preExisting_book_published', type='UNIQUENESS', schema=(:Book {published}), ownedIndex=3 ) ---- //// @@ -203,20 +197,22 @@ The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6. Create a property uniqueness constraint on the property `wordCount` on nodes with the `Book` label, when an index already exists on that label and property combination. //// -Set-up to get expected behavior: -CREATE INDEX FOR (book:Book) ON (book.wordCount) +[source, cypher, role=test-setup] +---- +CREATE INDEX preExisting_book_word_count FOR (book:Book) ON (book.wordCount) +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.wordCount IS UNIQUE +CREATE CONSTRAINT book_word_count FOR (book:Book) REQUIRE book.wordCount IS UNIQUE ---- In this case the constraint can not be created because there already exists an index covering that schema. .Error message -[source, "error message", role="noheader"] +[source, error] ---- There already exists an index (:Book {wordCount}). A constraint cannot be created until the index has been dropped. @@ -235,13 +231,8 @@ A constraint cannot be created until the index has been dropped. Create a `Book` node with an `isbn` that is not already in the database. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS UNIQUE -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) ---- @@ -249,12 +240,7 @@ CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Nodes created: 1 -Properties set: 2 -Labels added: 1 +Added 1 label, created 1 node, set 2 properties ---- ====== @@ -270,14 +256,8 @@ Labels added: 1 Create a `Book` node with an `isbn` that is already used in the database. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS UNIQUE -CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) -//// - .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) ---- @@ -285,7 +265,7 @@ CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) In this case the node is not created in the graph. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Node(0) already exists with label `Book` and property `isbn` = '1449356265' ---- @@ -301,28 +281,30 @@ Node(0) already exists with label `Book` and property `isbn` = '1449356265' .+CREATE CONSTRAINT+ ====== -Create a property uniqueness constraint on the property `isbn` on nodes with the `Book` label when there are two nodes with the same `isbn`. +Create a property uniqueness constraint on the property `title` on nodes with the `Book` label when there are two nodes with the same `title`. //// -Set-up to get expected behavior: -CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) -CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) +[source, cypher, role=test-setup] +---- +CREATE (book:Book {isbn: '9780393972832', title: 'Moby Dick'}); +CREATE (book:Book {isbn: '9780763630188', title: 'Moby Dick'}) +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS UNIQUE +CREATE CONSTRAINT book_title FOR (book:Book) REQUIRE book.title IS UNIQUE ---- In this case the constraint can not be created because it is violated by existing data. Either use xref::indexes-for-search-performance.adoc[] instead, or remove the offending nodes and then re-apply the constraint. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Unable to create Constraint( name='constraint_62365a16', type='UNIQUENESS', schema=(:Book {isbn}) ): -Both Node(0) and Node(1) have the label `Book` and property `isbn` = '1449356265' +Unable to create Constraint( name='book_title', type='UNIQUENESS', schema=(:Book {title}) ): +Both Node(0) and Node(1) have the label `Book` and property `title` = 'Moby Dick' ---- //// @@ -337,6 +319,7 @@ The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6. //// TODO: Re-add this part when adding back relationship key and uniqueness constraints +TODO: Remove 'test-skip' message on queries when feature is introduced [[constraints-examples-relationship-uniqueness]] == Relationship property uniqueness constraints @@ -363,7 +346,7 @@ When creating a property uniqueness constraint, a name can be provided. ====== .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT constraint_name FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE @@ -394,7 +377,7 @@ This will ensure that no error is thrown and no constraint is created if any oth ====== .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT constraint_name IF NOT EXISTS FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE @@ -433,7 +416,7 @@ The only valid value for the index provider is: ====== .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT constraint_with_options FOR ()-[friend:FRIENDS_WITH]-() REQUIRE (friend.nickname, friend.since) IS UNIQUE @@ -470,7 +453,7 @@ Create a property uniqueness constraint on the property `nickname` on relationsh // CREATE CONSTRAINT preExistingUnique FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE ---- @@ -478,7 +461,7 @@ CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNI In this case, the constraint cannot be created because it already exists. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Constraint already exists: Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', schema=()-[:FRIENDS_WITH {nickname}]-(), ownedIndex=3 ) @@ -501,7 +484,7 @@ Create a property uniqueness constraint on the property `nickname` on relationsh // CREATE INDEX FOR ()-[friend:FRIENDS_WITH]-() ON (friend.nickname) .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE ---- @@ -509,7 +492,7 @@ CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNI In this case, the constraint cannot be created because there already exists an index covering that schema. .Error message -[source, "error message", role="noheader"] +[source, error] ---- There already exists an index ()-[:FRIENDS_WITH {nickname}]-(). A constraint cannot be created until the index has been dropped. @@ -532,7 +515,7 @@ Create a `FRIENDS_WITH` relationship with an `nickname` that is not already in t // CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE (:Person {name: 'Josefin'})-[:FRIENDS_WITH {nickname: 'Mimi'}]->(:Person {name: 'Emilia'}) ---- @@ -567,7 +550,7 @@ Create a `FRIENDS_WITH` relationship with an `nickname` that is already used in // CREATE (:Person {name: 'Emma'}), (:Person {name: 'Josefin'})-[:FRIENDS_WITH {nickname: 'Mimi'}]->(:Person {name: 'Emilia'}) .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- MATCH (emma:Person {name: 'Emma'}), (emilia:Person {name: 'Emilia'}) CREATE (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) @@ -576,7 +559,7 @@ CREATE (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) In this case, the relationship is not created in the graph. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Relationship(0) already exists with type `FRIENDS_WITH` and property `nickname` = 'Mimi' ---- @@ -599,7 +582,7 @@ Create a property uniqueness constraint on the property `nickname` on relationsh // CREATE (josefin)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia), (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT friends FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE ---- @@ -608,7 +591,7 @@ In this case, the constraint cannot be created because it is violated by existin Either use xref::indexes-for-search-performance.adoc[] instead, or remove the offending relationships and then re-apply the constraint. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Unable to create Constraint( name='friends', type='RELATIONSHIP_UNIQUENESS', schema=()-[:FRIENDS_WITH {nickname}]-() ): Both Relationship(0) and Relationship(1) have the type `FRIENDS_WITH` and property `nickname` = 'Mimi' @@ -643,19 +626,16 @@ When creating a node property existence constraint, a name can be provided. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name -FOR (book:Book) REQUIRE book.isbn IS NOT NULL +CREATE CONSTRAINT author_name +FOR (author:Author) REQUIRE author.name IS NOT NULL ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Property existence constraints added: 1 +Added 1 constraint. ---- //// @@ -683,25 +663,26 @@ This will ensure that no error is thrown and no constraint is created if any oth ====== //// -Set-up to get expected behavior: -CREATE CONSTRAINT constraint_name FOR (book:Book) REQUIRE book.isbn IS UNIQUE +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT author_pseudonym +FOR (author:Author) REQUIRE author.pseudonym IS UNIQUE +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name IF NOT EXISTS -FOR (book:Book) REQUIRE book.isbn IS NOT NULL +CREATE CONSTRAINT author_pseudonym IF NOT EXISTS +FOR (author:Author) REQUIRE author.pseudonym IS NOT NULL ---- -Assuming a constraint with the name `constraint_name` already existed: +Assuming a constraint with the name `author_pseudonym` already existed: .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== @@ -715,27 +696,21 @@ Assuming a constraint with the name `constraint_name` already existed: .+CREATE CONSTRAINT+ ====== -Create a node property existence constraint on the property `title` on nodes with the `Book` label, when that constraint already exists. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT preExistingNodePropExist FOR (book:Book) REQUIRE book.title IS NOT NULL -//// +Create a node property existence constraint on the property `name` on nodes with the `Author` label, when that constraint already exists. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT booksShouldHaveTitles -FOR (book:Book) REQUIRE book.title IS NOT NULL +CREATE CONSTRAINT author_name +FOR (author:Author) REQUIRE author.name IS NOT NULL ---- In this case the constraint can not be created because it already exists. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Constraint already exists: -Constraint( id=3, name='preExistingNodePropExist', type='NODE PROPERTY EXISTENCE', schema=(:Book {title}) ) +An equivalent constraint already exists, 'Constraint( id=10, name='author_name', type='NODE PROPERTY EXISTENCE', schema=(:Author {name}) )'. ---- ====== @@ -749,28 +724,18 @@ Constraint( id=3, name='preExistingNodePropExist', type='NODE PROPERTY EXISTENCE .+CREATE NODE+ ====== -Create a `Book` node with an `isbn` property. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS NOT NULL -//// +Create an `Author` node with a `name` property. .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) +CREATE (author:Author {name:'Virginia Woolf'}) ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Nodes created: 1 -Properties set: 2 -Labels added: 1 +Added 1 label, created 1 node, set 1 properties ---- ====== @@ -784,25 +749,20 @@ Labels added: 1 .+CREATE NODE+ ====== -Trying to create a `Book` node without an `isbn` property, given a property existence constraint on `:Book(isbn)`. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS NOT NULL -//// +Trying to create an `Author` node without a `name` property, given a property existence constraint on `:Author(name)`. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE (book:Book {title: 'Graph Databases'}) +CREATE (author:Author) ---- In this case the node is not created in the graph. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Node(0) with label `Book` must have the property `isbn` +Node(0) with label `Author` must have the property `name` ---- ====== @@ -816,27 +776,21 @@ Node(0) with label `Book` must have the property `isbn` .+REMOVE PROPERTY+ ====== -Trying to remove the `isbn` property from an existing node `book`, given a property existence constraint on `:Book(isbn)`. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS NOT NULL -CREATE (book:Book {isbn: '1449356265', title: 'Graph Databases'}) -//// +Trying to remove the `name` property from an existing node `Author`, given a property existence constraint on `:Author(name)`. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -MATCH (book:Book {title: 'Graph Databases'}) -REMOVE book.isbn +MATCH (author:Author {name: 'Virginia Woolf'}) +REMOVE author.name ---- In this case the property is not removed. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Node(0) with label `Book` must have the property `isbn` +Node(0) with label `Author` must have the property `name` ---- ====== @@ -850,27 +804,22 @@ Node(0) with label `Book` must have the property `isbn` .+CREATE CONSTRAINT+ ====== -Create a constraint on the property `isbn` on nodes with the `Book` label when there already exists a node without an `isbn`. - -//// -Set-up to get expected behavior: -CREATE (book:Book {title: 'Graph Databases'}) -//// +Create a constraint on the property `nationality` on nodes with the `Author` label when there already exists a node without a `nationality` property. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.isbn IS NOT NULL +CREATE CONSTRAINT author_nationality FOR (author:Author) REQUIRE author.nationality IS NOT NULL ---- In this case the constraint can't be created because it is violated by existing data. Remove the offending nodes and then re-apply the constraint. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Unable to create Constraint( type='NODE PROPERTY EXISTENCE', schema=(:Book {isbn}) ): -Node(0) with label `Book` must have the property `isbn` +Unable to create Constraint( type='NODE PROPERTY EXISTENCE', schema=(:Author {nationality}) ): +Node(0) with label `Author` must have the property `nationality` ---- ====== @@ -902,19 +851,16 @@ When creating a relationship property existence constraint, a name can be provid ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name -FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL +CREATE CONSTRAINT wrote_year +FOR ()-[wrote:WROTE]-() REQUIRE wrote.year IS NOT NULL ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Property existence constraints added: 1 +Added 1 constraint. ---- //// @@ -941,26 +887,19 @@ This will ensure that no error is thrown and no constraint is created if any oth .+CREATE CONSTRAINT+ ====== -//// -Set-up to get expected behavior: -CREATE CONSTRAINT constraint_name FOR (book:Book) REQUIRE book.isbn IS NOT NULL -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name -IF NOT EXISTS FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL +CREATE CONSTRAINT wrote_year IF NOT EXISTS +FOR ()-[wrote:WROTE]-() REQUIRE wrote.year IS NOT NULL ---- -Assuming a constraint with the name `constraint_name` already existed: +Assuming that such a constraint already existed: .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== @@ -974,26 +913,28 @@ Assuming a constraint with the name `constraint_name` already existed: .+CREATE CONSTRAINT+ ====== -Create a named relationship property existence constraint on the property `week` on relationships with the `LIKED` type, when a constraint with the given name already exists. +Create a named relationship property existence constraint on the property `locations` on relationships with the `WROTE` type, when a constraint with the given name already exists. //// -Set-up to get expected behavior: -CREATE CONSTRAINT relPropExist FOR ()-[like:LIKED]-() REQUIRE like.since IS NOT NULL +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT wrote_locations FOR ()-[wrote:WROTE]-() REQUIRE wrote.location IS NOT NULL +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT relPropExist -FOR ()-[like:LIKED]-() REQUIRE like.week IS NOT NULL +CREATE CONSTRAINT wrote_locations +FOR ()-[wrote:WROTE]-() REQUIRE wrote.locations IS NOT NULL ---- In this case the constraint can not be created because there already exists a constraint with the given name. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -There already exists a constraint called 'relPropExist'. +There already exists a constraint called 'wrote_locations'. ---- ====== @@ -1007,29 +948,18 @@ There already exists a constraint called 'relPropExist'. .+CREATE RELATIONSHIP+ ====== -Create a `LIKED` relationship with a `day` property. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL -//// +Create a `WROTE` relationship with a `year` and `location` property, given property existence constraints on `:WROTE(year)` and `:WROTE(location)`. .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE (user:User)-[like:LIKED {day: 'yesterday'}]->(book:Book) +CREATE (author:Author {name: 'Emily Brontë'})-[wrote:WROTE {year: 1847, location: 'Haworth, United Kingdom'}]->(book:Book {title:'Wuthering Heights', isbn: 9789186579296}) ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Nodes created: 2 -Relationships created: 1 -Properties set: 1 -Labels added: 2 +Added 2 labels, created 2 nodes, set 5 properties, created 1 relationship ---- ====== @@ -1043,25 +973,20 @@ Labels added: 2 .+CREATE RELATIONSHIP+ ====== -Trying to create a `LIKED` relationship without a `day` property, given a property existence constraint `:LIKED(day)`. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL -//// +Trying to create a `WROTE` relationship without a `location` property, given a property existence constraint `:WROTE(location)`. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE (user:User)-[like:LIKED]->(book:Book) +CREATE (author:Author {name: 'Charlotte Brontë'})-[wrote:WROTE {year: 1847}]->(book:Book {title: 'Jane Eyre', isbn:9780194241762}) ---- In this case the relationship is not created in the graph. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Relationship(0) with type `LIKED` must have the property `day` +Relationship(0) with type `WROTE` must have the property `location` ---- ====== @@ -1075,26 +1000,20 @@ Relationship(0) with type `LIKED` must have the property `day` .+REMOVE PROPERTY+ ====== -Trying to remove the `day` property from an existing relationship `like` of type `LIKED`, given a property existence constraint `:LIKED(day)`. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL -CREATE (user:User)-[like:LIKED {day: 'yesterday'}]->(book:Book) -//// +Trying to remove the `location` property from an existing relationship of type `WROTE`, given a property existence constraint `:WROTE(location)`. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -MATCH (user:User)-[like:LIKED]->(book:Book) REMOVE like.day +MATCH (author:Author)-[wrote:WROTE]->(book:Book) REMOVE wrote.location ---- In this case the property is not removed. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Relationship(0) with type `LIKED` must have the property `day` +Relationship(0) with type `WROTE` must have the property `location` ---- ====== @@ -1108,27 +1027,22 @@ Relationship(0) with type `LIKED` must have the property `day` .+CREATE CONSTRAINT+ ====== -Create a constraint on the property `day` on relationships with the `LIKED` type when there already exists a relationship without a property named `day`. - -//// -Set-up to get expected behavior: -CREATE (user:User)-[like:LIKED]->(book:Book) -//// +Create a constraint on the property `language` on relationships with the `WROTE` type when there already exists a relationship without a property named `language`. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR ()-[like:LIKED]-() REQUIRE like.day IS NOT NULL +CREATE CONSTRAINT wrote_language FOR ()-[wrote:WROTE]-() REQUIRE wrote.language IS NOT NULL ---- In this case the constraint can not be created because it is violated by existing data. Remove the offending relationships and then re-apply the constraint. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Unable to create Constraint( type='RELATIONSHIP PROPERTY EXISTENCE', schema=-[:LIKED {day}]- ): -Relationship(0) with type `LIKED` must have the property `day` +Unable to create Constraint( type='RELATIONSHIP PROPERTY EXISTENCE', schema=()-[:WROTE {language}]-() ): +Relationship(0) with type `WROTE` must have the property `language` ---- ====== @@ -1162,19 +1076,16 @@ When creating a node key constraint, a name can be provided. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name -FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY +CREATE CONSTRAINT actor_fullname +FOR (actor:Actor) REQUIRE (actor.firstname, actor.surname) IS NODE KEY ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Node key constraints added: 1 +Added 1 constraint. ---- ====== @@ -1192,26 +1103,19 @@ This will ensure that no error is thrown and no constraint is created if any oth .+CREATE CONSTRAINT+ ====== -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name IF NOT EXISTS -FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY +CREATE CONSTRAINT actor_names IF NOT EXISTS +FOR (actor:Actor) REQUIRE (actor.firstname, actor.surname) IS NODE KEY ---- -Assuming a node key constraint on `(:Person {firstname, surname})` already existed: +Assuming a node key constraint on `(:Actor {firstname, surname})` already existed: .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== @@ -1234,10 +1138,10 @@ The only valid value for the index provider is: ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE CONSTRAINT constraint_with_provider -FOR (n:Label) REQUIRE (n.prop1) IS NODE KEY +FOR (actor:Actor) REQUIRE (actor.surname) IS NODE KEY OPTIONS { indexProvider: 'range-1.0' } @@ -1246,10 +1150,7 @@ OPTIONS { .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Node key constraints added: 1 +Added 1 constraint. ---- ====== @@ -1265,26 +1166,28 @@ There is no valid index configuration values for the constraint-backing range in .+CREATE CONSTRAINT+ ====== -Create a node key constraint on the properties `firstname` and `age` on nodes with the `Person` label, when a property uniqueness constraint already exists on the same label and property combination. +Create a node key constraint on the properties `firstname` and `age` on nodes with the `Actor` label, when a property uniqueness constraint already exists on the same label and property combination. //// -Set-up to get expected behavior: -CREATE CONSTRAINT preExistingUnique FOR (p:Person) REQUIRE (p.firstname, p.age) IS UNIQUE +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT preExisting_actor_name_age FOR (actor:Actor) REQUIRE (actor.firstname, actor.age) IS UNIQUE +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR (p:Person) REQUIRE (p.firstname, p.age) IS NODE KEY +CREATE CONSTRAINT actor_name_age FOR (actor:Actor) REQUIRE (actor.firstname, actor.age) IS NODE KEY ---- In this case the constraint can not be created because there already exist a conflicting constraint on that label and property combination. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Constraint already exists: -Constraint( id=4, name='preExistingUnique', type='UNIQUENESS', schema=(:Person {firstname, age}), ownedIndex=3 ) +Constraint( id=10, name='preExisting_actor_name_age', type='UNIQUENESS', schema=(:Actor {firstname, age}), ownedIndex=9 ) ---- ====== @@ -1298,26 +1201,28 @@ Constraint( id=4, name='preExistingUnique', type='UNIQUENESS', schema=(:Person { .+CREATE CONSTRAINT+ ====== -Create a named node key constraint on the property `title` on nodes with the `Book` label, when an index already exists with the given name. +Create a named node key constraint on the property `citizenship` on nodes with the `Actor` label, when an index already exists with the given name. //// -Set-up to get expected behavior: -CREATE INDEX bookTitle FOR (book:ComicBook) ON (book.title) +[source, cypher, role=test-setup] +---- +CREATE INDEX citizenship FOR (person:Person) ON (person.citizenship) +---- //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT bookTitle -FOR (book:Book) REQUIRE book.title IS NODE KEY +CREATE CONSTRAINT citizenship +FOR (actor:Actor) REQUIRE actor.citizenship IS NODE KEY ---- In this case the constraint can't be created because there already exists an index with the given name. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -There already exists an index called 'bookTitle'. +There already exists an index called 'citizenship'. ---- ====== @@ -1331,28 +1236,18 @@ There already exists an index called 'bookTitle'. .+CREATE NODE+ ====== -Create a `Person` node with both a `firstname` and `surname` property. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY -//// +Create an `Actor` node with a `firstname` and `surname` property. .Query -[source, cypher, indent=0] +[source, cypher] ---- -CREATE (p:Person {firstname: 'John', surname: 'Wood', age: 55}) +CREATE (actor:Actor {firstname: 'Keanu', surname: 'Reeves'}) ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Nodes created: 1 -Properties set: 3 -Labels added: 1 +Added 1 label, created 1 node, set 2 properties. ---- ====== @@ -1366,25 +1261,21 @@ Labels added: 1 .+CREATE NODE+ ====== -Trying to create a `Person` node without a `surname` property, given a node key constraint on `:Person(firstname, surname)`, will fail. +Trying to create an `Actor` node without a `firstname` property, given a node key constraint on `:Actor(firstname, surname)`, will fail. -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY -//// .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE (p:Person {firstname: 'Jane', age: 34}) +CREATE (actor:Actor {surname: 'Wood'}) ---- In this case the node is not created in the graph. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Node(0) with label `Person` must have the properties (`firstname`, `surname`) +Node(0) with label `Actor` must have the properties (`firstname`, `surname`) ---- ====== @@ -1398,26 +1289,20 @@ Node(0) with label `Person` must have the properties (`firstname`, `surname`) .+REMOVE PROPERTY+ ====== -Trying to remove the `surname` property from an existing node `Person`, given a `NODE KEY` constraint on `:Person(firstname, surname)`. - -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY -CREATE (p:Person {firstname: 'John', surname: 'Wood', age: 55}) -//// +Trying to remove the `firstname` property from an existing node `Actor`, given a `NODE KEY` constraint on `:Actor(firstname, surname)`. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -MATCH (p:Person {firstname: 'John', surname: 'Wood'}) REMOVE p.surname +MATCH (actor:Actor {firstname: 'Keanu', surname: 'Reeves'}) REMOVE actor.firstname ---- In this case the property is not removed. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Node(0) with label `Person` must have the properties (`firstname`, `surname`) +Node(0) with label `Actor` must have the properties (`firstname`, `surname`) ---- ====== @@ -1431,27 +1316,22 @@ Node(0) with label `Person` must have the properties (`firstname`, `surname`) .+CREATE CONSTRAINT+ ====== -Trying to create a node key constraint on the property `surname` on nodes with the `Person` label will fail when a node without a `surname` already exists in the database. - -//// -Set-up to get expected behavior: -CREATE (p:Person {firstname: 'John', age: 55}) -//// +Trying to create a node key constraint on the property `born` on nodes with the `Actor` label will fail when a node without a `born` property already exists in the database. .Query -[source, cypher, indent=0] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR (n:Person) REQUIRE (n.firstname, n.surname) IS NODE KEY +CREATE CONSTRAINT actor_born FOR (actor:Actor) REQUIRE (actor.born) IS NODE KEY ---- In this case the node key constraint can not be created because it is violated by existing data. Either use xref::indexes-for-search-performance.adoc[] instead, or remove the offending nodes and then re-apply the constraint. .Error message -[source, "error message", role="noheader"] +[source, error] ---- -Unable to create Constraint( type='NODE KEY', schema=(:Person {firstname, surname}) ): -Node(0) with label `Person` must have the properties (`firstname`, `surname`) +Unable to create Constraint( type='NODE KEY', schema=(:Actor {born}) ): +Node(0) with label `Actor` must have the property `born` ---- ====== @@ -1459,6 +1339,7 @@ Node(0) with label `Person` must have the properties (`firstname`, `surname`) //// TODO: Re-add this part when adding back relationship key and uniqueness constraints [role=enterprise-edition] +TODO: Remove role=skip-test from queries once feature is introduced [[constraints-examples-relationship-key]] == Relationship key constraints @@ -1487,7 +1368,7 @@ When creating a relationship key constraint, a name can be provided. ====== .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT constraint_name FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY @@ -1521,7 +1402,7 @@ This will ensure that no error is thrown and no constraint is created if any oth // CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT constraint_name IF NOT EXISTS FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY @@ -1557,7 +1438,7 @@ The only valid value for the index provider is: ====== .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT constraint_with_provider FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY @@ -1594,7 +1475,7 @@ Create a relationship key constraint on the properties `startPoint` and `endPoin // CREATE CONSTRAINT preExistingUnique FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY ---- @@ -1602,7 +1483,7 @@ CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL K In this case, the constraint cannot be created because there already exists a conflicting constraint on that relationship type and property combination. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Constraint already exists: Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', schema=()-[:ROAD {startPoint, endPoint}]-(), ownedIndex=3 ) @@ -1625,7 +1506,7 @@ Create a named relationship key constraint on the property `coordinates` on rela // CREATE INDEX intersections FOR ()-[intersect:Roundabout]-() ON (intersect.coordinates) .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT intersections FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY @@ -1634,7 +1515,7 @@ FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY In this case, the constraint cannot be created because there already exists an index with the given name. .Error message -[source, "error message", role="noheader"] +[source, error] ---- There already exists an index called 'intersections'. ---- @@ -1657,7 +1538,7 @@ Create a `ROAD` relationship with both a `startPoint` and `endPoint` property. // CREATE (:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- MATCH (a:Intersection {name: 'a'}), (b:Intersection {name: 'b'}) CREATE (a)-[:ROAD {startPoint: a.coordinates, endPoint: b.coordinates}]->(b) @@ -1691,7 +1572,7 @@ Trying to create a `INTERSECTION` relationship without a `coordinates` property, // CREATE (:Road {name: 'a'}), (:Road {name: 'b'}) .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- MATCH (a:Road {name: 'a'}), (b:Road {name: 'b'}) CREATE (a)-[:INTERSECTION]->(b) @@ -1700,7 +1581,7 @@ CREATE (a)-[:INTERSECTION]->(b) In this case, the relationship is not created in the graph. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Relationship(0) with type `INTERSECTION` must have the property `coordinates` ---- @@ -1724,7 +1605,7 @@ Trying to remove the `endPoint` property from an existing relationship `ROAD`, g // CREATE (a)-[:ROAD {startPoint: a.coordinates, endPoint: b.coordinates}]->(b) .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- MATCH ()-[r:ROAD {startPoint: point({x: 1, y:2}), endPoint: point({x: 2, y:5})}]->() REMOVE r.endPoint ---- @@ -1732,7 +1613,7 @@ MATCH ()-[r:ROAD {startPoint: point({x: 1, y:2}), endPoint: point({x: 2, y:5})}] In this case, the property is not removed. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Relationship(0) with type `ROAD` must have the properties (`startPoint`, `endPoint`) ---- @@ -1756,7 +1637,7 @@ Trying to create a relationship key constraint on the property `coordinates` on // CREATE (a)<-[:INTERSECTION {coordinates: point({x:1, y:2})}]-(b) .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE CONSTRAINT intersectionConstraint FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY ---- @@ -1765,7 +1646,7 @@ In this case, the relationship key constraint cannot be created because it is vi Either use xref::indexes-for-search-performance.adoc[] instead, or remove the offending relationships and then re-apply the constraint. .Error message -[source, "error message", role="noheader"] +[source, error] ---- Unable to create Constraint( name='intersectionConstraint', type='RELATIONSHIP KEY', schema=()-[:INTERSECTION {coordinates}]-() ): Both Relationship(0) and Relationship(1) have the type `INTERSECTION` and property `coordinates` = {geometry: {type: "Point", coordinates: [1.0, 2.0], crs: {type: link, properties: {href: "http://spatialreference.org/ref/sr-org/7203/", code: 7203}}}} @@ -1795,24 +1676,16 @@ The name of the constraint can be found using the xref::constraints/syntax.adoc# .+DROP CONSTRAINT+ ====== -//// -Set-up to get expected behavior: -CREATE CONSTRAINT constraint_name FOR (n:Person) REQUIRE (n.name) IS NOT NULL -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- -DROP CONSTRAINT constraint_name +DROP CONSTRAINT book_isbn ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Named constraints removed: 1 +Removed 1 constraint. ---- ====== @@ -1831,7 +1704,7 @@ It is the same command for uniqueness, property existence, and node constraints. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- DROP CONSTRAINT missing_constraint_name IF EXISTS ---- @@ -1839,9 +1712,7 @@ DROP CONSTRAINT missing_constraint_name IF EXISTS .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== @@ -1871,40 +1742,42 @@ This can be used to drop the constraint with the xref::constraints/syntax.adoc#c .+SHOW CONSTRAINTS+ ====== -//// -Set-up to get expected behavior: -CREATE CONSTRAINT isbnConstraint FOR (n:Book) REQUIRE (n.isbn) IS UNIQUE -(TODO: not relevant until the constraint is re-added) CREATE CONSTRAINT roadConstraint FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW CONSTRAINTS ---- [queryresult] ---- -+---------------------------------------------------------------------------------------------------+ -| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | -+---------------------------------------------------------------------------------------------------+ -| 4 | "isbnConstraint" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | "isbnConstraint" | -+---------------------------------------------------------------------------------------------------+ -2 rows ----- - -//// -TODO: Switch the result above to the one below when adding back relationship key and uniqueness constraints -[queryresult] ----- -+------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | -+------------------------------------------------------------------------------------------------------------------------------------+ -| 4 | "isbnConstraint" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | "isbnConstraint" | -| 6 | "roadConstraint" | "RELATIONSHIP_UNIQUENESS" | "RELATIONSHIP" | ["ROAD"] | ["startPoint", "endPoint"] | "roadConstraint" | -+------------------------------------------------------------------------------------------------------------------------------------+ -2 rows ----- +╒════╤════════════════════════════╤═════════════════════════════════╤══════════════╤═══════════════╤═══════════════════════╤════════════════════════════╕ +│"id"│"name" │"type" │"entityType" │"labelsOrTypes"│"properties" │"ownedIndex" │ +╞════╪════════════════════════════╪═════════════════════════════════╪══════════════╪═══════════════╪═══════════════════════╪════════════════════════════╡ +│16 │"actor_fullname" │"NODE_KEY" │"NODE" │["Actor"] │["firstname","surname"]│"actor_fullname" │ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│10 │"author_name" │"NODE_PROPERTY_EXISTENCE" │"NODE" │["Author"] │["name"] │null │ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│12 │"author_pseudonym" │"UNIQUENESS" │"NODE" │["Author"] │["pseudonym"] │"author_pseudonym" │ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│4 │"book_isbn2" │"UNIQUENESS" │"NODE" │["Book"] │["isbn2"] │"book_isbn2" │ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│6 │"constraint_with_options" │"UNIQUENESS" │"NODE" │["Book"] │["prop1","prop2"] │"constraint_with_options" │ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│18 │"constraint_with_provider" │"NODE_KEY" │"NODE" │["Actor"] │["surname"] │"constraint_with_provider" │ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│20 │"preExisting_actor_name_age"│"UNIQUENESS" │"NODE" │["Actor"] │["firstname","age"] │"preExisting_actor_name_age"│ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│8 │"preExisting_book_published"│"UNIQUENESS" │"NODE" │["Book"] │["published"] │"preExisting_book_published"│ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│14 │"wrote_locations" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["location"] │null │ +├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ +│13 │"wrote_year" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["year"] │null │ +└────┴────────────────────────────┴─────────────────────────────────┴──────────────┴───────────────┴───────────────────────┴────────────────────────────┘ +10 rows +---- + +//// +TODO: Update the above table of constraints once the relationship key and uniqueness constraints are no longer behind a feature flag. //// //// @@ -1933,15 +1806,8 @@ An example is to only show constraints on relationships. .+SHOW CONSTRAINTS+ ====== -//// -Set-up to get expected behavior: -CREATE CONSTRAINT FOR (n:Book) REQUIRE (n.isbn) IS UNIQUE -CREATE CONSTRAINT FOR (book:Book) REQUIRE book.title IS NOT NULL -CREATE CONSTRAINT `constraint_f076a74d` FOR ()-[r:KNOWS]-() REQUIRE r.since IS NOT NULL -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- SHOW EXISTENCE CONSTRAINTS WHERE entityType = 'RELATIONSHIP' @@ -1952,12 +1818,14 @@ To get all columns, use `+SHOW INDEXES YIELD * WHERE ...+`. [queryresult] ---- -+---------------------------------------------------------------------------------------------------------------------------+ -| id | name | type | entityType | labelsOrTypes | properties | ownedIndex | -+---------------------------------------------------------------------------------------------------------------------------+ -| 7 | "constraint_f076a74d" | "RELATIONSHIP_PROPERTY_EXISTENCE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | | -+---------------------------------------------------------------------------------------------------------------------------+ -1 row +╒════╤═════════════════╤═════════════════════════════════╤══════════════╤═══════════════╤════════════╤════════════╕ +│"id"│"name" │"type" │"entityType" │"labelsOrTypes"│"properties"│"ownedIndex"│ +╞════╪═════════════════╪═════════════════════════════════╪══════════════╪═══════════════╪════════════╪════════════╡ +│14 │"wrote_locations"│"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["location"]│null │ +├────┼─────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────┼────────────┤ +│13 │"wrote_year" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["year"] │null │ +└────┴─────────────────┴─────────────────────────────────┴──────────────┴───────────────┴────────────┴────────────┘ +2 rows ---- ====== From c2d2db08f2bb809a50475605ed9c8db0f3812ebc Mon Sep 17 00:00:00 2001 From: Lidia Zuin <102308961+lidiazuin@users.noreply.github.com> Date: Fri, 3 Feb 2023 12:21:15 +0000 Subject: [PATCH 103/383] Changing the label from deprecated to removed (#387) This is what happened to this index on Neo4j 5. Instead of just removing it from the list, I believe it's better to just change the label to removed so users are informed about that change. --- modules/ROOT/pages/query-tuning/indexes.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index 0cb7f8d93..1910782a0 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -32,7 +32,7 @@ The different index types used for search performance are: * `RANGE` * `POINT` * `TEXT` -* `BTREE` label:deprecated[] +* `BTREE` label:removed[] [NOTE] ==== From b55bcbf928d9f1aeb1c876c003e1fbba9f8fbb5e Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Fri, 3 Feb 2023 15:03:27 +0100 Subject: [PATCH 104/383] update dict data in result (maps.adoc) (#385) https://github.com/neo4j/docs-testing/pull/7 With the test code updated, we will able to check dictionary data type , I update some result after I run then new test code --- modules/ROOT/pages/syntax/maps.adoc | 52 +++-------------------------- 1 file changed, 5 insertions(+), 47 deletions(-) diff --git a/modules/ROOT/pages/syntax/maps.adoc b/modules/ROOT/pages/syntax/maps.adoc index 91814da8d..79c3ac25d 100644 --- a/modules/ROOT/pages/syntax/maps.adoc +++ b/modules/ROOT/pages/syntax/maps.adoc @@ -53,7 +53,7 @@ RETURN {key: 'Value', listKey: [{inner: 'Map1'}, {inner: 'Map2'}]} [role="queryresult",options="header,footer",cols="1* [{inner -> "Map1"},{inner -> "Map2"}], key -> "Value"}+ +| +{'listKey': [{'inner': 'Map1'}, {'inner': 'Map2'}], 'key': 'Value'}+ 1+d|Rows: 1 |=== @@ -88,20 +88,6 @@ The following conditions apply: Find *'Charlie Sheen'* and return data about him and the movies he has acted in. This example shows an example of map projection with a literal entry, which in turn also uses map projection inside the aggregating `collect()`. -//// -CREATE - (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), - (martin:Person {name: 'Martin Sheen'}), - (wallstreet:Movie {title: 'Wall Street', year: 1987}), - (reddawn:Movie {title: 'Red Dawn', year: 1984}), - (apocalypsenow:Movie {title: 'Apocalypse Now', year: 1979}), - (charlie)-[:ACTED_IN]->(wallstreet), - (charlie)-[:ACTED_IN]->(reddawn), - (charlie)-[:ACTED_IN]->(apocalypsenow), - (martin)-[:ACTED_IN]->(wallstreet), - (martin)-[:ACTED_IN]->(apocalypsenow) -//// - .Query [source, cypher, indent=0] ---- @@ -114,29 +100,15 @@ RETURN actor{.name, .realName, movies: movies} [role="queryresult",options="header,footer",cols="1* [{year -> 1979, title -> "Apocalypse Now"},{year -> 1984, title -> "Red Dawn"},{year -> 1987, title -> "Wall Street"}], realName -> "Carlos Irwin Estévez", name -> "Charlie Sheen"}+ +| +{'movies': [{'year': 1979, 'title': 'Apocalypse Now'}, {'year': 1984, 'title': 'Red Dawn'}, {'year': 1987, 'title': 'Wall Street'}], 'realName': 'Carlos Irwin Estévez', 'name': 'Charlie Sheen'}+ 1+d|Rows: 1 |=== Find all persons that have acted in movies, and show number for each. This example introduces an variable with the count, and uses a variable selector to project the value. -//// -CREATE - (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), - (martin:Person {name: 'Martin Sheen'}), - (wallstreet:Movie {title: 'Wall Street', year: 1987}), - (reddawn:Movie {title: 'Red Dawn', year: 1984}), - (apocalypsenow:Movie {title: 'Apocalypse Now', year: 1979}), - (charlie)-[:ACTED_IN]->(wallstreet), - (charlie)-[:ACTED_IN]->(reddawn), - (charlie)-[:ACTED_IN]->(apocalypsenow), - (martin)-[:ACTED_IN]->(wallstreet), - (martin)-[:ACTED_IN]->(apocalypsenow) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (actor:Person)-[:ACTED_IN]->(movie:Movie) WITH actor, count(movie) AS nbrOfMovies @@ -156,22 +128,8 @@ Again, focusing on *'Charlie Sheen'*, this time returning all properties from th Here we use an all-properties selector to project all the node properties, and additionally, explicitly project the property `age`. Since this property does not exist on the node, a `null` value is projected instead. -//// -CREATE - (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), - (martin:Person {name: 'Martin Sheen'}), - (wallstreet:Movie {title: 'Wall Street', year: 1987}), - (reddawn:Movie {title: 'Red Dawn', year: 1984}), - (apocalypsenow:Movie {title: 'Apocalypse Now', year: 1979}), - (charlie)-[:ACTED_IN]->(wallstreet), - (charlie)-[:ACTED_IN]->(reddawn), - (charlie)-[:ACTED_IN]->(apocalypsenow), - (martin)-[:ACTED_IN]->(wallstreet), - (martin)-[:ACTED_IN]->(apocalypsenow) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (actor:Person {name: 'Charlie Sheen'}) RETURN actor{.*, .age} @@ -181,7 +139,7 @@ RETURN actor{.*, .age} [role="queryresult",options="header,footer",cols="1* "Carlos Irwin Estévez", name -> "Charlie Sheen", age -> }+ +| +{'realName': 'Carlos Irwin Estévez', 'name': 'Charlie Sheen', 'age': None}+ 1+d|Rows: 1 |=== From 3affb0df5e5745046c2bdb1015c053d701f6b335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:16:58 +0100 Subject: [PATCH 105/383] Fix links in elementId function (#392) --- modules/ROOT/pages/functions/scalar.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/functions/scalar.adoc b/modules/ROOT/pages/functions/scalar.adoc index 6d85cb876..a2c79f11a 100644 --- a/modules/ROOT/pages/functions/scalar.adoc +++ b/modules/ROOT/pages/functions/scalar.adoc @@ -377,8 +377,8 @@ The identifier for a relationship is guaranteed to be unique among other relatio [NOTE] ==== -On a <>, the `id()` function should be used with caution. -It is recommended to use <> instead. +On a xref:databases.adoc#administration-databases-create-composite-database[composite database], the `id()` function should be used with caution. +It is recommended to use xref:functions/scalar.adoc#functions-elementid[`elementId()`] instead. When called in database-specific subqueries, the resulting id value for a node or relationship is local to that database. The local id for nodes or relationships from different databases may be the same. From f708f64f93a45aa4f5f74c766b747f5436133f9a Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Tue, 7 Feb 2023 11:39:03 +0100 Subject: [PATCH 106/383] Put in `[role="queryplan"]` on profile query results (#390) Co-authored-by: Jane Yang --- .../ROOT/pages/execution-plans/operators.adoc | 220 +++++++++--------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 50463a7ae..aa1025b2b 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -104,7 +104,7 @@ RETURN n ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -148,7 +148,7 @@ RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -192,7 +192,7 @@ RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -237,7 +237,7 @@ RETURN candidate ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -282,7 +282,7 @@ RETURN candidate ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -326,7 +326,7 @@ RETURN r, n1 ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -371,7 +371,7 @@ RETURN r, n1 ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -416,7 +416,7 @@ RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -461,7 +461,7 @@ RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -506,7 +506,7 @@ RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -551,7 +551,7 @@ RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -596,7 +596,7 @@ RETURN candidate ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -643,7 +643,7 @@ RETURN candidate ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -690,7 +690,7 @@ RETURN countryOrLocation ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -734,7 +734,7 @@ RETURN countryAndLocation ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -772,7 +772,7 @@ MATCH ()-[r]->() RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -807,7 +807,7 @@ MATCH ()-[r]-() RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -849,7 +849,7 @@ RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -890,7 +890,7 @@ RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -933,7 +933,7 @@ RETURN friendOrFoe ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -976,7 +976,7 @@ RETURN friendOrFoe ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1020,7 +1020,7 @@ RETURN n ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1063,7 +1063,7 @@ RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1108,7 +1108,7 @@ RETURN location ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1155,7 +1155,7 @@ RETURN t ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1206,7 +1206,7 @@ RETURN location, person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1251,7 +1251,7 @@ MERGE (t:Team {name: 'Engineering', id: 42}) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1301,7 +1301,7 @@ RETURN l ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1347,7 +1347,7 @@ RETURN t ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1392,7 +1392,7 @@ RETURN l ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1437,7 +1437,7 @@ RETURN l ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1481,7 +1481,7 @@ RETURN l ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1530,7 +1530,7 @@ RETURN p, q ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1581,7 +1581,7 @@ RETURN p.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1637,7 +1637,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1696,7 +1696,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1760,7 +1760,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1824,7 +1824,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1886,7 +1886,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1944,7 +1944,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2004,7 +2004,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2066,7 +2066,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2131,7 +2131,7 @@ ON CREATE SET p.existed = false ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2179,7 +2179,7 @@ MERGE (s)-[:FRIENDS_WITH]->(s) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2234,7 +2234,7 @@ RETURN p.name, [(p)-[:WORKS_IN]->(location) | location.name] AS cities ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2285,7 +2285,7 @@ MERGE (s)-[:FRIENDS_WITH]->(s) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2341,7 +2341,7 @@ RETURN fof ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2388,7 +2388,7 @@ RETURN fof ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2440,7 +2440,7 @@ RETURN p, l ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2489,7 +2489,7 @@ RETURN p ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2536,7 +2536,7 @@ RETURN p, q ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2583,7 +2583,7 @@ RETURN p ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2637,7 +2637,7 @@ RETURN DISTINCT p, q ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2693,7 +2693,7 @@ RETURN DISTINCT p, q ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2742,7 +2742,7 @@ MERGE (t:Team {name: 'Engineering', id: 42}) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2793,7 +2793,7 @@ CREATE (:Person) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2839,7 +2839,7 @@ RETURN n ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2883,7 +2883,7 @@ RETURN line ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -2946,7 +2946,7 @@ RETURN loc.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3006,7 +3006,7 @@ RETURN p, q ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3060,7 +3060,7 @@ RETURN a.name, b.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3117,7 +3117,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3176,7 +3176,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3241,7 +3241,7 @@ RETURN other.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3304,7 +3304,7 @@ RETURN p, t ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3353,7 +3353,7 @@ FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3407,7 +3407,7 @@ MERGE () ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3476,7 +3476,7 @@ RETURN ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3529,7 +3529,7 @@ RETURN p.name, count(*) AS count ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3577,7 +3577,7 @@ RETURN count(p) AS people ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3623,7 +3623,7 @@ RETURN count(r) AS jobs ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3668,7 +3668,7 @@ RETURN DISTINCT l ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3718,7 +3718,7 @@ RETURN DISTINCT p.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3764,7 +3764,7 @@ RETURN p ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3810,7 +3810,7 @@ LIMIT 3 ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3857,7 +3857,7 @@ SKIP 1 ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3908,7 +3908,7 @@ ORDER BY p.name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -3959,7 +3959,7 @@ ORDER BY p.name, p.age ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4009,7 +4009,7 @@ LIMIT 2 ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4061,7 +4061,7 @@ LIMIT 2 ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4111,7 +4111,7 @@ MATCH (p:Country) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4168,7 +4168,7 @@ RETURN value ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4215,7 +4215,7 @@ LIMIT 3 ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4265,7 +4265,7 @@ RETURN p, q ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4316,7 +4316,7 @@ RETURN u, v ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4366,7 +4366,7 @@ RETURN 'hello' AS greeting ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4412,7 +4412,7 @@ RETURN p ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4458,7 +4458,7 @@ FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4510,7 +4510,7 @@ ORDER BY label ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4559,7 +4559,7 @@ RETURN ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4612,7 +4612,7 @@ CREATE (max)-[:FRIENDS_WITH]->(chris) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4658,7 +4658,7 @@ DELETE w ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4711,7 +4711,7 @@ DETACH DELETE p ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4763,7 +4763,7 @@ SET n:Person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4810,7 +4810,7 @@ REMOVE n:Person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4857,7 +4857,7 @@ SET n = {weekday: 'Monday', meal: 'Lunch'} ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4904,7 +4904,7 @@ SET r = {weight: 5, unit: 'kg'} ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -4951,7 +4951,7 @@ SET n.checked = true ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -5009,7 +5009,7 @@ FOR (c:Country) REQUIRE c.name is UNIQUE ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner ADMINISTRATION @@ -5051,7 +5051,7 @@ FOR (c:Country) REQUIRE c.name is UNIQUE ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner ADMINISTRATION @@ -5104,7 +5104,7 @@ DROP CONSTRAINT name ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner ADMINISTRATION @@ -5143,7 +5143,7 @@ SHOW CONSTRAINTS ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -5187,7 +5187,7 @@ FOR (c:Country) ON (c.name) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner ADMINISTRATION @@ -5229,7 +5229,7 @@ FOR ()-[k:KNOWS]-() ON (k.since) ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner ADMINISTRATION @@ -5269,7 +5269,7 @@ DROP INDEX my_index ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner ADMINISTRATION @@ -5308,7 +5308,7 @@ SHOW INDEXES ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -5351,7 +5351,7 @@ SHOW FUNCTIONS ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -5392,7 +5392,7 @@ SHOW PROCEDURES ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -5433,7 +5433,7 @@ SHOW TRANSACTIONS ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -5474,7 +5474,7 @@ TERMINATE TRANSACTIONS 'database-transaction-123' ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST From 43043806b599161cca10d1b2b1fcef9f5ba02bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 7 Feb 2023 16:47:42 +0100 Subject: [PATCH 107/383] Update elementId params and queries (#396) --- modules/ROOT/pages/syntax/parameters.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/syntax/parameters.adoc b/modules/ROOT/pages/syntax/parameters.adoc index f89ebdaa2..adce28bf9 100644 --- a/modules/ROOT/pages/syntax/parameters.adoc +++ b/modules/ROOT/pages/syntax/parameters.adoc @@ -233,7 +233,7 @@ LIMIT $l [source,javascript, indent=0] ---- { - "id" : 0 + "id" : "0" } ---- @@ -241,7 +241,7 @@ LIMIT $l [source,cypher, indent=0] ---- MATCH (n) -WHERE elementId(n) = $id +WHERE split(elementId(n), ":")[2] = $id RETURN n.name ---- @@ -255,7 +255,7 @@ RETURN n.name [source,javascript, indent=0] ---- { - "ids" : [ 0, 1, 2 ] + "ids" : [ "0", "1", "2" ] } ---- @@ -263,7 +263,7 @@ RETURN n.name [source,cypher, indent=0] ---- MATCH (n) -WHERE elementId(n) IN $ids +WHERE split(elementId(n), ":")[2] IN $ids RETURN n.name ---- From b38a48733cc066f93be89162d4f5cdb49f0f7ecd Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Wed, 8 Feb 2023 11:32:15 +0100 Subject: [PATCH 108/383] Updating index query tuning (#391) Co-authored-by: Stefano Ottolenghi --- .../pages/indexes-for-search-performance.adoc | 2 +- modules/ROOT/pages/query-tuning/indexes.adoc | 271 ++++++++++++------ 2 files changed, 190 insertions(+), 83 deletions(-) diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index 0b3fa99b8..e27a7b409 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -679,7 +679,7 @@ CREATE (n2:Label1 {prop1: 7, prop2: 'Blue'}); //// .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) ---- diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index 1910782a0..0c47653bd 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -298,18 +298,20 @@ It is defined as such: In the example below, a node `LOOKUP` index is available. //// +// Lookup indexes exist by default CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) //// .Query [source, cypher] ---- +PROFILE MATCH (person:Person) RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -338,18 +340,20 @@ Total database accesses: 43, total allocated memory: 184 In the example below, a relationship `LOOKUP` index is available. //// +// Lookup indexes exist by default CREATE LOOKUP INDEX rel_type_lookup_index FOR ()-[r]-() ON EACH type(r) //// .Query [source, cypher] ---- +PROFILE MATCH ()-[r:KNOWS]->() RETURN r ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -378,18 +382,22 @@ Total database accesses: 23, total allocated memory: 184 In the example below, a `Person(firstname)` node `RANGE` index is available. //// +[source, cypher, role=test-setup] +---- CREATE RANGE INDEX node_range_index_name FOR (n:Person) ON (n.firstname) +---- //// .Query -[source,cypher] +[source, cypher] ---- +PROFILE MATCH (person:Person {firstname: 'Andy'}) RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -417,18 +425,22 @@ Total database accesses: 2, total allocated memory: 184 In this example, a `KNOWS(since)` relationship `RANGE` index is available. //// +[source, cypher, role=test-setup] +---- CREATE RANGE INDEX rel_range_index_name FOR ()-[r:KNOWS]-() ON (r.since) +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person)-[relationship:KNOWS {since: 1992}]->(friend) RETURN person, friend ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -453,17 +465,25 @@ Total database accesses: 2, total allocated memory: 184 [[administration-indexes-node-text-index-example]] === Node TEXT index +//// +[source, cypher, role=test-setup] +---- +CREATE TEXT INDEX node_text_person_surname FOR (n:Person) ON (n.surname) +---- +//// + In the example below, a `Person(surname)` node `TEXT` index is available. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person {surname: 'Smith'}) RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -489,17 +509,25 @@ Total database accesses: 2, total allocated memory: 184 [[administration-indexes-relationship-text-index-example]] === Relationship TEXT index -In this example, a `KNOWS(lastMetLocation)` relationship `TEXT` index is available. +//// +[source, cypher, role=test-setup] +---- +CREATE TEXT INDEX rel_text_index_name FOR ()-[r:KNOWS]-() ON (r.metIn) +---- +//// + +In this example, a `KNOWS(metIn)` relationship `TEXT` index is available. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person)-[relationship:KNOWS {metIn: 'Malmo'} ]->(friend) RETURN person, friend ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -525,18 +553,27 @@ Total database accesses: 2, total allocated memory: 184 [[administration-indexes-multiple-available-index-types]] === Multiple available index types +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX node_range_person_middlename FOR (p:Person) ON (p.middlename); +CREATE TEXT INDEX node_text_person_middlename FOR (p:Person) ON (p.middlename); +---- +//// + In the example below, both a `Person(middlename)` node `TEXT` index and a `Person(middlename)` node `RANGE` index are available. The `RANGE` node index is chosen. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person {middlename: 'Ron'}) RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -568,7 +605,7 @@ For example, if indexes exist on both `:Label(p1)` and `:Label(p2)`, `MATCH (n:L .Query -[source, query plan, subs="attributes+", role="noheader"] +[source, cypher] ---- MATCH (person:Person) WHERE person.firstname = 'Andy' @@ -576,7 +613,7 @@ RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -608,13 +645,16 @@ It can have ranges and existence predicates as well. But in these cases rewrites might happen depending on which properties have which predicates, see xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. //// -CREATE RANGE INDEX node_index_name FOR (n:Person) ON (n.age, n.country) -CREATE (p0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}) +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX node_range_age_country FOR (n:Person) ON (n.age, n.country) +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (n:Person) WHERE n.age = 35 AND n.country = 'UK' RETURN n @@ -623,20 +663,27 @@ RETURN n However, the query `MATCH (n:Person) WHERE n.age = 35 RETURN n` will not be backed by the composite index, as the query does not contain a predicate on the `country` property. It will only be backed by an index on the `Person` label and `age` property defined thus: `:Person(age)`; i.e. a single-property index. -// TODO: this should output a query plan - -//// -.Result -[source, result, role="noheader"] +[role="queryresult"] ---- -+------------------------------------------------------------------------------------------------------------+ -| n | -+------------------------------------------------------------------------------------------------------------+ -| Node[0]{country:"UK",highScore:54321,firstname:"John",surname:"Smith",name:"john",middlename:"Ron",age:35} | -+------------------------------------------------------------------------------------------------------------+ -1 row +Planner COST + +Runtime PIPELINED + +Runtime version 5.5 + +Batch size 128 + ++-----------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | n | 0 | 3 | 24 | | | | | +| | +----+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | 1 | RANGE INDEX n:Person(age, country) WHERE age = $autoint_0 AND country = $autostring_1 | 0 | 3 | 4 | 120 | 3/0 | 0.476 | Fused in Pipeline 0 | ++-----------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 28, total allocated memory: 184 ---- -//// + [discrete] [[administration-indexes-range-comparisons-using-where-single-property-index]] @@ -645,15 +692,16 @@ It will only be backed by an index on the `Person` label and `age` property defi Single-property indexes are also automatically used for inequality (range) comparisons of an indexed property in the `WHERE` clause. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (friend)<-[r:KNOWS]-(person) WHERE r.since < 2011 RETURN friend, person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -683,16 +731,24 @@ Composite indexes are also automatically used for inequality (range) comparisons Equality or list membership check predicates may precede the range predicate. However, predicates after the range predicate may be rewritten as an existence check predicate and a filter as described in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX rel_range_since_lastmet FOR ()-[r:KNOWS]-() ON (r.since, r.lastMet) +---- +//// + .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH ()-[r:KNOWS]-() WHERE r.since < 2011 AND r.lastMet > 2019 RETURN r.since ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -723,18 +779,26 @@ Total database accesses: 2, total allocated memory: 184 [[administration-indexes-multiple-range-comparisons-using-where-single-property-index]] === Multiple range comparisons using `WHERE` (single-property index) +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX node_range_highscore FOR (p:Person) ON p.highScore +---- +//// + When the `WHERE` clause contains multiple inequality (range) comparisons for the same property, these can be combined in a single index range seek. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person) WHERE 10000 < person.highScore < 20000 RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -764,15 +828,16 @@ When the `WHERE` clause contains multiple inequality (range) comparisons for the That single range seek created in the following query will then use the composite index `Person(highScore, name)` if it exists. //// -CREATE INDEX -FOR (p:Person) ON (p.highScore, p.name) - -CREATE (:Person) +[source, cypher, role=test-setup] +---- +CREATE INDEX FOR (p:Person) ON (p.highScore, p.name); +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person) WHERE 10000 < person.highScore < 20000 AND person.name IS NOT NULL RETURN person @@ -780,7 +845,7 @@ RETURN person .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -809,16 +874,24 @@ Total database accesses: 2, total allocated memory: 184 The `IN` predicate on `r.since` in the following query will use the single-property index `KNOWS(lastMetIn)` if it exists. +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX rel_range_lastmetin FOR ()-[r:KNOWS]-() ON r.lastMetIn +---- +//// + .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person)-[r:KNOWS]->(friend) WHERE r.lastMetIn IN ['Malmo', 'Stockholm'] RETURN person, friend ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -847,15 +920,16 @@ Total database accesses: 3, total allocated memory: 184 The `IN` predicates on `r.since` and `r.lastMet` in the following query will use the composite index `KNOWS(since, lastMet)` if it exists. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person)-[r:KNOWS]->(friend) WHERE r.since IN [1992, 2017] AND r.lastMet IN [2002, 2021] RETURN person, friend ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -885,15 +959,16 @@ Total database accesses: 5, total allocated memory: 184 The `STARTS WITH` predicate on `person.firstname` in the following query will use the `Person(firstname)` index, if it exists. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person) WHERE person.firstname STARTS WITH 'And' RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -919,21 +994,29 @@ Total database accesses: 2, total allocated memory: 184 [[administration-indexes-prefix-search-using-starts-with-composite-index]] === Prefix search using `STARTS WITH` (composite index) +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX node_text_firstname_surname FOR (p:Person) ON (p.firstname, p.surname) +---- +//// + The `STARTS WITH` predicate on `person.firstname` in the following query will use the `Person(firstname,surname)` index, if it exists. Any (non-existence check) predicate on `person.surname` will be rewritten as existence check with a filter. However, if the predicate on `person.firstname` is a equality check then a `STARTS WITH` on `person.surname` would also use the index (without rewrites). More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person) WHERE person.firstname STARTS WITH 'And' AND person.surname IS NOT NULL RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -963,32 +1046,37 @@ Total database accesses: 2, total allocated memory: 184 The `ENDS WITH` predicate on `r.metIn` in the following query uses the `KNOWS(metIn)` index, if it exists. Text indexes are optimized for `CONTAINS` and `ENDS WITH` and they are the only indexes that can solve those predicates. -//// -CREATE TEXT INDEX FOR ()-[r:KNOWS]-() ON (r.metIn) -//// - .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person)-[r:KNOWS]->(friend) WHERE r.metIn ENDS WITH 'mo' RETURN person, friend ---- -// TODO: this should output a query plan - -//// .Query Plan -[source, query plan, role="noheader"] +[role="queryplan"] ---- -+-------------------------+ -| person | friend | -+-------------------------+ -| Node[82]{} | Node[83]{} | -+-------------------------+ -1 row +Planner COST + +Runtime PIPELINED + +Runtime version 5.5 + +Batch size 128 + ++----------------------------------------+----+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------------+----+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | person, friend | 0 | 0 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexEndsWithScan | 1 | TEXT INDEX (person)-[r:KNOWS(metIn)]->(friend) WHERE metIn ENDS WITH $autostring_0 | 0 | 0 | 1 | 120 | 0/0 | 1.135 | Fused in Pipeline 0 | ++----------------------------------------+----+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 1, total allocated memory: 184 + ---- -//// Text indexes only index String values and therefore do not find other values. @@ -1003,19 +1091,24 @@ Any (non-existence check) predicate on `KNOWS.lastMetIn` is also rewritten as ex More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. //// - +[source, cypher, role=test-setup] +---- +DROP INDEX rel_text_index_name; // TEXT indexes would take precedence over composite RANGE index +CREATE RANGE INDEX rel_text_metin_lastmetin FOR ()-[r:KNOWS]-() ON (r.metIn, r.lastMetIn) +---- //// .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person)-[r:KNOWS]->(friend) WHERE r.metIn ENDS WITH 'mo' AND r.lastMetIn IS NOT NULL RETURN person, friend ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1049,8 +1142,9 @@ Text indexes are optimized for `CONTAINS` and `ENDS WITH` and they are the only Composite indexes are currently not able to support `CONTAINS`. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person) WHERE person.firstname CONTAINS 'h' RETURN person @@ -1084,15 +1178,16 @@ Any (non-existence check) predicate on `person.age` will also be rewritten as ex More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person) WHERE person.country CONTAINS '300' AND person.age IS NOT NULL RETURN person ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1124,15 +1219,16 @@ Total database accesses: 304, total allocated memory: 184 The `r.since IS NOT NULL` predicate in the following query uses the `KNOWS(since)` index, if it exists. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person)-[r:KNOWS]->(friend) WHERE r.since IS NOT NULL RETURN person, friend ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1162,15 +1258,16 @@ The `p.firstname IS NOT NULL` and `p.surname IS NOT NULL` predicates in the foll Any (non-existence check) predicate on `person.surname` will be rewritten as existence check with a filter. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (p:Person) WHERE p.firstname IS NOT NULL AND p.surname IS NOT NULL RETURN p ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1197,22 +1294,25 @@ Total database accesses: 3, total allocated memory: 184 === Spatial distance searches (single-property index) //// -CREATE POINT INDEX -FOR ()-[r:KNOWS]-() ON (r.lastMetPoint) +[source, cypher, role=test-setup] +---- +CREATE POINT INDEX FOR ()-[r:KNOWS]-() ON (r.lastMetPoint) +---- //// If a property with point values is indexed, the index is used for spatial distance searches as well as for range queries. .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH ()-[r:KNOWS]->() WHERE point.distance(r.lastMetPoint, point({x: 1, y: 2})) < 2 RETURN r.lastMetPoint ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST @@ -1245,17 +1345,24 @@ Total database accesses: 10, total allocated memory: 184 The ability to do index seeks on bounded ranges works even with the 2D and 3D spatial `Point` types. +//// +[source, cypher, role=test-setup] +---- +CREATE POINT INDEX FOR (p:Person) ON (p.location) +---- +//// .Query -[source, cypher, indent=0] +[source, cypher] ---- +PROFILE MATCH (person:Person) WHERE point.withinBBox(person.location, point({x: 1.2, y: 5.4}), point({x: 1.3, y: 5.5})) RETURN person.firstname ---- .Query Plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST From e7455285f3970f33130b820bb49de8ffdba13ea2 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 8 Feb 2023 13:28:53 +0100 Subject: [PATCH 109/383] Fix date time example blocks --- .../ROOT/pages/functions/temporal/index.adoc | 134 +++++++++--------- modules/ROOT/pages/syntax/temporal.adoc | 58 ++++---- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/modules/ROOT/pages/functions/temporal/index.adoc b/modules/ROOT/pages/functions/temporal/index.adoc index b4d15cd58..0c29ae4a4 100644 --- a/modules/ROOT/pages/functions/temporal/index.adoc +++ b/modules/ROOT/pages/functions/temporal/index.adoc @@ -368,7 +368,7 @@ date([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN date() AS currentDate ---- @@ -392,7 +392,7 @@ The current date is returned. ====== .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- RETURN date({timezone: 'America/Los Angeles'}) AS currentDateInLA ---- @@ -450,7 +450,7 @@ date.transaction([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN date.transaction() AS currentDate ---- @@ -507,7 +507,7 @@ date.statement([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN date.statement() AS currentDate ---- @@ -563,7 +563,7 @@ date.realtime([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN date.realtime() AS currentDate ---- @@ -585,7 +585,7 @@ RETURN date.realtime() AS currentDate ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN date.realtime('America/Los Angeles') AS currentDateInLA ---- @@ -659,7 +659,7 @@ date({year [, month, day]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ date({year: 1984, month: 10, day: 11}), @@ -740,7 +740,7 @@ date({year [, week, dayOfWeek]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ date({year: 1984, week: 10, dayOfWeek: 3}), @@ -821,7 +821,7 @@ date({year [, quarter, dayOfQuarter]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ date({year: 1984, quarter: 3, dayOfQuarter: 45}), @@ -897,7 +897,7 @@ date({year [, ordinalDay]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ date({year: 1984, ordinalDay: 202}), @@ -969,7 +969,7 @@ date(temporalValue) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ date('2015-07-21'), @@ -1073,7 +1073,7 @@ date({date [, year, month, day, week, dayOfWeek, quarter, dayOfQuarter, ordinalD ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ date({year: 1984, month: 11, day: 11}), @@ -1157,7 +1157,7 @@ date.truncate(unit [, temporalInstantValue [, mapOfComponents ] ]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH datetime({ @@ -1259,7 +1259,7 @@ datetime([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime() AS currentDateTime ---- @@ -1283,7 +1283,7 @@ The current date and time using the local time zone is returned. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime({timezone: 'America/Los Angeles'}) AS currentDateTimeInLA ---- @@ -1342,7 +1342,7 @@ datetime.transaction([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime.transaction() AS currentDateTime ---- @@ -1364,7 +1364,7 @@ RETURN datetime.transaction() AS currentDateTime ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime.transaction('America/Los Angeles') AS currentDateTimeInLA ---- @@ -1420,7 +1420,7 @@ datetime.statement([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime.statement() AS currentDateTime ---- @@ -1476,7 +1476,7 @@ datetime.realtime([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime.realtime() AS currentDateTime ---- @@ -1578,7 +1578,7 @@ datetime({year [, month, day, hour, minute, second, millisecond, microsecond, na ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ datetime({year: 1984, month: 10, day: 11, hour: 12, minute: 31, second: 14, millisecond: 123, microsecond: 456, nanosecond: 789}), @@ -1697,7 +1697,7 @@ datetime({year [, week, dayOfWeek, hour, minute, second, millisecond, microsecon ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ datetime({year: 1984, week: 10, dayOfWeek: 3, hour: 12, minute: 31, second: 14, millisecond: 645}), @@ -1814,7 +1814,7 @@ datetime({year [, quarter, dayOfQuarter, hour, minute, second, millisecond, micr ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ datetime({year: 1984, quarter: 3, dayOfQuarter: 45, hour: 12, minute: 31, second: 14, microsecond: 645876}), @@ -1921,7 +1921,7 @@ datetime({year [, ordinalDay, hour, minute, second, millisecond, microsecond, na ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ datetime({year: 1984, ordinalDay: 202, hour: 12, minute: 31, second: 14, millisecond: 645}), @@ -1996,7 +1996,7 @@ datetime(temporalValue) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ datetime('2015-07-21T21:40:32.142+0100'), @@ -2137,7 +2137,7 @@ datetime({datetime [, year, ..., timezone]}) | datetime({date [, year, ..., time The following query shows the various usages of `+datetime({date [, year, ..., timezone]})+`. .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH date({year: 1984, month: 10, day: 11}) AS dd RETURN @@ -2166,7 +2166,7 @@ RETURN The following query shows the various usages of `datetime({time [, year, ..., timezone]})`. .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH time({hour: 12, minute: 31, second: 14, microsecond: 645876, timezone: '+01:00'}) AS tt RETURN @@ -2195,7 +2195,7 @@ RETURN The following query shows the various usages of `+datetime({date, time [, year, ..., timezone]})+`; i.e. combining a _Date_ and a _Time_ value to create a single _DateTime_ value. .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH date({year: 1984, month: 10, day: 11}) AS dd, @@ -2226,7 +2226,7 @@ RETURN The following query shows the various usages of `+datetime({datetime [, year, ..., timezone]})+`. .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH datetime({ @@ -2307,7 +2307,7 @@ datetime({ epochSeconds | epochMillis }) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime({epochSeconds: timestamp() / 1000, nanosecond: 23}) AS theDate ---- @@ -2329,7 +2329,7 @@ RETURN datetime({epochSeconds: timestamp() / 1000, nanosecond: 23}) AS theDate ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime({epochMillis: 424797300000}) AS theDate ---- @@ -2412,7 +2412,7 @@ During truncation, a time zone can be attached or overridden using the key `time ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH datetime({ @@ -2510,7 +2510,7 @@ localdatetime([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime() AS now ---- @@ -2534,7 +2534,7 @@ The current local date and time (i.e. in the local time zone) is returned. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime({timezone: 'America/Los Angeles'}) AS now ---- @@ -2593,7 +2593,7 @@ localdatetime.transaction([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime.transaction() AS now ---- @@ -2650,7 +2650,7 @@ localdatetime.statement([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime.statement() AS now ---- @@ -2706,7 +2706,7 @@ localdatetime.realtime([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime.realtime() AS now ---- @@ -2728,7 +2728,7 @@ RETURN localdatetime.realtime() AS now ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime.realtime('America/Los Angeles') AS nowInLA ---- @@ -2826,7 +2826,7 @@ localdatetime({year [, month, day, hour, minute, second, millisecond, microsecon ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime({ @@ -2928,7 +2928,7 @@ localdatetime({year [, week, dayOfWeek, hour, minute, second, millisecond, micro ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime({ @@ -3030,7 +3030,7 @@ localdatetime({year [, quarter, dayOfQuarter, hour, minute, second, millisecond, ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime({ @@ -3128,7 +3128,7 @@ localdatetime({year [, ordinalDay, hour, minute, second, millisecond, microsecon ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime({ @@ -3197,7 +3197,7 @@ localdatetime(temporalValue) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ localdatetime('2015-07-21T21:40:32.142'), @@ -3323,7 +3323,7 @@ localdatetime({datetime [, year, ..., nanosecond]}) | localdatetime({date [, yea The following query shows the various usages of `+localdatetime({date [, year, ..., nanosecond]})+`. .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH date({year: 1984, month: 10, day: 11}) AS dd RETURN @@ -3350,7 +3350,7 @@ RETURN The following query shows the various usages of `+localdatetime({time [, year, ..., nanosecond]})+`. .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH time({hour: 12, minute: 31, second: 14, microsecond: 645876, timezone: '+01:00'}) AS tt RETURN @@ -3377,7 +3377,7 @@ RETURN The following query shows the various usages of `+localdatetime({date, time [, year, ..., nanosecond]})+`; i.e. combining a _Date_ and a _Time_ value to create a single _LocalDateTime_ value. .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH date({year: 1984, month: 10, day: 11}) AS dd, @@ -3406,7 +3406,7 @@ RETURN The following query shows the various usages of `+localdatetime({datetime [, year, ..., nanosecond]})+`. .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH datetime({ @@ -3492,7 +3492,7 @@ localdatetime.truncate(unit [, temporalInstantValue [, mapOfComponents ] ]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH localdatetime({ @@ -3586,7 +3586,7 @@ localtime([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localtime() AS now ---- @@ -3610,7 +3610,7 @@ The current local time (i.e. in the local time zone) is returned. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localtime({timezone: 'America/Los Angeles'}) AS nowInLA ---- @@ -3669,7 +3669,7 @@ localtime.transaction([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localtime.transaction() AS now ---- @@ -3726,7 +3726,7 @@ localtime.statement([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localtime.statement() AS now ---- @@ -3748,7 +3748,7 @@ RETURN localtime.statement() AS now ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localtime.statement('America/Los Angeles') AS nowInLA ---- @@ -3804,7 +3804,7 @@ localtime.realtime([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localtime.realtime() AS now ---- @@ -3890,7 +3890,7 @@ localtime({hour [, minute, second, millisecond, microsecond, nanosecond]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ localtime({hour: 12, minute: 31, second: 14, nanosecond: 789, millisecond: 123, microsecond: 456}), @@ -3962,7 +3962,7 @@ localtime(temporalValue) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ localtime('21:40:32.142'), @@ -4057,7 +4057,7 @@ localtime({time [, hour, ..., nanosecond]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH time({hour: 12, minute: 31, second: 14, microsecond: 645876, timezone: '+01:00'}) AS tt RETURN @@ -4140,7 +4140,7 @@ However, the time zone of `temporalInstantValue` is retained. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH time({hour: 12, minute: 31, second: 14, nanosecond: 645876123, timezone: '-01:00'}) AS t RETURN @@ -4230,7 +4230,7 @@ time([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN time() AS currentTime ---- @@ -4254,7 +4254,7 @@ The current time of day using the local time zone is returned. ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN time({timezone: 'America/Los Angeles'}) AS currentTimeInLA ---- @@ -4312,7 +4312,7 @@ time.transaction([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN time.transaction() AS currentTime ---- @@ -4366,7 +4366,7 @@ time.statement([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN time.statement() AS currentTime ---- @@ -4388,7 +4388,7 @@ RETURN time.statement() AS currentTime ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN time.statement('America/Los Angeles') AS currentTimeInLA ---- @@ -4444,7 +4444,7 @@ time.realtime([{timezone}]) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN time.realtime() AS currentTime ---- @@ -4535,7 +4535,7 @@ time({hour [, minute, second, millisecond, microsecond, nanosecond, timezone]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ time({hour: 12, minute: 31, second: 14, millisecond: 123, microsecond: 456, nanosecond: 789}), @@ -4612,7 +4612,7 @@ time(temporalValue) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- UNWIND [ time('21:40:32.142+0100'), @@ -4720,7 +4720,7 @@ time({time [, hour, ..., timezone]}) ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH localtime({hour: 12, minute: 31, second: 14, microsecond: 645876}) AS tt RETURN @@ -4807,7 +4807,7 @@ During truncation, a time zone can be attached or overridden using the key `time ====== .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH time({hour: 12, minute: 31, second: 14, nanosecond: 645876123, timezone: '-01:00'}) AS t RETURN diff --git a/modules/ROOT/pages/syntax/temporal.adoc b/modules/ROOT/pages/syntax/temporal.adoc index f5034bfc1..ff14203a2 100644 --- a/modules/ROOT/pages/syntax/temporal.adoc +++ b/modules/ROOT/pages/syntax/temporal.adoc @@ -241,7 +241,7 @@ For more details, refer to xref::functions/temporal/index.adoc#functions-tempora Parsing a _DateTime_ using the _calendar date_ format: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN datetime('2015-06-24T12:50:35.556+0100') AS theDateTime ---- @@ -263,7 +263,7 @@ RETURN datetime('2015-06-24T12:50:35.556+0100') AS theDateTime Parsing a _LocalDateTime_ using the _ordinal date_ format: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localdatetime('2015185T19:32:24') AS theLocalDateTime ---- @@ -285,7 +285,7 @@ RETURN localdatetime('2015185T19:32:24') AS theLocalDateTime Parsing a _Date_ using the _week date_ format: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN date('+2015-W13-4') AS theDate ---- @@ -306,7 +306,7 @@ RETURN date('+2015-W13-4') AS theDate Parsing a _Time_: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN time('125035.556+0100') AS theTime ---- @@ -328,7 +328,7 @@ RETURN time('125035.556+0100') AS theTime Parsing a _LocalTime_: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN localtime('12:50:35.556') AS theLocalTime ---- @@ -619,7 +619,7 @@ For more information, see the xref::syntax/temporal.adoc#cypher-temporal-year[ru The following query shows how to extract the components of a _Date_ value: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH date({year: 1984, month: 10, day: 11}) AS d RETURN d.year, d.quarter, d.month, d.week, d.weekYear, d.day, d.ordinalDay, d.dayOfWeek, d.dayOfQuarter @@ -642,7 +642,7 @@ RETURN d.year, d.quarter, d.month, d.week, d.weekYear, d.day, d.ordinalDay, d.da The following query shows how to extract the date related components of a _DateTime_ value: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH datetime({ year: 1984, month: 11, day: 11, @@ -668,7 +668,7 @@ RETURN d.year, d.quarter, d.month, d.week, d.weekYear, d.day, d.ordinalDay, d.da The following query shows how to extract the time related components of a _DateTime_ value: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH datetime({ year: 1984, month: 11, day: 11, @@ -695,7 +695,7 @@ RETURN d.hour, d.minute, d.second, d.millisecond, d.microsecond, d.nanosecond The following query shows how to extract the epoch time and timezone related components of a _DateTime_ value: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH datetime({ year: 1984, month: 11, day: 11, @@ -767,7 +767,7 @@ For more details, refer to xref::functions/temporal/duration.adoc#functions-dura Return a _Duration_ of `14` _days_, `16` _hours_, and `12` _minutes_: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration('P14DT16H12M') AS theDuration ---- @@ -789,7 +789,7 @@ RETURN duration('P14DT16H12M') AS theDuration Return a _Duration_ of `5` _months_, `1` _day_, and `12` _hours_: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration('P5M1.5D') AS theDuration ---- @@ -811,7 +811,7 @@ RETURN duration('P5M1.5D') AS theDuration Return a _Duration_ of `45` seconds: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration('PT0.75M') AS theDuration ---- @@ -833,7 +833,7 @@ RETURN duration('PT0.75M') AS theDuration Return a _Duration_ of `2` _weeks_, `3` _days_, and `12` _hours_: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration('P2.5W') AS theDuration ---- @@ -997,7 +997,7 @@ It is also possible to access the smaller (less significant) components of a com The following query shows how to extract the month based components of a _Duration_ value: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH duration({years: 1, months: 5, days: 111, minutes: 42}) AS d RETURN d.years, d.quarters, d.quartersOfYear, d.months, d.monthsOfYear, d.monthsOfQuarter @@ -1020,7 +1020,7 @@ RETURN d.years, d.quarters, d.quartersOfYear, d.months, d.monthsOfYear, d.months The following query shows how to extract the day based components of a _Duration_ value: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH duration({months: 5, days: 25, hours: 1}) AS d RETURN d.weeks, d.days, d.daysOfWeek @@ -1043,7 +1043,7 @@ RETURN d.weeks, d.days, d.daysOfWeek The following query shows how to extract the most significant second based components of a _Duration_ value: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH duration({ years: 1, months:1, days:1, hours: 1, @@ -1069,7 +1069,7 @@ RETURN d.hours, d.minutes, d.seconds, d.milliseconds, d.microseconds, d.nanoseco The following query shows how to extract the less significant second based components of a _Duration_ value: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH duration({ years: 1, months:1, days:1, @@ -1103,7 +1103,7 @@ Refer to xref::functions/temporal/index.adoc[Temporal functions - instant types] Create a _Duration_ representing 1.5 _days_: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration({days: 1, hours: 12}) AS theDuration ---- @@ -1125,7 +1125,7 @@ RETURN duration({days: 1, hours: 12}) AS theDuration Compute the _Duration_ between two temporal instants: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration.between(date('1984-10-11'), date('2015-06-24')) AS theDuration ---- @@ -1147,7 +1147,7 @@ RETURN duration.between(date('1984-10-11'), date('2015-06-24')) AS theDuration Compute the number of days between two _Date_ values: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration.inDays(date('2014-10-11'), date('2015-08-06')) AS theDuration ---- @@ -1169,7 +1169,7 @@ RETURN duration.inDays(date('2014-10-11'), date('2015-08-06')) AS theDuration Get the first _Date_ of the current year: .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- RETURN date.truncate('year') AS day ---- @@ -1191,7 +1191,7 @@ RETURN date.truncate('year') AS day Get the _Date_ of the Thursday in the week of a specific date: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN date.truncate('week', date('2019-10-01'), {dayOfWeek: 4}) AS thursday ---- @@ -1213,7 +1213,7 @@ RETURN date.truncate('week', date('2019-10-01'), {dayOfWeek: 4}) AS thursday Get the _Date_ of the last day of the next month: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN date.truncate('month', date() + duration('P2M')) - duration('P1D') AS lastDay ---- @@ -1235,7 +1235,7 @@ RETURN date.truncate('month', date() + duration('P2M')) - duration('P1D') AS las Add a _Duration_ to a _Date_: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN time('13:42:19') + duration({days: 1, hours: 12}) AS theTime ---- @@ -1257,7 +1257,7 @@ RETURN time('13:42:19') + duration({days: 1, hours: 12}) AS theTime Add two _Duration_ values: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration({days: 2, hours: 7}) + duration({months: 1, hours: 18}) AS theDuration ---- @@ -1279,7 +1279,7 @@ RETURN duration({days: 2, hours: 7}) + duration({months: 1, hours: 18}) AS theDu Multiply a _Duration_ by a number: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration({hours: 5, minutes: 21}) * 14 AS theDuration ---- @@ -1301,7 +1301,7 @@ RETURN duration({hours: 5, minutes: 21}) * 14 AS theDuration Divide a _Duration_ by a number: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN duration({hours: 3, minutes: 16}) / 2 AS theDuration ---- @@ -1323,7 +1323,7 @@ RETURN duration({hours: 3, minutes: 16}) / 2 AS theDuration Examine whether two instants are less than one day apart: .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH datetime('2015-07-21T21:40:32.142+0100') AS date1, @@ -1352,7 +1352,7 @@ END AS lessThanOneDayApart Return the abbreviated name of the current month: .Query -[source, cypher, indent=0] +[source, cypher] ---- RETURN ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date().month-1] AS month ---- From bbc18c3e325d21fd4e554d03443934472720afc5 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Wed, 8 Feb 2023 16:31:09 +0100 Subject: [PATCH 110/383] Fix query-plan in using.adoc and shortestpath.adoc (#394) --- .../shortestpath-planning.adoc | 8 ++++-- modules/ROOT/pages/query-tuning/using.adoc | 26 +++++++++---------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc b/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc index 6ccee84fd..78115a4d4 100644 --- a/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc +++ b/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc @@ -33,6 +33,8 @@ Therefore, in these cases, it is recommended to set `cypher.forbid_exhaustive_sh ====== //// +[source, cypher, role=test-setup] +---- CREATE (KevinB:Person {name: 'Kevin Bacon'}), (JackN:Person {name: 'Jack Nicholson'}), @@ -54,10 +56,11 @@ CREATE (TheDevilsAdvocate:Movie {title: 'The Devil´s Advocate'}), (Keanu)-[:ACTED_IN {role: 'Kevin Lomax'}]->(TheDevilsAdvocate), - (Al)-[:ACTED_IN {role: 'John Milton'}]->(TheDevilsAdvocate) + (Al)-[:ACTED_IN {role: 'John Milton'}]->(TheDevilsAdvocate); CREATE INDEX FOR (n:Person) ON (n.name) +---- //// This query can be evaluated with the fast algorithm -- there are no predicates that need to see the whole path before being evaluated. @@ -65,6 +68,7 @@ This query can be evaluated with the fast algorithm -- there are no predicates t .Query [source, cypher, role="noplay"] ---- +PROFILE MATCH (KevinB:Person {name: 'Kevin Bacon'}), (Al:Person {name: 'Al Pacino'}), @@ -74,7 +78,7 @@ RETURN p ---- .Query plan -[source, query plan, subs="attributes+", role="noheader"] +[role="queryplan"] ---- Planner COST diff --git a/modules/ROOT/pages/query-tuning/using.adoc b/modules/ROOT/pages/query-tuning/using.adoc index 03ccfd299..8ce886ab5 100644 --- a/modules/ROOT/pages/query-tuning/using.adoc +++ b/modules/ROOT/pages/query-tuning/using.adoc @@ -70,7 +70,7 @@ The query above will be used in some of the examples on this page. Without any hints, one index and no join is used. .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -171,7 +171,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -224,7 +224,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -265,7 +265,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -316,7 +316,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -360,7 +360,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -417,7 +417,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -475,7 +475,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -532,7 +532,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -585,7 +585,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -643,7 +643,7 @@ RETURN * ---- .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -702,7 +702,7 @@ RETURN * Without any hint, the planner did not use a join to solve the `OPTIONAL MATCH`. .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST @@ -739,7 +739,7 @@ RETURN * Now the planner uses a join to solve the `OPTIONAL MATCH`. .Query plan -[role="queryresult"] +[role="queryplan"] ---- Planner COST From 6d76512f2cf17de016dd4ac64995153426cf2a4b Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Thu, 9 Feb 2023 07:01:36 +0100 Subject: [PATCH 111/383] test fixes and cleanup. Lookup indexes are present by default so cannot be recreated --- .../pages/indexes-for-search-performance.adoc | 41 ++----------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index e27a7b409..1d8e18fa5 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -440,15 +440,6 @@ Note that the index is not immediately available, but is created in the backgrou .+CREATE INDEX+ ====== -//// -[source, cypher, role=test-setup] ----- -CREATE (_0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}); -CREATE (_1:`Person` {`age`:40, `country`:"Sweden", `firstname`:"Andy", `highScore`:12345, `middlename`:"Mark", `name`:"andy", `surname`:"Jones"}); -CREATE (_0)-[:`KNOWS` {`lastMet`:2021, `lastMetIn`:"Stockholm", `metIn`:"Malmo", `since`:1992}]->(_1) ----- -//// - .Query [source, cypher] ---- @@ -669,15 +660,6 @@ The index is not immediately available, but is created in the background. .+CREATE LOOKUP INDEX+ ====== -//// -[source, cypher, role=test-setup] ----- -CREATE (n0:Label1:Label2 {prop1: 3, prop2: 'Green'}); -CREATE (n1:Label1:Label3 {prop1: 5, prop2: 'Pink'}); -CREATE (n2:Label1 {prop1: 7, prop2: 'Blue'}); ----- -//// - .Query [source, cypher, role=test-skip] ---- @@ -721,16 +703,8 @@ The index is not immediately available, but is created in the background. .+CREATE LOOKUP INDEX+ ====== -//// -[source, cypher, role=test-setup] ----- -CREATE (n0)-[:TYPE1]->(n1); -CREATE (n0)-[:TYPE2]->(n2); ----- -//// - .Query -[source, cypher] +[source, cypher, role=test-skip] ---- CREATE LOOKUP INDEX rel_type_lookup_index FOR ()-[r]-() ON EACH type(r) ---- @@ -768,21 +742,14 @@ Only one valid value exists for the index provider, `token-lookup-1.0`, which is // The name `token` is a collective word for both node label and relationship type, // hence the `node label lookup index` and `relationship type lookup index` variations above. -//// -[source, cypher, role=test-setup] ----- -drop index node_label_lookup_index ----- -//// - .+CREATE LOOKUP INDEX+ ====== .Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- -CREATE LOOKUP INDEX node_label_lookup_index_2 FOR (n) ON EACH labels(n) +CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) OPTIONS {indexProvider: 'token-lookup-1.0'} ---- @@ -984,7 +951,7 @@ OPTIONS { .Result [queryresult] ---- -Added 1 index. +Added 1 index. ---- ====== From b08d2d40f7c3007942c659421911fab6675b4c0e Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Thu, 9 Feb 2023 13:05:42 +0100 Subject: [PATCH 112/383] update point data for spatial (#400) --- modules/ROOT/pages/functions/spatial.adoc | 4 ++-- modules/ROOT/pages/syntax/spatial.adoc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ROOT/pages/functions/spatial.adoc b/modules/ROOT/pages/functions/spatial.adoc index 015b52e4b..1049bd81d 100644 --- a/modules/ROOT/pages/functions/spatial.adoc +++ b/modules/ROOT/pages/functions/spatial.adoc @@ -576,7 +576,7 @@ A 3D point with a `longitude` of `56.7`, a `latitude` of `12.78` and a height of |=== | +point+ -| +point({srid:4979, x:56.7, y:12.78, z:8})+ +| +point({srid:4979, x:56.7, y:12.78, z:8.0})+ 1+d|Rows: 1 |=== @@ -730,7 +730,7 @@ A 3D point with an `x` coordinate of `2.3`, a `y` coordinate of `4.5` and a `z` |=== | +point+ -| +point({srid:9157, x:2.3, y:4.5, z:2})+ +| +point({srid:9157, x:2.3, y:4.5, z:2.0})+ 1+d|Rows: 1 |=== diff --git a/modules/ROOT/pages/syntax/spatial.adoc b/modules/ROOT/pages/syntax/spatial.adoc index ae09a35c2..56d58452b 100644 --- a/modules/ROOT/pages/syntax/spatial.adoc +++ b/modules/ROOT/pages/syntax/spatial.adoc @@ -167,7 +167,7 @@ RETURN [role="queryresult",options="header,footer",cols="4* Date: Thu, 9 Feb 2023 14:20:58 +0100 Subject: [PATCH 113/383] Deprecate different orders inside a UNION clause (#314) Will be targeting the next release, 5.4. Adds a deprecation message to enforce Union branch ordering. --- ...ions-additions-removals-compatibility.adoc | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index fa19084b5..b38744c38 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -10,6 +10,52 @@ New features are added to the language continuously, and occasionally, some feat This section lists all of the features that have been removed, deprecated, added, or extended in different Cypher versions. Replacement syntax for deprecated and removed features are also indicated. +[[cypher-deprecations-additions-removals-5.5]] +== Version 5.5 + +=== Deprecated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:syntax[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +RETURN 'val' as one, 'val' as two +UNION +RETURN 'val' as two, 'val' as one +---- + +[source, cypher, role="noheader"] +---- +RETURN 'val' as one, 'val' as two +UNION ALL +RETURN 'val' as two, 'val' as one +---- +a| + +Using differently ordered return items in a `UNION [ALL]` clause is deprecated. Replaced by: + +[source, cypher, role="noheader"] +---- +RETURN 'val' as one, 'val' as two +UNION +RETURN 'val' as one, 'val' as two +---- + +[source, cypher, role="noheader"] +---- +RETURN 'val' as one, 'val' as two +UNION ALL +RETURN 'val' as one, 'val' as two +---- + +|=== + [[cypher-deprecations-additions-removals-5.3]] == Version 5.3 From 924dca40f2444546099ebd08345b5e57745c5cc3 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Thu, 9 Feb 2023 14:50:02 +0100 Subject: [PATCH 114/383] Fix some date time and paths in docs (#401) There is still some failure in the temporal/index.adoc page --- modules/ROOT/pages/clauses/limit.adoc | 8 +++---- modules/ROOT/pages/clauses/return.adoc | 2 +- modules/ROOT/pages/functions/predicate.adoc | 8 +++---- .../ROOT/pages/functions/temporal/index.adoc | 24 +++++++++---------- modules/ROOT/pages/syntax/temporal.adoc | 6 ++--- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modules/ROOT/pages/clauses/limit.adoc b/modules/ROOT/pages/clauses/limit.adoc index 6ad743a03..079f3241b 100644 --- a/modules/ROOT/pages/clauses/limit.adoc +++ b/modules/ROOT/pages/clauses/limit.adoc @@ -35,7 +35,7 @@ CREATE To return a limited subset of the rows, use this syntax: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) RETURN n.name @@ -62,7 +62,7 @@ Limit to 3 rows by the example query. Limit accepts any expression that evaluates to a positive integer as long as it is not referring to any external variables: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) RETURN n.name @@ -91,7 +91,7 @@ The use of `LIMIT` in a query will not stop side effects, like `CREATE`, `DELETE This behaviour was undefined in Neo4j versions before `4.3`. .Query -[source, cypher, indent=0] +[source, cypher] ---- CREATE (n) RETURN n @@ -109,7 +109,7 @@ Nodes created: 1 |=== .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n {name: 'A'}) SET n.age = 60 diff --git a/modules/ROOT/pages/clauses/return.adoc b/modules/ROOT/pages/clauses/return.adoc index ada80b933..f295bc96d 100644 --- a/modules/ROOT/pages/clauses/return.adoc +++ b/modules/ROOT/pages/clauses/return.adoc @@ -214,7 +214,7 @@ Returns a predicate, a literal and function call with a pattern expression param [role="queryresult",options="header,footer",cols="3* 30+ | +"I'm a literal"+ | +(a)-->()+ -| +true+ | +"I'm a literal"+ | +[(0)-[BLOCKS,1]->(1),(0)-[KNOWS,0]->(1)]+ +| +true+ | +"I'm a literal"+ | +[[{"happy":"Yes!","name":"A","age":55},{},{"name":"B"}],[{"happy":"Yes+!","name":"A","age":55},{},{"name":"B"}]]+ 3+d|Rows: 1 |=== diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 7d2fdc062..310e7e56a 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -105,7 +105,7 @@ All nodes in the returned paths will have a property `age` with a value larger t |=== | +p+ -| +[{"name":"Alice","eyes":"brown","age":38},{},{"name":"Charlie","eyes":"green","age":53},{"name":"Charlie","eyes":"green","age":53},{},{"name":"Daniel","eyes":"brown","age":54}]+ +| +[{"name":"Alice","eyes":"brown","age":38},{},{"name":"Charlie","eyes":"green","age":53},{"name":"Charlie","eyes":"green","age":53},{},{"liked_colors":[],"name":"Daniel","eyes":"brown","age":54}]+ 1+d|Rows: 1 |=== @@ -492,8 +492,8 @@ No node in the returned paths has a property `age` with the value `25`. |=== | +p+ -| +(0)-[KNOWS,1]->(2)+ -| +(0)-[KNOWS,1]->(2)-[KNOWS,3]->(3)+ +| +[{"name":"Alice","eyes":"brown","age":38},{},{"name":"Charlie","eyes":"green","age":53}]+ +| +[{"name":"Alice","eyes":"brown","age":38},{},{"name":"Charlie","eyes":"green","age":53},{"name":"Charlie","eyes":"green","age":53},{},{"liked_colors":[],"name":"Daniel","eyes":"brown","age":54}]+ 1+d|Rows: 2 |=== @@ -559,7 +559,7 @@ In every returned path there is exactly one node that has a property `eyes` with |=== | +p+ -| +(0)-[KNOWS,0]->(1)+ +| +[{"name":"Alice","eyes":"brown","age":38},{},{"name":"Bob","eyes":"blue","age":25}] + 1+d|Rows: 1 |=== diff --git a/modules/ROOT/pages/functions/temporal/index.adoc b/modules/ROOT/pages/functions/temporal/index.adoc index 0c29ae4a4..0b643005b 100644 --- a/modules/ROOT/pages/functions/temporal/index.adoc +++ b/modules/ROOT/pages/functions/temporal/index.adoc @@ -4157,7 +4157,7 @@ RETURN |=== | +truncDay+ | +truncHour+ | +truncMinute+ | +truncSecond+ | +truncMillisecond+ | +truncMicrosecond+ -| +00:00+ | +12:00+ | +12:31:00.002+ | +12:31:14+ | +12:31:14.645+ | +12:31:14.645876+ +| +00:00:00+ | +12:00:00+ | +12:31:00.002000000+ | +12:31:14"+ | +12:31:14.645000000+ | +12:31:14.645876000+ 6+d|Rows: 1 |=== @@ -4254,7 +4254,7 @@ The current time of day using the local time zone is returned. ====== .Query -[source, cypher] +[source, cypher, role="test-result-skip"] ---- RETURN time({timezone: 'America/Los Angeles'}) AS currentTimeInLA ---- @@ -4554,9 +4554,9 @@ RETURN theTime | +theTime+ | +12:31:14.123456789Z+ | +12:31:14.645876123Z+ -| +12:31:14.645876+01:00+ -| +12:31+01:00+ -| +12:00+01:00+ +| +12:31:14.645876000+01:00+ +| +12:31:00+01:00+ +| +12:00:00+01:00+ 1+d|Rows: 5 |=== @@ -4632,14 +4632,14 @@ RETURN theTime |=== | +theTime+ -| +21:40:32.142+01:00+ -| +21:40:32.142Z+ +| +21:40:32.142000000+01:00+ +| +21:40:32.142000000Z+ | +21:40:32+01:00+ | +21:40:32-01:00+ -| +21:40-01:30+ -| +21:40Z+ -| +21:40-02:00+ -| +22:00+18:00+ +| +21:40:00-01:30+ +| +21:40:00Z+ +| +21:40:00-02:00+ +| +22:00:00+18:00+ 1+d|Rows: 8 |=== @@ -4824,7 +4824,7 @@ RETURN |=== | +truncDay+ | +truncHour+ | +truncMinute+ | +truncSecond+ | +truncMillisecond+ | +truncMicrosecond+ -| +00:00-01:00+ | +12:00-01:00+ | +12:31-01:00+ | +12:31:14-01:00+ | +12:31:14.645000002-01:00+ | +12:31:14.645876-01:00+ +| +00:00:00-01:00+ | +12:00:00-01:00+ | +12:31:00-01:00+ | +12:31:14-01:00+ | +12:31:14.645000002-01:00+ | +12:31:14.645876000-01:00+ 6+d|Rows: 1 |=== diff --git a/modules/ROOT/pages/syntax/temporal.adoc b/modules/ROOT/pages/syntax/temporal.adoc index ff14203a2..a0a45f74b 100644 --- a/modules/ROOT/pages/syntax/temporal.adoc +++ b/modules/ROOT/pages/syntax/temporal.adoc @@ -315,7 +315,7 @@ RETURN time('125035.556+0100') AS theTime [role="queryresult",options="header,footer",cols="1* Date: Thu, 9 Feb 2023 17:00:19 +0100 Subject: [PATCH 115/383] Update query plans for operator.adoc (testing) (#395) Co-authored-by: Stefano Ottolenghi --- .../ROOT/pages/execution-plans/operators.adoc | 486 ++++++++++-------- 1 file changed, 264 insertions(+), 222 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index aa1025b2b..30c731c80 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -96,7 +96,7 @@ Any query using this operator is likely to encounter performance problems on a n ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n) @@ -126,7 +126,12 @@ Total database accesses: 36, total allocated memory: 184 ---- ====== - +//// +[source, cypher, role=test-setup] +---- +CREATE range INDEX relprop_index_range FOR ()-[r:WORKS_IN]->() ON (r.title) +---- +//// [[query-plan-directed-relationship-index-scan]] == Directed Relationship Index Scan @@ -139,7 +144,7 @@ The `DirectedRelationshipIndexScan` operator examines all values stored in an in ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH ()-[r: WORKS_IN]->() @@ -183,7 +188,7 @@ The `UndirectedRelationshipIndexScan` operator examines all values stored in an ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH ()-[r: WORKS_IN]-() @@ -228,7 +233,7 @@ The relationship variable and the index used are shown in the arguments of the o ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (candidate)-[r:WORKS_IN]->() @@ -273,7 +278,7 @@ The relationship variable and the index used are shown in the arguments of the o ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (candidate)-[r:WORKS_IN]-() @@ -317,7 +322,7 @@ The `DirectedRelationshipByIdSeek` operator reads one or more relationships by i ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n1)-[r]->() @@ -362,7 +367,7 @@ As the direction is unspecified, two rows are produced for each relationship as ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n1)-[r]-() @@ -394,6 +399,12 @@ Total database accesses: 1, total allocated memory: 184 ====== +//// +[source, cypher, role=test-setup] +---- +CREATE TEXT INDEX relprop_index_contains FOR ()-[r:WORKS_IN]->() ON (r.title) +---- +//// [[query-plan-directed-relationship-index-contains-scan]] == Directed Relationship Index Contains Scan @@ -407,7 +418,7 @@ Although this is slower than an index seek (since all entries need to be examine ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH ()-[r: WORKS_IN]->() @@ -452,7 +463,7 @@ Although this is slower than an index seek (since all entries need to be examine ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH ()-[r: WORKS_IN]-() @@ -497,7 +508,7 @@ Although this is slower than an index seek (since all entries need to be examine ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH ()-[r: WORKS_IN]->() @@ -542,7 +553,7 @@ Although this is slower than an index seek (since all entries need to be examine ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH ()-[r: WORKS_IN]-() @@ -574,6 +585,13 @@ Total database accesses: 9, total allocated memory: 184 ====== +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX relprop_index_100 FOR ()-[r:WORKS_IN]->() ON (r.duration) +---- +//// + [[query-plan-directed-relationship-index-seek-by-range]] == Directed Relationship Index Seek By Range @@ -587,7 +605,7 @@ The `DirectedRelationshipIndexSeekByRange` operator finds relationships and thei ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (candidate: Person)-[r:WORKS_IN]->(location) @@ -634,7 +652,7 @@ The `UndirectedRelationshipIndexSeekByRange` operator finds relationships and th ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (candidate: Person)-[r:WORKS_IN]-(location) @@ -744,6 +762,7 @@ Runtime version {neo4j-version-minor} Batch size 128 + +-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | +-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ @@ -752,7 +771,6 @@ Batch size 128 | +IntersectionNodeByLabelsScan | 1 | countryAndLocation:Country&Location | 10 | 0 | 0 | 120 | 0/0 | 1.011 | Fused in Pipeline 0 | +-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - Total database accesses: 13, total allocated memory: 184 ---- @@ -841,7 +859,7 @@ The `DirectedRelationshipTypeScan` operator fetches all relationships and their ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH ()-[r: FRIENDS_WITH]->() @@ -882,7 +900,7 @@ The `UndirectedRelationshipTypeScan` operator fetches all relationships and thei ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH ()-[r: FRIENDS_WITH]-() @@ -1011,7 +1029,7 @@ The `NodeByIdSeek` operator reads one or more nodes by id from the node store. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n) @@ -1055,7 +1073,7 @@ The `NodeByLabelScan` operator fetches all nodes with a specific label from the ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (person:Person) @@ -1086,6 +1104,13 @@ Total database accesses: 15, total allocated memory: 184 ====== +//// +[source, cypher, role=test-setup] +---- +CREATE INDEX index_node_seek +FOR (l:Location) ON (l.name) +---- +//// [[query-plan-node-index-seek]] == Node Index Seek @@ -1100,7 +1125,7 @@ If the index is a unique index, the operator is instead called xref::execution-p ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (location:Location {name: 'Malmo'}) @@ -1131,6 +1156,14 @@ Total database accesses: 2, total allocated memory: 184 ====== +//// +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT constraint_Team_name IF NOT EXISTS +FOR (t:Team) +REQUIRE (t.name) IS UNIQUE +---- +//// [[query-plan-node-unique-index-seek]] == Node Unique Index Seek @@ -1147,7 +1180,7 @@ This makes it clear that any nodes returned from the index will be locked in ord ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (t:Team {name: 'Malmo'}) @@ -1177,7 +1210,12 @@ Total database accesses: 1, total allocated memory: 184 ---- ====== - +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX FOR (p:Person) ON (p.name) +---- +//// [[query-plan-multi-node-index-seek]] == Multi Node Index Seek @@ -1195,7 +1233,7 @@ For example, if the operator does two seeks and the first seek finds the nodes ` ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=pipelined @@ -1230,6 +1268,12 @@ Total database accesses: 0, total allocated memory: 184 ====== +//// +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT constraint_Team_id IF NOT EXISTS FOR (t:Team) REQUIRE (t.id) IS UNIQUE +---- +//// [[query-plan-asserting-multi-node-index-seek]] == Asserting Multi Node Index Seek @@ -1244,7 +1288,7 @@ Owing to the existence of two property uniqueness constraints on `:Team(name)` a ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MERGE (t:Team {name: 'Engineering', id: 42}) @@ -1278,6 +1322,14 @@ Total database accesses: 4, total allocated memory: 184 ====== +//// +[source, cypher, role=test-setup] +---- +DROP INDEX index_node_seek; +CREATE RANGE INDEX index_range_node_location +FOR (l:Location) ON (l.name) +---- +//// [[query-plan-node-index-seek-by-range]] == Node Index Seek By Range @@ -1292,7 +1344,7 @@ If the index is a unique index, the operator is instead called `NodeUniqueIndexS ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (l:Location) @@ -1338,7 +1390,7 @@ If the index is not unique, the operator is instead called `NodeIndexSeekByRange ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (t:Team) @@ -1369,7 +1421,12 @@ Total database accesses: 1, total allocated memory: 184 ---- ====== - +//// +[source, cypher, role=test-setup] +---- +CREATE TEXT INDEX index_node_contains FOR (l:Location) ON (l.name) +---- +//// [[query-plan-node-index-contains-scan]] == Node Index Contains Scan == @@ -1383,7 +1440,7 @@ Although this is slower than an index seek (since all entries need to be examine ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (l:Location) @@ -1428,7 +1485,7 @@ Although this is slower than an index seek (since all entries need to be examine ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (l:Location) @@ -1472,7 +1529,7 @@ The `NodeIndexScan` operator examines all values stored in an index, returning a ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (l:Location) @@ -1521,7 +1578,7 @@ The `Apply` operator (i.e. the standard version) takes the row produced by the r ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person {name: 'me'}) @@ -1571,7 +1628,7 @@ This makes `SemiApply` a filtering operator, used mostly for pattern predicates ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -1625,7 +1682,7 @@ This makes `AntiSemiApply` a filtering operator, used for pattern predicates in ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -1684,7 +1741,7 @@ If there are no incoming rows, the `Anti` operator will yield a single row. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=pipelined @@ -1750,7 +1807,7 @@ In the example, `LetSemiApply` will be used to check for the presence of the `FR ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -1814,7 +1871,7 @@ In the example, `LetAntiSemiApply` will be used to check for the absence of the ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -1877,7 +1934,7 @@ First, the normal expression predicate is evaluated, and, only if it returns `fa ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (other:Person) @@ -1935,7 +1992,7 @@ If the predicate returns `false` or `null`, `SelectOrAntiSemiApply` will instead ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (other:Person) @@ -1994,7 +2051,7 @@ This is a variation of the xref::execution-plans/operators.adoc#query-plan-apply ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -2056,7 +2113,7 @@ This operator is a variation of the xref::execution-plans/operators.adoc#query-p ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -2122,7 +2179,7 @@ If no matches are found instead nodes and relationships are created and all `ON ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MERGE (p:Person {name: 'Andy'}) @@ -2171,7 +2228,7 @@ The `LockingMerge` operator is just like a normal `Merge` but will lock the star ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (s:Person {name: 'me'}) @@ -2225,7 +2282,7 @@ This operator is a variation of the xref::execution-plans/operators.adoc#query-p ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -2277,7 +2334,7 @@ The `Argument` operator indicates the variable to be used as an argument to the ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (s:Person {name: 'me'}) @@ -2333,7 +2390,7 @@ Given a start node, and depending on the pattern relationship, the `Expand(All)` ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof) @@ -2380,7 +2437,7 @@ This can make a noticeable difference when dense nodes appear as end points. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof)-->(p) @@ -2430,7 +2487,7 @@ In this situation, `OptionalExpand(all)` will return a single row with the relat ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -2480,7 +2537,7 @@ This can make a noticeable difference when dense nodes appear as end points. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person)-[works_in:WORKS_IN]->(l) @@ -2528,7 +2585,7 @@ Given a start node, the `VarLengthExpand(All)` operator will traverse variable-l ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(q:Person) @@ -2575,7 +2632,7 @@ When both the start and end node have already been found, the `VarLengthExpand(I ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(p:Person) @@ -2627,9 +2684,8 @@ The `VarLengthExpand(Pruning)` operator guarantees that all the end nodes produc .VarLengthExpand(Pruning) ====== - .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person)-[:FRIENDS_WITH *3..4]-(q:Person) @@ -2647,21 +2703,21 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, q | 17 | 32 | 0 | | 2/0 | 0.242 | | -| | +------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Distinct | p, q | 17 | 32 | 0 | 2208 | 0/0 | 13.409 | | -| | +------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Filter | q:Person | 18 | 32 | 64 | | 1/0 | 0.358 | | -| | +------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +VarLengthExpand(Pruning) | (p)-[:FRIENDS_WITH*3..4]-(q) | 18 | 32 | 204 | 1696 | | | In Pipeline 1 | -| | +------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 1/0 | 0,100 | In Pipeline 0 | -+---------------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ +| +ProduceResults | 0 | p, q | 0 | 0 | 0 | | 0/0 | 0.005 | | | +| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +OrderedDistinct | 1 | p, q | 0 | 0 | 0 | 40 | 0/0 | 0.014 | | | +| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Filter | 2 | q:Person | 0 | 0 | 0 | | 0/0 | 0.014 | | | +| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +VarLengthExpand(Pruning) | 3 | (p)-[:FRIENDS_WITH*3..4]-(q) | 1 | 0 | 15 | 400 | | | | In Pipeline 1 | +| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ +---------------+ +| +NodeByLabelScan | 4 | p:Person | 14 | 14 | 15 | 120 | 1/0 | 0.020 | p ASC | In Pipeline 0 | ++---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ -Total database accesses: 339, total allocated memory: 2288 +Total database accesses: 30, total allocated memory: 480 ---- ====== @@ -2703,21 +2759,21 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, q | 56 | 68 | 0 | | | | | -| | +-----------------------------+----------------+------+---------+----------------+ | | | -| +Distinct | p, q | 56 | 68 | 0 | 4480 | | | | -| | +-----------------------------+----------------+------+---------+----------------+ | | | -| +Filter | q:Person | 59 | 68 | 136 | | | | | -| | +-----------------------------+----------------+------+---------+----------------+ | | | -| +VarLengthExpand(Pruning,BFS) | (p)-[:FRIENDS_WITH*..4]-(q) | 59 | 68 | 280 | 696 | | | | -| | +-----------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 4/0 | 3,202 | Fused in Pipeline 0 | -+-------------------------------+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------------+----+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-------------------------------+----+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | 0 | p, q | 12 | 0 | 0 | | 0/0 | 0.019 | | | +| | +----+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +OrderedDistinct | 1 | p, q | 12 | 0 | 0 | 40 | 0/0 | 0.558 | | In Pipeline 0 | +| | +----+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+ +---------------------+ +| +Filter | 2 | q:Person | 13 | 0 | 0 | | | | | | +| | +----+-----------------------------+----------------+------+---------+----------------+ | | | | +| +VarLengthExpand(Pruning,BFS) | 3 | (p)-[:FRIENDS_WITH*..4]-(q) | 13 | 0 | 0 | 0 | | | | | +| | +----+-----------------------------+----------------+------+---------+----------------+ | | | | +| +NodeByLabelScan | 4 | p:Person | 10 | 0 | 1 | 120 | 1/0 | 0.155 | p ASC | Fused in Pipeline 0 | ++-------------------------------+----+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -Total database accesses: 487, total allocated memory: 5256 +Total database accesses: 1, total allocated memory: 200 ---- @@ -2734,7 +2790,7 @@ Owing to the existence of two property uniqueness constraints on `:Team(name)` a ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -2786,7 +2842,7 @@ The `EmptyResult` operator eagerly loads all incoming data and discards it. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CREATE (:Person) @@ -2831,7 +2887,7 @@ It is present in every single query that returns data to the user, and has littl ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n) @@ -2875,7 +2931,7 @@ It is used whenever the xref::clauses/load-csv.adoc[LOAD CSV] clause is used in ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line @@ -2936,12 +2992,12 @@ As primitive types and arrays can be used, it can be done very efficiently. .NodeHashJoin ====== - .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (bob:Person {name: 'Bob'})-[:WORKS_IN]->(loc)<-[:WORKS_IN]-(matt:Person {name: 'Mattias'}) +USING JOIN ON loc RETURN loc.name ---- @@ -2995,7 +3051,7 @@ It is most frequently used to solve predicates of the form: `n.prop1 = m.prop2` ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH @@ -3050,7 +3106,7 @@ On the other hand, if `(a:Person)` returns more results than `(a)-->(b:Person)`, ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (a:Person) @@ -3107,7 +3163,7 @@ The example finds the names of all friends of my friends that are not already my ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -3166,7 +3222,7 @@ The example finds the names of all friends of my friends that are not already my ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=pipelined @@ -3231,7 +3287,7 @@ The example finds the names of all friends of my friends that are not already my ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=pipelined @@ -3294,7 +3350,7 @@ The `CartesianProduct` operator produces a cartesian product of the two inputs - ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH @@ -3345,7 +3401,7 @@ In an analogous manner to the xref::execution-plans/operators.adoc#query-plan-ap ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -3398,7 +3454,7 @@ Alternatively, the records to be updated can be returned, followed by an update ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (a), (b) @@ -3464,9 +3520,8 @@ To do this, `EagerAggregation`, as the name implies, needs to pull in all data e .EagerAggregation ====== - .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (l:Location)<-[:WORKS_IN]-(p:Person) @@ -3486,23 +3541,19 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | location, people | 4 | 6 | 0 | | 0/0 | 0.113 | In Pipeline 1 | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +EagerAggregation | cache[l.name] AS location, collect(p.name) AS people | 4 | 6 | 30 | 2584 | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 15 | 15 | 30 | | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (l)<-[anon_0:WORKS_IN]-(p) | 15 | 15 | 16 | | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +CacheProperties | cache[l.name] | 10 | 10 | 10 | | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | l:Location | 10 | 10 | 11 | 120 | 5/0 | 3,077 | Fused in Pipeline 0 | -+-------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------------+----+-----------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------+----+-----------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | location, people | 0 | 0 | 0 | | 0/0 | 0.013 | In Pipeline 1 | +| | +----+-----------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | l.name AS location, collect(p.name) AS people | 0 | 0 | 0 | 488 | | | | +| | +----+-----------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | l:Location AND p:Person | 0 | 0 | 0 | | | | | +| | +----+-----------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipTypeScan | 3 | (p)-[anon_0:WORKS_IN]->(l) | 3 | 0 | 0 | 120 | 0/0 | 0.121 | Fused in Pipeline 0 | ++-------------------------------+----+-----------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 157, total allocated memory: 2664 +Total database accesses: 0, total allocated memory: 672 ---- ====== @@ -3520,7 +3571,7 @@ This operator uses lazy evaluation and has a lower memory pressure in the system ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -3569,7 +3620,7 @@ For example, we can get counts for all nodes, and nodes with a label, but not no ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -3615,7 +3666,7 @@ For example, we can get counts for all relationships, relationships with a type, ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person)-[r:WORKS_IN]->() @@ -3658,9 +3709,8 @@ This may lead to increased memory pressure in the system. .Distinct ====== - .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (l:Location)<-[:WORKS_IN]-(p:Person) @@ -3678,21 +3728,19 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 14 | 6 | 0 | | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +Distinct | l | 14 | 6 | 0 | 224 | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 15 | 15 | 30 | | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (l)<-[anon_0:WORKS_IN]-(p) | 15 | 15 | 16 | | | | | -| | +----------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | l:Location | 10 | 10 | 11 | 120 | 4/0 | 0,744 | Fused in Pipeline 0 | -+------------------+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | l | 0 | 0 | 0 | | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Distinct | 1 | l | 0 | 0 | 0 | 224 | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | l:Location AND p:Person | 0 | 0 | 0 | | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipTypeScan | 3 | (p)-[anon_0:WORKS_IN]->(l) | 3 | 0 | 0 | 120 | 0/0 | 0.112 | Fused in Pipeline 0 | ++-------------------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 117, total allocated memory: 304 +Total database accesses: 0, total allocated memory: 288 ---- ====== @@ -3709,7 +3757,7 @@ This operator has a lower memory pressure in the system than the `Distinct` oper ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -3755,7 +3803,7 @@ The `Filter` operator filters each row coming from the child operator, only pass ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -3801,7 +3849,7 @@ The `Limit` operator returns the first `+n+` rows from the incoming input. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -3847,7 +3895,7 @@ The `Skip` operator skips `+n+` rows from the incoming rows. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -3899,7 +3947,7 @@ In order to sort the data, all data from the source operator needs to be pulled ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -3921,7 +3969,7 @@ Batch size 128 +------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ | Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | +------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResult s | p | 14 | 14 | 0 | | 2/0 | 0.178 | | | +| +ProduceResults | p | 14 | 14 | 0 | | 2/0 | 0.178 | | | | | +--------------------+----------------+------+---------+----------------+------------------------+-----------+ | | | +Sort | `p.name` ASC | 14 | 14 | 0 | 1192 | 0/0 | 0.107 | p.name ASC | In Pipeline 1 | | | +--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ @@ -3949,7 +3997,7 @@ Partial sort is only applicable when sorting on multiple columns. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -3999,7 +4047,7 @@ Instead of sorting the entire input, only the top `+n+` rows are retained. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -4050,7 +4098,7 @@ Partial top is only applicable when sorting on multiple columns. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -4098,9 +4146,8 @@ The `Union` operator concatenates the results from the right child operator with .Union ====== - .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Location) @@ -4121,29 +4168,27 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `p.name` | 20 | 11 | 0 | | | | | -| | +--------------------+----------------+------+---------+----------------+ | | | -| +Union | | 20 | 11 | 0 | 776 | 0/0 | 0.171 | Fused in Pipeline 2 | -| |\ +--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Projection | `p.name` | 10 | 1 | 0 | | | | | -| | | +--------------------+----------------+------+---------+----------------+ | | | -| | +Projection | p.name AS `p.name` | 10 | 1 | 1 | | | | | -| | | +--------------------+----------------+------+---------+----------------+ | | | -| | +Filter | p:Country | 10 | 1 | 35 | | | | | -| | | +--------------------+----------------+------+---------+----------------+ | | | -| | +AllNodesScan | p | 35 | 35 | 36 | 120 | 0/0 | 0.127 | Fused in Pipeline 1 | -| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Projection | `p.name` | 10 | 10 | 0 | | | | | -| | +--------------------+----------------+------+---------+----------------+ | | | -| +Projection | p.name AS `p.name` | 10 | 10 | 10 | | | | | -| | +--------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Location | 10 | 10 | 11 | 120 | 3/0 | 0,171 | Fused in Pipeline 0 | -+------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | `p.name` | 20 | 0 | 0 | | | | | +| | +----+--------------------+----------------+------+---------+----------------+ | | | +| +Union | 1 | | 20 | 0 | 0 | 0 | 0/0 | 0.000 | Fused in Pipeline 2 | +| |\ +----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Projection | 2 | `p.name` | 10 | 0 | 0 | | | | | +| | | +----+--------------------+----------------+------+---------+----------------+ | | | +| | +Projection | 3 | p.name AS `p.name` | 10 | 0 | 0 | | | | | +| | | +----+--------------------+----------------+------+---------+----------------+ | | | +| | +NodeByLabelScan | 4 | p:Country | 10 | 0 | 0 | 120 | 0/0 | 0.049 | Fused in Pipeline 1 | +| | +----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Projection | 5 | `p.name` | 10 | 0 | 0 | | | | | +| | +----+--------------------+----------------+------+---------+----------------+ | | | +| +Projection | 6 | p.name AS `p.name` | 10 | 0 | 0 | | | | | +| | +----+--------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 7 | p:Location | 10 | 0 | 0 | 120 | 0/0 | 0.077 | Fused in Pipeline 0 | ++--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 153, total allocated memory: 984 +Total database accesses: 0, total allocated memory: 320 ---- ====== @@ -4160,7 +4205,7 @@ The `Unwind` operator returns one row per item in a list. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE UNWIND range(1, 5) AS value @@ -4205,7 +4250,7 @@ Used when combining `LIMIT` and updates ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -4225,19 +4270,21 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 3 | 3 | 0 | | | | | -| | +---------------+----------------+------+---------+----------------+ | | | -| +ExhaustiveLimit | 3 | 3 | 3 | 0 | 32 | | | | -| | +---------------+----------------+------+---------+----------------+ | | | -| +SetProperty | p.seen = true | 14 | 14 | 28 | | | | | -| | +---------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 5/0 | 41,656 | Fused in Pipeline 0 | -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | p | 3 | 0 | 0 | | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +Eager | 1 | | 10 | 0 | 0 | 16 | 0/0 | 0.000 | Fused in Pipeline 1 | +| | +----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ExhaustiveLimit | 2 | 3 | 3 | 0 | 0 | 32 | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +SetProperty | 3 | p.seen = true | 10 | 0 | 0 | | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 4 | p:Person | 10 | 0 | 1 | 120 | 1/0 | 0.337 | Fused in Pipeline 0 | ++------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 99, total allocated memory: 200 +Total database accesses: 1, total allocated memory: 216 ---- ====== @@ -4256,7 +4303,7 @@ However, if no data is returned by its source, `Optional` will yield a single ro ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person {name: 'me'}) @@ -4306,7 +4353,7 @@ The `ProjectEndpoints` operator projects the start and end node of a relationshi ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CREATE (n)-[p:KNOWS]->(m) @@ -4359,7 +4406,7 @@ For each incoming row, the `Projection` operator evaluates a set of expressions ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE RETURN 'hello' AS greeting @@ -4401,7 +4448,7 @@ The `ShortestPath` operator finds one or all shortest paths between two previous ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH @@ -4450,7 +4497,7 @@ The `EmptyRow` operator returns a single row with no columns. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CYPHER runtime=slotted @@ -4501,7 +4548,7 @@ The `ProcedureCall` operator indicates an invocation to a procedure. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CALL db.labels() YIELD label @@ -4547,9 +4594,8 @@ In the plan below we will cache `l.name` before `Expand(All)` where there are fe .CacheProperties ====== - .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (l:Location)<-[:WORKS_IN]-(p:Person) @@ -4569,23 +4615,19 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | location, name | 15 | 15 | 0 | | | | | -| | +-------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | cache[l.name] AS location, p.name AS name | 15 | 15 | 30 | | | | | -| | +-------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | p:Person | 15 | 15 | 30 | | | | | -| | +-------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (l)<-[anon_0:WORKS_IN]-(p) | 15 | 15 | 16 | | | | | -| | +-------------------------------------------+----------------+------+---------+----------------+ | | | -| +CacheProperties | cache[l.name] | 10 | 10 | 10 | | | | | -| | +-------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | l:Location | 10 | 10 | 35 | 120 | 5/0 | 0,422 | Fused in Pipeline 0 | -+------------------+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------------+----+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------+----+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | location, name | 0 | 0 | 0 | | | | | +| | +----+------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | l.name AS location, p.name AS name | 0 | 0 | 0 | | | | | +| | +----+------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | l:Location AND p:Person | 0 | 0 | 0 | | | | | +| | +----+------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipTypeScan | 3 | (p)-[anon_0:WORKS_IN]->(l) | 3 | 0 | 0 | 120 | 0/0 | 0.094 | Fused in Pipeline 0 | ++-------------------------------+----+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 157, total allocated memory: 200 +Total database accesses: 0, total allocated memory: 184 ---- ====== @@ -4602,7 +4644,7 @@ The `Create` operator is used to create nodes and relationships. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CREATE @@ -4650,7 +4692,7 @@ The `Delete` operator is used to delete a node or a relationship. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (me:Person {name: 'me'})-[w:WORKS_IN {duration: 190}]->(london:Location {name: 'London'}) @@ -4703,7 +4745,7 @@ The `DetachDelete` operator is used in all queries containing the xref::clauses/ ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (p:Person) @@ -4755,7 +4797,7 @@ The `SetLabels` operator is used when setting labels on a node. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n) @@ -4802,7 +4844,7 @@ The `RemoveLabels` operator is used when deleting labels from a node. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n) @@ -4849,7 +4891,7 @@ The `SetNodePropertiesFromMap` operator is used when setting properties from a m ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n) @@ -4896,7 +4938,7 @@ The `SetRelationshipPropertiesFromMap` operator is used when setting properties ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n)-[r]->(m) @@ -4943,7 +4985,7 @@ The `SetProperty` operator is used when setting a property on a node or relation ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE MATCH (n) @@ -5001,7 +5043,7 @@ The following query will create a property uniqueness constraint with the name ` ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CREATE CONSTRAINT uniqueness @@ -5043,7 +5085,7 @@ The following query will create a property uniqueness constraint with the name ` ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CREATE CONSTRAINT uniqueness IF NOT EXISTS @@ -5097,7 +5139,7 @@ FOR (book:Book) REQUIRE book.isbn IS UNIQUE ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE DROP CONSTRAINT name @@ -5136,7 +5178,7 @@ It may include filtering on constraint type and can have either default or full ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE SHOW CONSTRAINTS @@ -5179,7 +5221,7 @@ This index can either be a fulltext, point, range, text, or lookup index. The following query will create an index with the name `my_index` on the `name` property of nodes with the `Country` label. .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CREATE INDEX my_index @@ -5221,7 +5263,7 @@ The following query will create an index with the name `my_index` on the `since` ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE CREATE INDEX my_index IF NOT EXISTS @@ -5262,7 +5304,7 @@ The `DropIndex` operator removes an index using the name of the index. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE DROP INDEX my_index @@ -5301,7 +5343,7 @@ It may include filtering on index type and can have either default or full outpu ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE SHOW INDEXES @@ -5344,7 +5386,7 @@ The output can either be default or full output. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE SHOW FUNCTIONS @@ -5385,7 +5427,7 @@ It may include filtering on whether a given user can execute the procedure and c ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE SHOW PROCEDURES @@ -5426,7 +5468,7 @@ It may include filtering on given ids and can have either default or full output ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE SHOW TRANSACTIONS @@ -5467,7 +5509,7 @@ The `TerminateTransactions` operator terminates transactions by ID. ====== .Query -[source, cypher, role="noplay"] +[source, cypher] ---- PROFILE TERMINATE TRANSACTIONS 'database-transaction-123' From e780eca28013025c9e1f4c3385bd8babdf297516 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Fri, 10 Feb 2023 09:20:26 +0100 Subject: [PATCH 116/383] Fix path representation, add labels to query --- modules/ROOT/pages/clauses/create.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/clauses/create.adoc b/modules/ROOT/pages/clauses/create.adoc index 45f0ee88c..f22224e6f 100644 --- a/modules/ROOT/pages/clauses/create.adoc +++ b/modules/ROOT/pages/clauses/create.adoc @@ -247,7 +247,7 @@ When you use `CREATE` and a pattern, all parts of the pattern that are not alrea .Query [source, cypher] ---- -CREATE p = (andy {name:'Andy'})-[:WORKS_AT]->(neo)<-[:WORKS_AT]-(michael {name: 'Michael'}) +CREATE p = (:Person {name:'Andy'})-[:WORKS_AT]->(:Company {name: 'Neo4j'})<-[:WORKS_AT]-(:Person {name: 'Michael'}) RETURN p ---- @@ -257,7 +257,7 @@ This query creates three nodes and two relationships in one go, assigns it to a [role="queryresult",options="header,footer",cols="1*(3)<-[WORKS_AT,1]-(4)+ +| [{"name":"Andy"},{},{"name":"Neo4j"},{"name":"Neo4j"},{},{"name":"Michael"}] 1+d|Rows: 1 + Nodes created: 3 + Relationships created: 2 + From c6214910c4533e23239486281d7479c561dd7e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 10 Feb 2023 13:16:32 +0100 Subject: [PATCH 117/383] Update shortest path examples on MATCH page (testing) (#404) Also fix variable name in one example. --- .../MATCH_shortestpath_with_predicates_example.svg | 2 +- modules/ROOT/pages/clauses/match.adoc | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg b/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg index e3e0b9848..8ee63a467 100644 --- a/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg +++ b/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg @@ -1 +1 @@ -Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)ACTED_INACTED_IN Charlie Sheen Martin Sheen Wall Street \ No newline at end of file +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)OLD FRIENDSACTED_INACTED_IN Rob Reiner Michael Douglas Martin Sheen The American Pre… \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index de816bb06..70991853a 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -376,7 +376,7 @@ For example, the following query creates a relationship which contains a space ( [source, cypher, indent=0] ---- MATCH - (charlie:Person {name: 'Martin Sheen'}), + (martin:Person {name: 'Martin Sheen'}), (rob:Person {name: 'Rob Reiner'}) CREATE (rob)-[:`OLD FRIENDS`]->(martin) ---- @@ -682,14 +682,14 @@ Predicates used in the `WHERE` clause that apply to the shortest path pattern ar [source, cypher] ---- MATCH - (charlie:Person {name: 'Charlie Sheen'}), - (martin:Person {name: 'Martin Sheen'}), - p = shortestPath((charlie)-[*]-(martin)) -WHERE none(r IN relationships(p) WHERE type(r) = 'FATHER_OF') + (rob:Person {name: 'Rob Reiner'}), + (michael:Person {name: 'Michael Douglas'}), + p = shortestPath((rob)-[*]-(michael)) +WHERE none(r IN relationships(p) WHERE type(r) = 'DIRECTED') RETURN p ---- -This query will find the shortest path between `Charlie Sheen` and `Martin Sheen`, and the `WHERE` predicate will ensure that the `FATHER_OF` relationship between the two is not considered. +This query will find the shortest path between `Rob Reiner` and `Michael Douglas`, and the `WHERE` predicate will ensure that any relationship with the type `DIRECTED` is not considered. It returns the following graph and text: @@ -699,7 +699,7 @@ image::MATCH_shortestpath_with_predicates_example.svg[width="400",role="middle"] [role="queryresult",options="header,footer",cols="1* Date: Fri, 10 Feb 2023 14:05:30 +0100 Subject: [PATCH 118/383] remove test-result-skip for time (#405) --- modules/ROOT/pages/clauses/limit.adoc | 2 +- modules/ROOT/pages/clauses/match.adoc | 2 +- modules/ROOT/pages/functions/temporal/index.adoc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ROOT/pages/clauses/limit.adoc b/modules/ROOT/pages/clauses/limit.adoc index 079f3241b..a1759c211 100644 --- a/modules/ROOT/pages/clauses/limit.adoc +++ b/modules/ROOT/pages/clauses/limit.adoc @@ -130,7 +130,7 @@ Properties set: 1 If we want to limit the number of updates we can split the query using the `WITH` clause: .Query -[source, cypher] +[source, cypher, role="test-result-skip"] ---- MATCH (n) WITH n LIMIT 1 diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 70991853a..031015903 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -520,7 +520,7 @@ The following query adds two new paths between `Charlie Sheen` and his father `M The query makes evident that both actors had a leading role in the movie `No Code of Conduct`, but only `Martin Sheen` had a leading role in the movie `Free Money`. .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (charlie:Person {name: 'Charlie Sheen'}), diff --git a/modules/ROOT/pages/functions/temporal/index.adoc b/modules/ROOT/pages/functions/temporal/index.adoc index 0b643005b..f44f0780d 100644 --- a/modules/ROOT/pages/functions/temporal/index.adoc +++ b/modules/ROOT/pages/functions/temporal/index.adoc @@ -4254,7 +4254,7 @@ The current time of day using the local time zone is returned. ====== .Query -[source, cypher, role="test-result-skip"] +[source, cypher] ---- RETURN time({timezone: 'America/Los Angeles'}) AS currentTimeInLA ---- From 9aeb38fd1e0f1ca0c603e145160f6e1f7b607027 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Fri, 10 Feb 2023 17:29:39 +0100 Subject: [PATCH 119/383] Create relationships as directed in styleguide (testing) (#410) Creating the relationships as directed avoids the bug addressed in https://github.com/neo-technology/neo4j/pull/19075 , not yet released in 5.x, and is also better practice than creating undirected. --- modules/ROOT/pages/styleguide.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/styleguide.adoc b/modules/ROOT/pages/styleguide.adoc index 9dc513891..1276b883e 100644 --- a/modules/ROOT/pages/styleguide.adoc +++ b/modules/ROOT/pages/styleguide.adoc @@ -59,7 +59,7 @@ Put `ON CREATE` before `ON MATCH` if both are present. [source, cypher] ---- MERGE (n) ON CREATE SET n.prop = 0 -MERGE (a:A)-[:T]-(b:B) +MERGE (a:A)-[:T]->(b:B) ON MATCH SET b.name = 'you' ON CREATE SET a.name = 'me' RETURN a.prop @@ -70,7 +70,7 @@ RETURN a.prop ---- MERGE (n) ON CREATE SET n.prop = 0 -MERGE (a:A)-[:T]-(b:B) +MERGE (a:A)-[:T]->(b:B) ON CREATE SET a.name = 'me' ON MATCH SET b.name = 'you' RETURN a.prop From 09c7079b83120d567e48f87ada6882641436c331 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Mon, 13 Feb 2023 11:01:58 +0100 Subject: [PATCH 120/383] Make example in `limit.adoc` deterministic (#411) This makes the example always return the same result by enforcing an ordering, so that we don't need to skip testing its result (this PR updates #405). The sense of the example is unaffected. --- modules/ROOT/pages/clauses/limit.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/clauses/limit.adoc b/modules/ROOT/pages/clauses/limit.adoc index a1759c211..2c4cb3d30 100644 --- a/modules/ROOT/pages/clauses/limit.adoc +++ b/modules/ROOT/pages/clauses/limit.adoc @@ -130,12 +130,13 @@ Properties set: 1 If we want to limit the number of updates we can split the query using the `WITH` clause: .Query -[source, cypher, role="test-result-skip"] +[source, cypher] ---- MATCH (n) WITH n LIMIT 1 SET n.locked = true RETURN n +ORDER BY n.name ---- Writes `locked` property on one node and return that node: From a9cd43f4d55620c61fdc8174e1e7d1eaff767c86 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Mon, 13 Feb 2023 11:30:02 +0100 Subject: [PATCH 121/383] make example really deterministic --- modules/ROOT/pages/clauses/limit.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/ROOT/pages/clauses/limit.adoc b/modules/ROOT/pages/clauses/limit.adoc index 2c4cb3d30..67669f04e 100644 --- a/modules/ROOT/pages/clauses/limit.adoc +++ b/modules/ROOT/pages/clauses/limit.adoc @@ -133,10 +133,9 @@ If we want to limit the number of updates we can split the query using the `WITH [source, cypher] ---- MATCH (n) -WITH n LIMIT 1 +WITH n ORDER BY n.name LIMIT 1 SET n.locked = true RETURN n -ORDER BY n.name ---- Writes `locked` property on one node and return that node: From 680ef9b52e5c63f9ed1976e8a1e804f427913104 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Tue, 14 Feb 2023 09:08:12 +0100 Subject: [PATCH 122/383] Remove `WAIT` from database creation tests setups (#415) Since https://github.com/neo4j/docs-testing/commit/a350d6967bae90d0145024c1d9fb0188a2450abc , the tester automatically appends `WAIT` to database administration queries that don't have it, so we don't need to put it in explicitly. --- modules/ROOT/pages/aliases.adoc | 16 ++++++++-------- modules/ROOT/pages/databases.adoc | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index befdf6a60..593137aea 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -244,17 +244,17 @@ This prevents issues such as a transaction executing against multiple target dat //// [source, cypher, role=test-setup] ---- -CREATE DATABASE `movies` WAIT; +CREATE DATABASE `movies`; CREATE ALIAS `films` FOR DATABASE `movies`; CREATE ALIAS `motion pictures` FOR DATABASE `movies` PROPERTIES { nameContainsSpace: true }; -CREATE DATABASE `northwind-graph-2020` WAIT; // wait, so aliases get `currentStatus: online` -CREATE DATABASE `northwind-graph-2021` WAIT; -CREATE DATABASE `northwind-graph-2022` WAIT; -CREATE DATABASE `sci-fi-books` WAIT; -CREATE COMPOSITE DATABASE `library` WAIT; +CREATE DATABASE `northwind-graph-2020`; +CREATE DATABASE `northwind-graph-2021`; +CREATE DATABASE `northwind-graph-2022`; +CREATE DATABASE `sci-fi-books`; +CREATE COMPOSITE DATABASE `library`; CREATE ALIAS `library`.`sci-fi` FOR DATABASE `sci-fi-books`; -CREATE COMPOSITE DATABASE garden WAIT; -CREATE DATABASE `perennial-flowers` WAIT; +CREATE COMPOSITE DATABASE garden; +CREATE DATABASE `perennial-flowers`; ---- CREATE ALIAS `library`.`romance` FOR DATABASE `romance-books` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password'; diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 475201f25..0e78c7f87 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -3,7 +3,7 @@ //// [source, cypher, role=test-setup] ---- -CREATE DATABASE `movies` WAIT; +CREATE DATABASE `movies`; CREATE ALIAS `films` FOR DATABASE `movies`; CREATE ALIAS `motion pictures` FOR DATABASE `movies`; ---- @@ -438,9 +438,9 @@ For composite databases the `constituents` column is particularly interesting as //// [source, cypher, role=test-setup] ---- -CREATE COMPOSITE DATABASE `library` WAIT; -CREATE DATABASE `sci-fi` WAIT; -CREATE DATABASE `romance` WAIT; +CREATE COMPOSITE DATABASE `library`; +CREATE DATABASE `sci-fi`; +CREATE DATABASE `romance`; CREATE ALIAS `library`.`sci-fi` FOR DATABASE `sci-fi`; CREATE ALIAS `library`.`romance` FOR DATABASE `romance`; ---- From 78ca77591f708e3c273555ad6796b4bb3caa48a7 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Tue, 14 Feb 2023 16:17:15 +0100 Subject: [PATCH 123/383] Add a note about constraint creation failing on first offence (#406) Including helpful Cypher to find all offending nodes/relationships. --- modules/ROOT/pages/constraints/examples.adoc | 63 ++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc index fbbc1a784..6fab58884 100644 --- a/modules/ROOT/pages/constraints/examples.adoc +++ b/modules/ROOT/pages/constraints/examples.adoc @@ -317,6 +317,20 @@ The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6. ====== +The constraint creation fails on the first offending nodes that are found. +This does not guarantee that there are no other offending nodes in the data. +Therefore, all the data should be checked and cleaned up before re-attempting the constraint creation. + +This is an example `MATCH` query to find all offending nodes with the non-unique property values for the constraint above: + +.Query +[source, cypher] +---- +MATCH (book1:Book), (book2:Book) +WHERE book1.title = book2.title AND NOT book1 = book2 +RETURN book1, book2 +---- + //// TODO: Re-add this part when adding back relationship key and uniqueness constraints TODO: Remove 'test-skip' message on queries when feature is introduced @@ -824,6 +838,20 @@ Node(0) with label `Author` must have the property `nationality` ====== +The constraint creation fails on the first offending node that is found. +This does not guarantee that there are no other offending nodes in the data. +Therefore, all the data should be checked and cleaned up before re-attempting the constraint creation. + +This is an example `MATCH` query to find all offending nodes missing the property for the constraint above: + +.Query +[source, cypher] +---- +MATCH (author:Author) +WHERE author.nationality IS NULL +RETURN author +---- + [role=enterprise-edition] [[constraints-examples-relationship-property-existence]] @@ -1047,6 +1075,20 @@ Relationship(0) with type `WROTE` must have the property `language` ====== +The constraint creation fails on the first offending relationship that are found. +This does not guarantee that there are no other offending relationships in the data. +Therefore, all the data should be checked and cleaned up before re-attempting the constraint creation. + +This is an example `MATCH` query to find all offending relationships missing the property for the constraint above: + +.Query +[source, cypher] +---- +MATCH ()-[wrote:WROTE]-() +WHERE wrote.language IS NULL +RETURN wrote +---- + [role=enterprise-edition] [[constraints-examples-node-key]] @@ -1336,6 +1378,27 @@ Node(0) with label `Actor` must have the property `born` ====== +The constraint creation fails on the first offending nodes that are found. +This does not guarantee that there are no other offending nodes in the data. +Therefore, all the data should be checked and cleaned up before re-attempting the constraint creation. + +This is an example `MATCH` query to find all offending nodes for the constraint above: + +.Query +[source, cypher] +---- +MATCH (actor1:Actor), (actor2:Actor) +WHERE actor1.born = actor2.born AND NOT actor1 = actor2 +UNWIND [actor1, actor2] AS actor +RETURN actor, 'non-unique' AS reason + +UNION + +MATCH (actor:Actor) +WHERE actor.born IS NULL +RETURN actor, 'non-existing' AS reason +---- + //// TODO: Re-add this part when adding back relationship key and uniqueness constraints [role=enterprise-edition] From 8abbb6fe98d3057116c175d69ddc1f80ddaa84af Mon Sep 17 00:00:00 2001 From: Lidia Zuin <102308961+lidiazuin@users.noreply.github.com> Date: Wed, 15 Feb 2023 12:35:51 +0100 Subject: [PATCH 124/383] fixing command according to description and results (#417) (#419) Cherry-picked from https://github.com/neo4j/docs-cypher/pull/417 --- modules/ROOT/pages/access-control/manage-roles.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ROOT/pages/access-control/manage-roles.adoc b/modules/ROOT/pages/access-control/manage-roles.adoc index edc504436..33043227e 100644 --- a/modules/ROOT/pages/access-control/manage-roles.adoc +++ b/modules/ROOT/pages/access-control/manage-roles.adoc @@ -604,7 +604,7 @@ The roles assigned to each user can be seen on the list provided by `SHOW USERS` [source, cypher, role=noplay] ---- -SHOW ROLES +SHOW USERS ---- .Result @@ -658,7 +658,7 @@ GRANT ROLES role1, role2 TO user1, user2, user3 [source, cypher, role=noplay] ---- -SHOW ROLES +SHOW USERS ---- .Result @@ -718,7 +718,7 @@ The roles revoked from users can no longer be seen on the list provided by `SHOW [source, cypher, role=noplay] ---- -SHOW ROLES +SHOW USERS ---- .Result From bd88a76a6c24b47d4d7a17fe5760c74221775adf Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Thu, 16 Feb 2023 06:53:37 +0100 Subject: [PATCH 125/383] Improve test setup blocks in operators page. --- .../ROOT/pages/execution-plans/operators.adoc | 167 ++++++++---------- 1 file changed, 76 insertions(+), 91 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 30c731c80..01d06e83e 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -14,69 +14,69 @@ If that is the case, the example queries will be prefixed with an option to choo //// [source, cypher, role=test-setup] ---- -CREATE (me:Person {name: 'me'}); -CREATE (andy:Person {name: 'Andy'}); -CREATE (bob:Person {name: 'Bob'}); -CREATE (mattias:Person {name: 'Mattias'}); -CREATE (lovis:Person {name: 'Lovis'}); -CREATE (pontus:Person {name: 'Pontus'}); -CREATE (max:Person {name: 'Max'}); -CREATE (konstantin:Person {name: 'Konstantin'}); -CREATE (stefan:Person {name: 'Stefan'}); -CREATE (mats:Person {name: 'Mats'}); -CREATE (petra:Person {name: 'Petra'}); -CREATE (craig:Person {name: 'Craig'}); -CREATE (steven:Person {name: 'Steven'}); -CREATE (chris:Person {name: 'Chris'}); -CREATE (london:Location {name: 'London'}); -CREATE (malmo:Location {name: 'Malmo'}); -CREATE (sf:Location {name: 'San Francisco'}); -CREATE (berlin:Location {name: 'Berlin'}); -CREATE (newyork:Location {name: 'New York'}); -CREATE (kuala:Location {name: 'Kuala Lumpur'}); -CREATE (stockholm:Location {name: 'Stockholm'}); -CREATE (paris:Location {name: 'Paris'}); -CREATE (madrid:Location {name: 'Madrid'}); -CREATE (rome:Location {name: 'Rome'}); -CREATE (england:Country {name: 'England'}); -CREATE (field:Team {name: 'Field'}); -CREATE (engineering:Team {name: 'Engineering', id:42}); -CREATE (sales:Team {name: 'Sales'}); -CREATE (monads:Team {name: 'Team Monads'}); -CREATE (birds:Team {name: 'Team Enlightened Birdmen'}); -CREATE (quality:Team {name: 'Team Quality'}); -CREATE (rassilon:Team {name: 'Team Rassilon'}); -CREATE (executive:Team {name: 'Team Executive'}); -CREATE (remoting:Team {name: 'Team Remoting'}); -CREATE (other:Team {name: 'Other'}); -CREATE (me)-[:WORKS_IN {duration: 190, title: 'senior sales engineer'}]->(london); -CREATE (bob)-[:WORKS_IN {duration: 187, title: 'junior developer'}]->(london); -CREATE (andy)-[:WORKS_IN {duration: 150, title: ''}]->(london); -CREATE (mattias)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(london); -CREATE (lovis)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(sf); -CREATE (pontus)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(malmo); -CREATE (max)-[:WORKS_IN {duration: 230, title: 'field engineer'}]->(newyork); -CREATE (konstantin)-[:WORKS_IN {duration: 230, title: 'frontend developer'}]->(london); -CREATE (stefan)-[:WORKS_IN {duration: 230, title: 'chief architect'}]->(london); -CREATE (stefan)-[:WORKS_IN {duration: 230, title: 'language architect'}]->(berlin); -CREATE (mats)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(malmo); -CREATE (petra)-[:WORKS_IN {duration: 230, title: 'language architect'}]->(london); -CREATE (craig)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(malmo); -CREATE (steven)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(malmo); -CREATE (chris)-[:WORKS_IN {duration: 230, title: 'field engineer'}]->(madrid); -CREATE (london)-[:IN]->(england); -CREATE (me)-[:FRIENDS_WITH]->(andy); -CREATE (andy)-[:FRIENDS_WITH]->(bob); -CREATE (mattias)-[:FRIENDS_WITH]->(max); -CREATE (pontus)-[:FRIENDS_WITH]->(mats); -CREATE (konstantin)-[:FRIENDS_WITH]->(steven); -CREATE (craig)-[:FRIENDS_WITH]->(stefan); -CREATE (petra)-[:FRIENDS_WITH]->(lovis); -CREATE (me)-[:FRIENDS_WITH]->(chris); -CREATE (chris)-[:FRIENDS_WITH]->(stefan); -CREATE (bob)-[:FRIENDS_WITH]->(andy); -CREATE (steven)-[:FRIENDS_WITH]->(mats); -CREATE (mattias)-[:FRIENDS_WITH]->(me); +CREATE (me:Person {name: 'me'}), + (andy:Person {name: 'Andy'}), + (bob:Person {name: 'Bob'}), + (mattias:Person {name: 'Mattias'}), + (lovis:Person {name: 'Lovis'}), + (pontus:Person {name: 'Pontus'}), + (max:Person {name: 'Max'}), + (konstantin:Person {name: 'Konstantin'}), + (stefan:Person {name: 'Stefan'}), + (mats:Person {name: 'Mats'}), + (petra:Person {name: 'Petra'}), + (craig:Person {name: 'Craig'}), + (steven:Person {name: 'Steven'}), + (chris:Person {name: 'Chris'}), + (london:Location {name: 'London'}), + (malmo:Location {name: 'Malmo'}), + (sf:Location {name: 'San Francisco'}), + (berlin:Location {name: 'Berlin'}), + (newyork:Location {name: 'New York'}), + (kuala:Location {name: 'Kuala Lumpur'}), + (stockholm:Location {name: 'Stockholm'}), + (paris:Location {name: 'Paris'}), + (madrid:Location {name: 'Madrid'}), + (rome:Location {name: 'Rome'}), + (england:Country {name: 'England'}), + (field:Team {name: 'Field'}), + (engineering:Team {name: 'Engineering', id:42}), + (sales:Team {name: 'Sales'}), + (monads:Team {name: 'Team Monads'}), + (birds:Team {name: 'Team Enlightened Birdmen'}), + (quality:Team {name: 'Team Quality'}), + (rassilon:Team {name: 'Team Rassilon'}), + (executive:Team {name: 'Team Executive'}), + (remoting:Team {name: 'Team Remoting'}), + (other:Team {name: 'Other'}), + (me)-[:WORKS_IN {duration: 190, title: 'senior sales engineer'}]->(london), + (bob)-[:WORKS_IN {duration: 187, title: 'junior developer'}]->(london), + (andy)-[:WORKS_IN {duration: 150, title: ''}]->(london), + (mattias)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(london), + (lovis)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(sf), + (pontus)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(malmo), + (max)-[:WORKS_IN {duration: 230, title: 'field engineer'}]->(newyork), + (konstantin)-[:WORKS_IN {duration: 230, title: 'frontend developer'}]->(london), + (stefan)-[:WORKS_IN {duration: 230, title: 'chief architect'}]->(london), + (stefan)-[:WORKS_IN {duration: 230, title: 'language architect'}]->(berlin), + (mats)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(malmo), + (petra)-[:WORKS_IN {duration: 230, title: 'language architect'}]->(london), + (craig)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(malmo), + (steven)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(malmo), + (chris)-[:WORKS_IN {duration: 230, title: 'field engineer'}]->(madrid), + (london)-[:IN]->(england), + (me)-[:FRIENDS_WITH]->(andy), + (andy)-[:FRIENDS_WITH]->(bob), + (mattias)-[:FRIENDS_WITH]->(max), + (pontus)-[:FRIENDS_WITH]->(mats), + (konstantin)-[:FRIENDS_WITH]->(steven), + (craig)-[:FRIENDS_WITH]->(stefan), + (petra)-[:FRIENDS_WITH]->(lovis), + (me)-[:FRIENDS_WITH]->(chris), + (chris)-[:FRIENDS_WITH]->(stefan), + (bob)-[:FRIENDS_WITH]->(andy), + (steven)-[:FRIENDS_WITH]->(mats), + (mattias)-[:FRIENDS_WITH]->(me); ---- //// @@ -129,7 +129,7 @@ Total database accesses: 36, total allocated memory: 184 //// [source, cypher, role=test-setup] ---- -CREATE range INDEX relprop_index_range FOR ()-[r:WORKS_IN]->() ON (r.title) +CREATE RANGE INDEX relprop_index_range FOR ()-[r:WORKS_IN]->() ON (r.title) ---- //// @@ -1107,8 +1107,7 @@ Total database accesses: 15, total allocated memory: 184 //// [source, cypher, role=test-setup] ---- -CREATE INDEX index_node_seek -FOR (l:Location) ON (l.name) +CREATE RANGE INDEX index_node_seek FOR (l:Location) ON (l.name) ---- //// @@ -1159,9 +1158,7 @@ Total database accesses: 2, total allocated memory: 184 //// [source, cypher, role=test-setup] ---- -CREATE CONSTRAINT constraint_Team_name IF NOT EXISTS -FOR (t:Team) -REQUIRE (t.name) IS UNIQUE +CREATE CONSTRAINT constraint_Team_name IF NOT EXISTS FOR (t:Team) REQUIRE (t.name) IS UNIQUE ---- //// @@ -1213,7 +1210,7 @@ Total database accesses: 1, total allocated memory: 184 //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX FOR (p:Person) ON (p.name) +CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name) ---- //// @@ -1326,8 +1323,7 @@ Total database accesses: 4, total allocated memory: 184 [source, cypher, role=test-setup] ---- DROP INDEX index_node_seek; -CREATE RANGE INDEX index_range_node_location -FOR (l:Location) ON (l.name) +CREATE RANGE INDEX index_range_node_location FOR (l:Location) ON (l.name) ---- //// @@ -5112,12 +5108,7 @@ Runtime version {neo4j-version-minor} Total database accesses: ? ---- -//// -[source, cypher, role=test-setup] ----- -DROP CONSTRAINT uniqueness ----- -//// + ====== @@ -5127,13 +5118,7 @@ DROP CONSTRAINT uniqueness The `DropConstraint` operator removes a constraint using the name of the constraint, no matter the type. -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT name IF NOT EXISTS -FOR (book:Book) REQUIRE book.isbn IS UNIQUE ----- -//// + .DropConstraint ====== @@ -5142,7 +5127,7 @@ FOR (book:Book) REQUIRE book.isbn IS UNIQUE [source, cypher] ---- PROFILE -DROP CONSTRAINT name +DROP CONSTRAINT uniqueness ---- .Query Plan @@ -5154,11 +5139,11 @@ Runtime SCHEMA Runtime version {neo4j-version-minor} -+-----------------+-----------------+ -| Operator | Details | -+-----------------+-----------------+ -| +DropConstraint | CONSTRAINT name | -+-----------------+-----------------+ ++-----------------+-----------------------+ +| Operator | Details | ++-----------------+-----------------------+ +| +DropConstraint | CONSTRAINT uniqueness | ++-----------------+-----------------------+ Total database accesses: ? ---- From 58311ee0c06ef1edecf35e59e6936f3db8139ce3 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Thu, 16 Feb 2023 07:38:42 +0100 Subject: [PATCH 126/383] Add some nodes and relationships to test setups in indexes page. --- modules/ROOT/pages/query-tuning/indexes.adoc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index 0c47653bd..1b0fad3ee 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -647,7 +647,8 @@ But in these cases rewrites might happen depending on which properties have whic //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX node_range_age_country FOR (n:Person) ON (n.age, n.country) +CREATE (p0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}); +CREATE RANGE INDEX node_range_age_country FOR (n:Person) ON (n.age, n.country); ---- //// @@ -734,7 +735,15 @@ However, predicates after the range predicate may be rewritten as an existence c //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX rel_range_since_lastmet FOR ()-[r:KNOWS]-() ON (r.since, r.lastMet) +MERGE (a:Person {name: 'a'}) +MERGE (b:Person {name: 'b'}) +MERGE (c:Person {name: 'c'}) +MERGE (d:Person {name: 'd'}) +MERGE (a)-[:KNOWS {since: 1900, lastMet: 2020}]->(b) +MERGE (a)-[:KNOWS {since: 2017, lastMet: 2021}]->(c) +MERGE (b)-[:KNOWS {since: 2017, lastMet: 2021}]->(d) +CREATE RANGE INDEX rel_range_since_lastmet FOR ()-[r:KNOWS]-() ON (r.since, r.lastMet); + ---- //// @@ -831,6 +840,7 @@ That single range seek created in the following query will then use the composit [source, cypher, role=test-setup] ---- CREATE INDEX FOR (p:Person) ON (p.highScore, p.name); +CREATE (:Person {name: 'Tom', highScore: 15000}); ---- //// From d640112f9a4f1777d6ea8c87293ebca00ca7f5be Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Thu, 16 Feb 2023 08:05:00 +0100 Subject: [PATCH 127/383] Fix mistake in test setup and add more dummy data --- modules/ROOT/pages/query-tuning/indexes.adoc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index 1b0fad3ee..2c00458bb 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -742,8 +742,13 @@ MERGE (d:Person {name: 'd'}) MERGE (a)-[:KNOWS {since: 1900, lastMet: 2020}]->(b) MERGE (a)-[:KNOWS {since: 2017, lastMet: 2021}]->(c) MERGE (b)-[:KNOWS {since: 2017, lastMet: 2021}]->(d) +MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) +MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) +MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) +MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) +MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) +MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y); CREATE RANGE INDEX rel_range_since_lastmet FOR ()-[r:KNOWS]-() ON (r.since, r.lastMet); - ---- //// From 7dcd3d89c9fca0ce5f4dcbf0bb2949c906def6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:01:54 +0100 Subject: [PATCH 128/383] Update WHERE page (#418) - New graph - Create set-up query reduced (there were several create clauses that were previously not used in the examples) - Remove ToC before introduction (too large, and redundant given pre-existing ToC on the right-hand side of the page) - General edit --- modules/ROOT/images/graph_where_clause.svg | 123 +-------- modules/ROOT/pages/clauses/where.adoc | 292 ++++++++------------- 2 files changed, 104 insertions(+), 311 deletions(-) diff --git a/modules/ROOT/images/graph_where_clause.svg b/modules/ROOT/images/graph_where_clause.svg index b7f52981f..b307c8355 100644 --- a/modules/ROOT/images/graph_where_clause.svg +++ b/modules/ROOT/images/graph_where_clause.svg @@ -1,122 +1 @@ - - - - - - -L - - - -N0 - -Swedish, Person - -name = 'Andy' -age = 36 -belt = 'white' - - - -N3 - -Dog - -name = 'Andy' - - - -N0->N3 - - -HAS_DOG -since = 2016 - - - -N2 - -Person - -name = 'Peter' -email = 'peter_n@example.com' -age = 35 - - - -N0->N2 - - -KNOWS -since = 1999 - - - -N1 - -Person - -age = 25 -address = 'Sweden/Malmo' -name = 'Timothy' - - - -N0->N1 - - -KNOWS -since = 2012 - - - -N4 - -Dog - -name = 'Fido' - - - -N2->N4 - - -HAS_DOG -since = 2010 - - - -N5 - -Dog - -name = 'Ozzy' - - - -N2->N5 - - -HAS_DOG -since = 2018 - - - -N6 - -Toy - -name = 'Banana' - - - -N4->N6 - - -HAS_TOY - - - +KNOWSsince:2012KNOWSsince:1999PersonSwedishname:'Andy'age:36belt:'white'Personname:'Timothy'age:25Personname:'Peter'age:35email:'peter_n@example.com' \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index 8b282081f..ee2ecad3d 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -3,73 +3,27 @@ [[query-where]] = WHERE -[abstract] --- -`WHERE` adds constraints to the patterns in a `MATCH` or `OPTIONAL MATCH` clause or filters the results of a `WITH` clause. --- - -* xref::clauses/where.adoc#where-introduction[Introduction] -* xref::clauses/where.adoc#query-where-basic[Basic usage] - ** xref::clauses/where.adoc#node-pattern-predicates[Node pattern predicates] - ** xref::clauses/where.adoc#boolean-operations[Boolean operations] - ** xref::clauses/where.adoc#filter-on-node-label[Filter on node label] - ** xref::clauses/where.adoc#filter-on-node-property[Filter on node property] - ** xref::clauses/where.adoc#filter-on-relationship-property[Filter on relationship property] - ** xref::clauses/where.adoc#filter-on-dynamic-property[Filter on dynamically-computed property] - ** xref::clauses/where.adoc#property-existence-checking[Property existence checking] -* xref::clauses/where.adoc#query-where-string[String matching] - ** xref::clauses/where.adoc#match-string-start[Prefix string search using `STARTS WITH`] - ** xref::clauses/where.adoc#match-string-end[Suffix string search using `ENDS WITH`] - ** xref::clauses/where.adoc#match-string-contains[Substring search using `CONTAINS`] - ** xref::clauses/where.adoc#match-string-negation[String matching negation] -* xref::clauses/where.adoc#query-where-regex[Regular expressions] - ** xref::clauses/where.adoc#matching-using-regular-expressions[Matching using regular expressions] - ** xref::clauses/where.adoc#escaping-in-regular-expressions[Escaping in regular expressions] - ** xref::clauses/where.adoc#case-insensitive-regular-expressions[Case-insensitive regular expressions] -* xref::clauses/where.adoc#query-where-patterns[Using path patterns in `WHERE`] - ** xref::clauses/where.adoc#filter-on-patterns[Filter on patterns] - ** xref::clauses/where.adoc#filter-on-patterns-using-not[Filter on patterns using `NOT`] - ** xref::clauses/where.adoc#filter-on-patterns-with-properties[Filter on patterns with properties] - ** xref::clauses/where.adoc#filter-on-relationship-type[Filter on relationship type] -* xref::clauses/where.adoc#query-where-lists[Lists] - ** xref::clauses/where.adoc#where-in-operator[`IN` operator] -* xref::clauses/where.adoc#missing-properties-and-values[Missing properties and values] - ** xref::clauses/where.adoc#default-to-false-missing-property[Default to `false` if property is missing] - ** xref::clauses/where.adoc#default-to-true-missing-property[Default to `true` if property is missing] - ** xref::clauses/where.adoc#filter-on-null[Filter on `null`] -* xref::clauses/where.adoc#query-where-ranges[Using ranges] - ** xref::clauses/where.adoc#simple-range[Simple range] - ** xref::clauses/where.adoc#composite-range[Composite range] -* xref::clauses/where.adoc#pattern-element-predicates[Pattern element predicates] - ** xref::clauses/where.adoc#relationship-pattern-predicates[Relationship pattern predicates] - - [[where-introduction]] == Introduction -`WHERE` is not a clause in its own right -- rather, it is part of `MATCH`, `OPTIONAL MATCH`, and `WITH`. +The `WHERE` clause is not a clause in its own right -- rather, it is part of the `MATCH`, `OPTIONAL MATCH`, and `WITH` clauses. -In the case of `WITH`, `WHERE` simply filters the results. - -For `MATCH` and `OPTIONAL MATCH` on the other hand, `WHERE` adds constraints to the patterns described. +When used with `MATCH` and `OPTIONAL MATCH`, `WHERE` adds constraints to the patterns described. _It should not be seen as a filter after the matching is finished._ -[IMPORTANT] -==== +In the case of `WITH`, however, `WHERE` simply filters the results. + In the case of multiple `MATCH` / `OPTIONAL MATCH` clauses, the predicate in `WHERE` is always a part of the patterns in the directly preceding `MATCH` / `OPTIONAL MATCH`. -Both results and performance may be impacted if the `WHERE` is put inside the wrong `MATCH` clause. -==== +Both results and performance may be impacted if `WHERE` is put inside the wrong `MATCH` clause. -[NOTE] -==== xref::indexes-for-search-performance.adoc[Indexes] may be used to optimize queries using `WHERE` in a variety of cases. -==== +[[where-example-graph]] == Example graph The following graph is used for the examples below: -image:graph_where_clause.svg[] +image::graph_where_clause.svg[width="600",role="middle"] To recreate the graph, run the following query in an empty Neo4j database: @@ -77,13 +31,10 @@ To recreate the graph, run the following query in an empty Neo4j database: ---- CREATE (andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), -(timothy:Person {name: 'Timothy', age: 25, address: 'Sweden/Malmo'}), +(timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35, email: 'peter_n@example.com'}), (andy)-[:KNOWS {since: 2012}]->(timothy), -(andy)-[:KNOWS {since: 1999}]->(peter), -(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), -(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), -(fido)-[:HAS_TOY]->(:Toy {name:'Banana'}) +(andy)-[:KNOWS {since: 1999}]->(peter) ---- [[query-where-basic]] @@ -94,12 +45,8 @@ CREATE `WHERE` can appear inside a node pattern in a `MATCH` clause or a pattern comprehension: - -.+WHERE+ -====== - .Query -[source, cypher, indent=0] +[source, cypher] ---- WITH 30 AS minAge MATCH (a:Person WHERE a.name = 'Andy')-[:KNOWS]->(b:Person WHERE b.age > minAge) @@ -114,17 +61,12 @@ RETURN b.name 1+d|Rows: 1 |=== -====== - When used this way, predicates in `WHERE` can reference the node variable that the `WHERE` clause belongs to, but not other elements of the `MATCH` pattern. -The same rule applies to pattern comprehensions. - -.+WHERE+ -====== +The same rule applies to pattern comprehensions: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:Person {name: 'Andy'}) RETURN [(a)-->(b WHERE b:Person) | b.name] AS friends @@ -138,16 +80,14 @@ RETURN [(a)-->(b WHERE b:Person) | b.name] AS friends 1+d|Rows: 1 |=== -====== - [[boolean-operations]] === Boolean operations -You can use the boolean operators `AND`, `OR`, `XOR` and `NOT`. -See xref::syntax/working-with-null.adoc[] for more information on how this works with `null`. +The following boolean operators can be used with the `WHERE` clause: `AND`, `OR`, `XOR`, and `NOT`. +For more information on how operators work with `null`, see the chapter on xref::syntax/working-with-null.adoc[Working with null]. .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n:Person) WHERE n.name = 'Peter' XOR (n.age < 30 AND n.name = 'Timothy') OR NOT (n.name = 'Timothy' OR n.name = 'Peter') @@ -171,17 +111,17 @@ ORDER BY name [[filter-on-node-label]] === Filter on node label -To filter nodes by label, write a label predicate after the `WHERE` keyword using `WHERE n:foo`. +To filter nodes by label, write a label predicate after the `WHERE` keyword using `WHERE n:foo`: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (n) WHERE n:Swedish RETURN n.name, n.age ---- -The name and age for the *'Andy'* node will be returned. +The `name` and `age` values for `Andy` are returned: .Result [role="queryresult",options="header,footer",cols="2*(f) WHERE k.since < 2000 RETURN f.name, f.age, f.email ---- -The name, age and email values for the *'Peter'* node are returned because Andy has known him since before 2000. +The `name`, `age` and `email` values for `Peter` are returned because `Andy` has known him since before 2000: .Result [role="queryresult",options="header,footer",cols="3*(b)+` is very different from `+WHERE (a)-[*]->(b)+`. The first will produce a path for every path it can find between `a` and `b`, whereas the latter will eliminate any matched paths where `a` and `b` do not have a directed relationship chain between them. .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (timothy:Person {name: 'Timothy'}), @@ -569,7 +509,7 @@ WHERE other.name IN ['Andy', 'Peter'] AND (other)-->(timothy) RETURN other.name, other.age ---- -The name and age for nodes that have an outgoing relationship to the `'Timothy'` node are returned. +The `name` and `age` values for nodes that have an outgoing relationship to `Timothy` are returned: .Result [role="queryresult",options="header,footer",cols="2*(peter) RETURN person.name, person.age ---- -Name and age values for nodes that do not have an outgoing relationship to the `'Peter'` node are returned. +The `name` and `age` values for nodes that do not have an outgoing relationship to `Peter` are returned: .Result [role="queryresult",options="header,footer",cols="2*() WHERE n.name='Andy' AND type(r) =~ 'K.*' RETURN type(r), r.since ---- -This returns all relationships having a type whose name starts with `'K'`. +This returns all relationships having a type whose name starts with 'K': .Result [role="queryresult",options="header,footer",cols="2*=+`, `+>+`. +To check whether an element exists within a specific range, use the inequality operators `+<+`, `+<=+`, `+>=+`, `+>+`: .Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:Person) WHERE a.name >= 'Peter' RETURN a.name, a.age ---- -The name and age values of nodes having a name property lexicographically greater than or equal to `'Peter'` are returned. +The `name` and `age` values of nodes having a `name` property lexicographically (i.e. using the dictionary order) greater than or equal to `Peter` are returned: .Result [role="queryresult",options="header,footer",cols="2* 'Andy' AND a.name < 'Timothy' RETURN a.name, a.age ---- -The name and age values of nodes having a name property lexicographically between `'Andy'` and `'Timothy'` are returned. +The `name` and `age` values of nodes having a `name` property lexicographically between `Andy` and `Timothy` are returned: .Result [role="queryresult",options="header,footer",cols="2*> in order to specify additional constraints. +`WHERE` clauses can be added to pattern elements in order to specify additional constraints: [[relationship-pattern-predicates]] === Relationship pattern predicates -`WHERE` can also appear inside a relationship pattern in a `MATCH` clause. - - -.+WHERE+ -====== +`WHERE` can also appear inside a relationship pattern in a `MATCH` clause: .Query [source, cypher] @@ -848,14 +783,7 @@ RETURN r.since 1+d|Rows: 1 |=== -====== - - However, it cannot be used inside of variable length relationships, as this would lead to an error. - -.+WHERE+ -====== - For example: .Query @@ -872,15 +800,9 @@ RETURN r.since Relationship pattern predicates are not supported for variable-length relationships. ---- -====== - Putting predicates inside a relationship pattern can help with readability. -Please note that it is strictly equivalent to using a standalone `WHERE` sub-clause. - - -.+WHERE+ -====== +Note that it is strictly equivalent to using a standalone `WHERE` sub-clause. .Query [source, cypher] @@ -899,14 +821,7 @@ RETURN r.since 1+d|Rows: 1 |=== -====== - - -Relationship pattern predicates can also be used inside pattern comprehensions, where the same caveats apply. - - -.+WHERE+ -====== +Relationship pattern predicates can also be used inside pattern comprehensions, where the same caveats apply: .Query [source, cypher] @@ -924,5 +839,4 @@ RETURN [(a)-[r:KNOWS WHERE r.since < minYear]->(b:Person) | r.since] AS years 1+d|Rows: 1 |=== -====== From 25654d465589ef5e8a0da9e98c3f4495e50a5385 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 16 Feb 2023 12:51:50 +0000 Subject: [PATCH 129/383] Add attribute subs to codeblocks (#424) We are using the `{neo4j-version-minor}` attribute in queryplan codeblocks, but this is not converted into its attribute value by default. This PR adds `subs=attributes+` to `role=queryplan` codeblocks so that attribute substitution in the codeblock is processed, and then any default substitutions are applied. See the Asciidoctor docs for more information: https://docs.asciidoctor.org/asciidoc/latest/subs/apply-subs-to-blocks/ --- .../ROOT/pages/execution-plans/operators.adoc | 220 +++++++++--------- .../shortestpath-planning.adoc | 2 +- modules/ROOT/pages/query-tuning/indexes.adoc | 46 ++-- modules/ROOT/pages/query-tuning/using.adoc | 26 +-- 4 files changed, 147 insertions(+), 147 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 01d06e83e..d1fd46c2c 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -104,7 +104,7 @@ RETURN n ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -153,7 +153,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -197,7 +197,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -242,7 +242,7 @@ RETURN candidate ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -287,7 +287,7 @@ RETURN candidate ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -331,7 +331,7 @@ RETURN r, n1 ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -376,7 +376,7 @@ RETURN r, n1 ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -427,7 +427,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -472,7 +472,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -517,7 +517,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -562,7 +562,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -614,7 +614,7 @@ RETURN candidate ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -661,7 +661,7 @@ RETURN candidate ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -708,7 +708,7 @@ RETURN countryOrLocation ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -752,7 +752,7 @@ RETURN countryAndLocation ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -790,7 +790,7 @@ MATCH ()-[r]->() RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -825,7 +825,7 @@ MATCH ()-[r]-() RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -867,7 +867,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -908,7 +908,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -951,7 +951,7 @@ RETURN friendOrFoe ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -994,7 +994,7 @@ RETURN friendOrFoe ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1038,7 +1038,7 @@ RETURN n ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1081,7 +1081,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1132,7 +1132,7 @@ RETURN location ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1185,7 +1185,7 @@ RETURN t ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1241,7 +1241,7 @@ RETURN location, person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1292,7 +1292,7 @@ MERGE (t:Team {name: 'Engineering', id: 42}) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1349,7 +1349,7 @@ RETURN l ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1395,7 +1395,7 @@ RETURN t ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1445,7 +1445,7 @@ RETURN l ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1490,7 +1490,7 @@ RETURN l ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1534,7 +1534,7 @@ RETURN l ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1583,7 +1583,7 @@ RETURN p, q ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1634,7 +1634,7 @@ RETURN p.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1690,7 +1690,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1749,7 +1749,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1813,7 +1813,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1877,7 +1877,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1939,7 +1939,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1997,7 +1997,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2057,7 +2057,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2119,7 +2119,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2184,7 +2184,7 @@ ON CREATE SET p.existed = false ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2232,7 +2232,7 @@ MERGE (s)-[:FRIENDS_WITH]->(s) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2287,7 +2287,7 @@ RETURN p.name, [(p)-[:WORKS_IN]->(location) | location.name] AS cities ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2338,7 +2338,7 @@ MERGE (s)-[:FRIENDS_WITH]->(s) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2394,7 +2394,7 @@ RETURN fof ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2441,7 +2441,7 @@ RETURN fof ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2493,7 +2493,7 @@ RETURN p, l ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2542,7 +2542,7 @@ RETURN p ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2589,7 +2589,7 @@ RETURN p, q ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2636,7 +2636,7 @@ RETURN p ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2689,7 +2689,7 @@ RETURN DISTINCT p, q ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2745,7 +2745,7 @@ RETURN DISTINCT p, q ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2794,7 +2794,7 @@ MERGE (t:Team {name: 'Engineering', id: 42}) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2845,7 +2845,7 @@ CREATE (:Person) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2891,7 +2891,7 @@ RETURN n ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2935,7 +2935,7 @@ RETURN line ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -2998,7 +2998,7 @@ RETURN loc.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3058,7 +3058,7 @@ RETURN p, q ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3112,7 +3112,7 @@ RETURN a.name, b.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3169,7 +3169,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3228,7 +3228,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3293,7 +3293,7 @@ RETURN other.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3356,7 +3356,7 @@ RETURN p, t ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3405,7 +3405,7 @@ FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3459,7 +3459,7 @@ MERGE () ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3527,7 +3527,7 @@ RETURN ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3576,7 +3576,7 @@ RETURN p.name, count(*) AS count ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3624,7 +3624,7 @@ RETURN count(p) AS people ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3670,7 +3670,7 @@ RETURN count(r) AS jobs ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3714,7 +3714,7 @@ RETURN DISTINCT l ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3762,7 +3762,7 @@ RETURN DISTINCT p.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3808,7 +3808,7 @@ RETURN p ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3854,7 +3854,7 @@ LIMIT 3 ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3901,7 +3901,7 @@ SKIP 1 ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -3952,7 +3952,7 @@ ORDER BY p.name ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4003,7 +4003,7 @@ ORDER BY p.name, p.age ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4053,7 +4053,7 @@ LIMIT 2 ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4105,7 +4105,7 @@ LIMIT 2 ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4154,7 +4154,7 @@ MATCH (p:Country) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4209,7 +4209,7 @@ RETURN value ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4256,7 +4256,7 @@ LIMIT 3 ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4308,7 +4308,7 @@ RETURN p, q ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4359,7 +4359,7 @@ RETURN u, v ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4409,7 +4409,7 @@ RETURN 'hello' AS greeting ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4455,7 +4455,7 @@ RETURN p ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4501,7 +4501,7 @@ FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4553,7 +4553,7 @@ ORDER BY label ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4601,7 +4601,7 @@ RETURN ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4650,7 +4650,7 @@ CREATE (max)-[:FRIENDS_WITH]->(chris) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4696,7 +4696,7 @@ DELETE w ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4749,7 +4749,7 @@ DETACH DELETE p ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4801,7 +4801,7 @@ SET n:Person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4848,7 +4848,7 @@ REMOVE n:Person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4895,7 +4895,7 @@ SET n = {weekday: 'Monday', meal: 'Lunch'} ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4942,7 +4942,7 @@ SET r = {weight: 5, unit: 'kg'} ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -4989,7 +4989,7 @@ SET n.checked = true ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -5047,7 +5047,7 @@ FOR (c:Country) REQUIRE c.name is UNIQUE ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner ADMINISTRATION @@ -5089,7 +5089,7 @@ FOR (c:Country) REQUIRE c.name is UNIQUE ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner ADMINISTRATION @@ -5131,7 +5131,7 @@ DROP CONSTRAINT uniqueness ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner ADMINISTRATION @@ -5170,7 +5170,7 @@ SHOW CONSTRAINTS ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -5214,7 +5214,7 @@ FOR (c:Country) ON (c.name) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner ADMINISTRATION @@ -5256,7 +5256,7 @@ FOR ()-[k:KNOWS]-() ON (k.since) ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner ADMINISTRATION @@ -5296,7 +5296,7 @@ DROP INDEX my_index ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner ADMINISTRATION @@ -5335,7 +5335,7 @@ SHOW INDEXES ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -5378,7 +5378,7 @@ SHOW FUNCTIONS ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -5419,7 +5419,7 @@ SHOW PROCEDURES ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -5460,7 +5460,7 @@ SHOW TRANSACTIONS ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -5501,7 +5501,7 @@ TERMINATE TRANSACTIONS 'database-transaction-123' ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST diff --git a/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc b/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc index 78115a4d4..efe4e4a7f 100644 --- a/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc +++ b/modules/ROOT/pages/execution-plans/shortestpath-planning.adoc @@ -78,7 +78,7 @@ RETURN p ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index 2c00458bb..ed6c44e32 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -311,7 +311,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -353,7 +353,7 @@ RETURN r ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -397,7 +397,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -440,7 +440,7 @@ RETURN person, friend ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -483,7 +483,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -527,7 +527,7 @@ RETURN person, friend ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -573,7 +573,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -613,7 +613,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -702,7 +702,7 @@ RETURN friend, person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -762,7 +762,7 @@ RETURN r.since ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -812,7 +812,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -860,7 +860,7 @@ RETURN person .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -906,7 +906,7 @@ RETURN person, friend ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -944,7 +944,7 @@ RETURN person, friend ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -983,7 +983,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1031,7 +1031,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1071,7 +1071,7 @@ RETURN person, friend ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1123,7 +1123,7 @@ RETURN person, friend ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1202,7 +1202,7 @@ RETURN person ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1243,7 +1243,7 @@ RETURN person, friend ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1282,7 +1282,7 @@ RETURN p ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1327,7 +1327,7 @@ RETURN r.lastMetPoint ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -1377,7 +1377,7 @@ RETURN person.firstname ---- .Query Plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST diff --git a/modules/ROOT/pages/query-tuning/using.adoc b/modules/ROOT/pages/query-tuning/using.adoc index 8ce886ab5..6d7913d71 100644 --- a/modules/ROOT/pages/query-tuning/using.adoc +++ b/modules/ROOT/pages/query-tuning/using.adoc @@ -70,7 +70,7 @@ The query above will be used in some of the examples on this page. Without any hints, one index and no join is used. .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -171,7 +171,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -224,7 +224,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -265,7 +265,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -316,7 +316,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -360,7 +360,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -417,7 +417,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -475,7 +475,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -532,7 +532,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -585,7 +585,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -643,7 +643,7 @@ RETURN * ---- .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -702,7 +702,7 @@ RETURN * Without any hint, the planner did not use a join to solve the `OPTIONAL MATCH`. .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST @@ -739,7 +739,7 @@ RETURN * Now the planner uses a join to solve the `OPTIONAL MATCH`. .Query plan -[role="queryplan"] +[role="queryplan", subs="attributes+"] ---- Planner COST From 221dc5f4257a97ab7141b9d557a09edbeaa437e6 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 16 Feb 2023 17:18:27 +0000 Subject: [PATCH 130/383] Update Neo4j version (#426) --- antora.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antora.yml b/antora.yml index a4d5049ed..67e3512a8 100644 --- a/antora.yml +++ b/antora.yml @@ -7,5 +7,5 @@ nav: asciidoc: attributes: neo4j-version: '5' - neo4j-version-minor: '5.5' - neo4j-version-exact: '5.5.0' + neo4j-version-minor: '5.6' + neo4j-version-exact: '5.6.0' From e314d2b4db03cc2a2b9a4a7f3414cb52052101c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:51:20 +0100 Subject: [PATCH 131/383] Update Return page (#430) - New graph - General edit --- modules/ROOT/images/graph_return_clause.svg | 48 +----- modules/ROOT/pages/clauses/return.adoc | 165 ++++++++++---------- 2 files changed, 88 insertions(+), 125 deletions(-) diff --git a/modules/ROOT/images/graph_return_clause.svg b/modules/ROOT/images/graph_return_clause.svg index 0607c9c1c..8547a94d1 100644 --- a/modules/ROOT/images/graph_return_clause.svg +++ b/modules/ROOT/images/graph_return_clause.svg @@ -1,41 +1,9 @@ - - - - - - -L - - - -N0 - -name = 'A' -age = 55 -happy = 'Yes!' - - - -N1 - -name = 'B' - - - -N0->N1 - - -BLOCKS - - - -N0->N1 - - -KNOWS - - + + + + + + + + diff --git a/modules/ROOT/pages/clauses/return.adoc b/modules/ROOT/pages/clauses/return.adoc index f295bc96d..076391179 100644 --- a/modules/ROOT/pages/clauses/return.adoc +++ b/modules/ROOT/pages/clauses/return.adoc @@ -3,53 +3,46 @@ [[query-return]] = RETURN -[abstract] --- -The `RETURN` clause defines what to include in the query result set. --- +[[return-introduction]] +== Introduction +The `RETURN` clause defines the parts of a pattern (nodes, relationships, and/or properties) to be included in the query result. -In the `RETURN` part of your query, you define which parts of the pattern you are interested in. -It can be nodes, relationships, or properties on these. +[[return-example-graph]] +== Example graph -[TIP] -==== -If what you actually want is the value of a property, make sure to not return the full node/relationship. -This will improve performance. -==== +The following graph is used for the examples below: -image:graph_return_clause.svg[] +image::graph_return_clause.svg[width="600",role="middle"] + +To recreate the graph, run the following query against an empty Neo4j database. -//// [source, cypher, role=test-setup] ---- CREATE - (a {name: 'A', happy: 'Yes!', age: 55}), - (b {name: 'B'}), - (a)-[:KNOWS]->(b), - (a)-[:BLOCKS]->(b) + (keanu:Person {name: 'Keanu Reeves', bornIn: 'Beirut', nationality: 'Canadian'}), + (taiChi:Movie {title: 'Man of Tai Chi', released: 2013}), + (keanu)-[:ACTED_IN]->(taiChi), + (keanu)-[:DIRECTED]->(taiChi) ---- -//// [[return-nodes]] == Return nodes -To return a node, list it in the `RETURN` statement. +To return a node, list it in the `RETURN` clause: .Query -[source, cypher, indent=0] +[source, cypher] ---- -MATCH (n {name: 'B'}) -RETURN n +MATCH (p:Person {name: 'Keanu Reeves'}) +RETURN p ---- -The example will return the node. - .Result [role="queryresult",options="header,footer",cols="1*(c) -RETURN r +MATCH (p:Person {name: 'Keanu Reeves'})-[r:ACTED_IN]->(m) +RETURN type(r) ---- -The relationship is returned by the example. - .Result [role="queryresult",options="header,footer",cols="1*(b) +MATCH p = (keanu:Person {name: 'Keanu Reeves'})-[r]->(m) RETURN * ---- -This returns the two nodes, the relationship and the path used in the query. +This returns the two nodes, and the two possible paths between them. .Result [role="queryresult",options="header,footer",cols="4*(1)+ | +:BLOCKS[1]{}+ -| +Node[0]{name:"A",age:55,happy:"Yes!"}+ | +Node[1]{name:"B"}+ | +(0)-[KNOWS,0]->(1)+ | +:KNOWS[0]{}+ -4+d|Rows: 2 +| +keanu+ | +m+ | +p+ | +r+ +| +{"bornIn":"Beirut","nationality":"Canadian","name":"Keanu Reeves"}+ | +{"title":"Man of Tai Chi","released":2013}+ | +[{"bornIn":"Beirut","nationality":"Canadian","name":"Keanu Reeves"},{},{"title":"Man of Tai Chi","released":2013}]+ | +{:ACTED_IN}+ +| +{"bornIn":"Beirut","nationality":"Canadian","name":"Keanu Reeves"}+ | +{"title":"Man of Tai Chi","released":2013}+ | +[{"bornIn":"Beirut","nationality":"Canadian","name":"Keanu Reeves"},{},{"title":"Man of Tai Chi","released":2013}]+ | +{:DIRECTED}+ +4+d|Rows: 1 |=== [[return-variable-with-uncommon-characters]] == Variable with uncommon characters -To introduce a placeholder that is made up of characters that are not contained in the English alphabet, you can use the ``` to enclose the variable, like this: +To introduce a variable made up of characters not contained in the English alphabet, use ``` to enclose the variable: .Query -[source, cypher, indent=0] +[source, cypher] ---- -MATCH (`This isn\'t a common variable`) -WHERE `This isn\'t a common variable`.name = 'A' -RETURN `This isn\'t a common variable`.happy +MATCH (`/uncommon variable\`) +WHERE `/uncommon variable\`.name = 'Keanu Reeves' +RETURN `/uncommon variable\`.bornIn ---- -The node with name "A" is returned. +The `bornIn` property of the node with the `name` property set to `'Keanu Reeves'` is returned: .Result [role="queryresult",options="header,footer",cols="1*. +Names of returned columns can be renamed using the `AS` operator: .Query -[source, cypher, indent=0] +[source, cypher] ---- -MATCH (a {name: 'A'}) -RETURN a.age AS SomethingTotallyDifferent +MATCH (p:Person {name: 'Keanu Reeves'}) +RETURN p.nationality AS citizenship ---- -Returns the age property of a node, but renames the column. +Returns the `nationality` property of `'Keanu Reeves'`, but the column is renamed to `citizenship`. .Result [role="queryresult",options="header,footer",cols="1*+ 1+d|Rows: 2 |=== @@ -199,22 +194,22 @@ This example returns the age when the node has that property, or `null` if the p [[return-other-expressions]] == Other expressions -Any expression can be used as a return item -- literals, predicates, properties, functions, and everything else. +Any expression can be used as a return item -- literals, predicates, properties, functions, and so on. .Query -[source, cypher, indent=0] +[source, cypher] ---- -MATCH (a {name: 'A'}) -RETURN a.age > 30, "I'm a literal", [p=(a)-->() | p] AS `(a)-->()` +MATCH (m:Movie {title: 'Man of Tai Chi'}) +RETURN m.released < 2012, "I'm a literal",[p=(m)--() | p] AS `(m)--()` ---- -Returns a predicate, a literal and function call with a pattern expression parameter. +Returns a predicate, a literal and function call with a pattern expression parameter: .Result [role="queryresult",options="header,footer",cols="3* 30+ | +"I'm a literal"+ | +(a)-->()+ -| +true+ | +"I'm a literal"+ | +[[{"happy":"Yes!","name":"A","age":55},{},{"name":"B"}],[{"happy":"Yes+!","name":"A","age":55},{},{"name":"B"}]]+ +| +m.released < 2012+ | +"I'm a literal"+ | +(m)--()+ +| +false+ | +"I'm a literal"+ | +[[{"title":"Man of Tai Chi","released":2013},{},{"bornIn":"Beirut","nationality":"Canadian","name":"Keanu Reeves"}],[{"title":"Man of Tai Chi","released":2013},{},{"bornIn":"Beirut","nationality":"Canadian","name":"Keanu Reeves"}]]+ 3+d|Rows: 1 |=== @@ -222,22 +217,22 @@ Returns a predicate, a literal and function call with a pattern expression param [[return-unique-results]] == Unique results -`DISTINCT` retrieves only unique rows depending on the columns that have been selected to output. +`DISTINCT` retrieves only unique rows for the columns that have been selected for output. .Query -[source, cypher, indent=0] +[source, cypher] ---- -MATCH (a {name: 'A'})-->(b) -RETURN DISTINCT b +MATCH (p:Person {name: 'Keanu Reeves'})-->(m) +RETURN DISTINCT m ---- -The node named "B" is returned by the query, but only once. +The `Movie` node `'Man of Tai Chi'` is returned by the query, but only once (without the `DISTINCT` operator it would have been returned twice because there are two relationships going to it from `'Keanu Reeves'`): .Result [role="queryresult",options="header,footer",cols="1* Date: Wed, 22 Feb 2023 11:00:23 +0100 Subject: [PATCH 132/383] Update Merge page (#428) - New graph - Remove ToC before introduction (too large, and redundant given pre-existing ToC on the right-hand side of the page) - General edit --------- JPryce-Aklundh --- modules/ROOT/images/graph_merge_clause.svg | 136 +------------------ modules/ROOT/pages/clauses/merge.adoc | 149 ++++++++------------- 2 files changed, 58 insertions(+), 227 deletions(-) diff --git a/modules/ROOT/images/graph_merge_clause.svg b/modules/ROOT/images/graph_merge_clause.svg index 2de22114c..ac3e96690 100644 --- a/modules/ROOT/images/graph_merge_clause.svg +++ b/modules/ROOT/images/graph_merge_clause.svg @@ -1,135 +1 @@ - - - - - - -L - - - -N0 - -Person - -bornIn = 'New York' -chauffeurName = 'John Brown' -name = 'Charlie Sheen' - - - -N1 - -Person - -bornIn = 'Ohio' -chauffeurName = 'Bob Brown' -name = 'Martin Sheen' - - - -N0->N1 - - -FATHER - - - -N5 - -Movie - -title = 'Wall Street' - - - -N0->N5 - - -ACTED_IN - - - -N1->N5 - - -ACTED_IN - - - -N6 - -Movie - -title = 'The American President' - - - -N1->N6 - - -ACTED_IN - - - -N2 - -Person - -name = 'Michael Douglas' -chauffeurName = 'John Brown' -bornIn = 'New Jersey' - - - -N2->N5 - - -ACTED_IN - - - -N2->N6 - - -ACTED_IN - - - -N3 - -Person - -bornIn = 'New York' -chauffeurName = 'Bill White' -name = 'Oliver Stone' - - - -N3->N5 - - -ACTED_IN - - - -N4 - -Person - -bornIn = 'New York' -chauffeurName = 'Ted Green' -name = 'Rob Reiner' - - - -N4->N6 - - -ACTED_IN - - - +ACTED_INACTED_INACTED_INDIRECTEDDIRECTEDACTED_INACTED_INPersonname:'Charlie Sheen'bornIn:'New York'chauffeurName:'John Brown'Personname:'Martin Sheen'bornIn:'Ohio'chauffeurName:'Bob Brown'Movietitle:'Wall Street'Personname:'Michael Douglas'bornIn:'New Jersey'chauffeurName:'John Brown'Personname:'Oliver Stone'bornIn:'New York'chauffeurName:'Bill White'Personname:'Rob Reiner'bornIn:'New York'chauffeurName:'Ted Green'Movietitle:'The American President' \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index e6805ac5a..bc0d2ff14 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -3,44 +3,14 @@ [[query-merge]] = MERGE -[abstract] --- -The `MERGE` clause ensures that a pattern exists in the graph. -Either the pattern already exists, or it needs to be created. --- - -* xref::clauses/merge.adoc#query-merge-introduction[Introduction] -* xref::clauses/merge.adoc#query-merge-node-derived[Merge nodes] -** xref::clauses/merge.adoc#merge-merge-single-node-with-a-label[Merge single node with a label] -** xref::clauses/merge.adoc#merge-merge-single-node-with-properties[Merge single node with properties] -** xref::clauses/merge.adoc#merge-merge-single-node-specifying-both-label-and-property[Merge single node specifying both label and property] -** xref::clauses/merge.adoc#merge-merge-single-node-derived-from-an-existing-node-property[Merge single node derived from an existing node property] -* xref::clauses/merge.adoc#query-merge-on-create-on-match[Use `ON CREATE` and `ON MATCH`] -** xref::clauses/merge.adoc#merge-merge-with-on-create[Merge with `ON CREATE`] -** xref::clauses/merge.adoc#merge-merge-with-on-match[Merge with `ON MATCH`] -** xref::clauses/merge.adoc#merge-merge-with-on-create-and-on-match[Merge with `ON CREATE` and `ON MATCH`] -** xref::clauses/merge.adoc#merge-merge-with-on-match-setting-multiple-properties[Merge with `ON MATCH` setting multiple properties] -* xref::clauses/merge.adoc#query-merge-relationships[Merge relationships] -** xref::clauses/merge.adoc#merge-merge-on-a-relationship[Merge on a relationship] -** xref::clauses/merge.adoc#merge-merge-on-multiple-relationships[Merge on multiple relationships] -** xref::clauses/merge.adoc#merge-merge-on-an-undirected-relationship[Merge on an undirected relationship] -** xref::clauses/merge.adoc#merge-merge-on-a-relationship-between-two-existing-nodes[Merge on a relationship between two existing nodes] -** xref::clauses/merge.adoc#merge-merge-on-a-relationship-between-an-existing-node-and-a-merged-node-derived-from-a-node-property[Merge on a relationship between an existing node and a merged node derived from a node property] -* xref::clauses/merge.adoc#query-merge-using-unique-constraints[Using property uniqueness constraints with `MERGE`] -** xref::clauses/merge.adoc#merge-merge-using-unique-constraints-creates-a-new-node-if-no-node-is-found[Merge using property uniqueness constraints creates a new node if no node is found] -** xref::clauses/merge.adoc#merge-merge-using-unique-constraints-matches-an-existing-node[Merge using property uniqueness constraints matches an existing node] -** xref::clauses/merge.adoc#merge-merge-with-unique-constraints-and-partial-matches[Merge with property uniqueness constraints and partial matches] -** xref::clauses/merge.adoc#merge-merge-with-unique-constraints-and-conflicting-matches[Merge with property uniqueness constraints and conflicting matches] -* xref::clauses/merge.adoc#merge-using-map-parameters-with-merge[Using map parameters with `MERGE`] - [[query-merge-introduction]] == Introduction -`MERGE` either matches existing nodes and binds them, or it creates new data and binds that. -It's like a combination of `MATCH` and `CREATE` that additionally allows you to specify what happens if the data was matched or created. +The `MERGE` clause either matches existing node patterns in the graph and binds them or, if not present, creates new data and binds that. +In this way, it acts as a combination of `MATCH` and `CREATE` that allows for specific actions depending on whether the specified data was matched or created. -For example, you can specify that the graph must contain a node for a user with a certain name. -If there isn't a node with the correct name, a new node will be created and its name property set. +For example, `MERGE` can be used to specify that a graph must contain a node with a `Person` label and a specific `name` property. +If there isn't a node with the specific `name` property, a new node will be created with that `name` property. [NOTE] ==== @@ -49,27 +19,27 @@ See xref::indexes-for-search-performance.adoc[] for more information. ==== When using `MERGE` on full patterns, the behavior is that either the whole pattern matches, or the whole pattern is created. -`MERGE` will not partially use existing patterns -- it is all or nothing. -If partial matches are needed, this can be accomplished by splitting a pattern up into multiple `MERGE` clauses. +`MERGE` will not partially use existing patterns. +If partial matches are needed, this can be accomplished by splitting a pattern into multiple `MERGE` clauses. -[IMPORTANT] +[NOTE] ==== -Under concurrent updates, `MERGE` only guarantees existence of the `MERGE` pattern, but not uniqueness. +Under concurrent updates, `MERGE` only guarantees the existence of the `MERGE` pattern, but not uniqueness. To guarantee uniqueness of nodes with certain properties, a xref::constraints/index.adoc[property uniqueness constraint] should be used. -See xref::clauses/merge.adoc#query-merge-using-unique-constraints[Using property uniqueness constraints with `MERGE`] to see how `MERGE` can be used in combination with a property uniqueness constraint. +See xref::clauses/merge.adoc#query-merge-using-unique-constraints[Using property uniqueness constraints with `MERGE`]. ==== -As with `MATCH`, `MERGE` can match multiple occurrences of a pattern. +Similar to `MATCH`, `MERGE` can match multiple occurrences of a pattern. If there are multiple matches, they will all be passed on to later stages of the query. -The last part of `MERGE` is the `ON CREATE` and `ON MATCH`. -These allow a query to express additional changes to the properties of a node or relationship, depending on if the element was matched (`MATCH`) in the database or if it was created (`CREATE`). +The last part of a `MERGE` clause is the `ON CREATE` and/or `ON MATCH` operators. +These allow a query to express additional changes to the properties of a node or relationship, depending on whether the element was matched (`MATCH`) in the database or if it was created (`CREATE`). == Example graph The following graph is used for the examples below: -image:graph_merge_clause.svg[] +image::graph_merge_clause.svg[] To recreate the graph, run the following query in an empty Neo4j database: @@ -88,9 +58,8 @@ CREATE (michael)-[:ACTED_IN]->(wallStreet), (martin)-[:ACTED_IN]->(theAmericanPresident), (michael)-[:ACTED_IN]->(theAmericanPresident), - (oliver)-[:ACTED_IN]->(wallStreet), - (rob)-[:ACTED_IN]->(theAmericanPresident), - (charlie)-[:FATHER]->(martin) + (oliver)-[:DIRECTED]->(wallStreet), + (rob)-[:DIRECTED]->(theAmericanPresident) ---- [[query-merge-node-derived]] @@ -99,7 +68,7 @@ CREATE [[merge-merge-single-node-with-a-label]] === Merge single node with a label -Merging a single node with the given label. +Merge a node with a specific label: .Query [source, cypher] @@ -108,7 +77,7 @@ MERGE (robert:Critic) RETURN robert, labels(robert) ---- -A new node is created because there are no nodes labeled `Critic` in the database. +A new node is created because there are no nodes labeled `Critic` in the database: .Result [role="queryresult",options="header,footer",cols="2*(movie:Movie)<-[:ACTED_IN]-(reiner) +MERGE (oliver)-[:DIRECTED]->(movie:Movie)<-[:DIRECTED]-(reiner) RETURN movie ---- -In our example graph, `'Oliver Stone'` and `'Rob Reiner'` have never worked together. -When we try to `MERGE` a "movie between them, Neo4j will not use any of the existing movies already connected to either person. -Instead, a new `'movie'` node is created. +In the example graph, `'Oliver Stone'` and `'Rob Reiner'` have never worked together. +When trying to `MERGE` a `Movie` node between them, Neo4j will not use any of the existing `Movie` nodes already connected to either person. +Instead, a new `Movie` node is created. .Result [role="queryresult",options="header,footer",cols="1*(city) -RETURN person.name, person.bornIn, city +MERGE (location:Location {name: person.bornIn}) +MERGE (person)-[r:BORN_IN]->(location) +RETURN person.name, person.bornIn, location ---- This builds on the example from xref::clauses/merge.adoc#merge-merge-single-node-derived-from-an-existing-node-property[Merge single node derived from an existing node property]. -The second `MERGE` creates a `BORN_IN` relationship between each person and a city corresponding to the value of the person’s `bornIn` property. -`'Charlie Sheen'`, `'Rob Reiner'` and `'Oliver Stone'` all have a `BORN_IN` relationship to the _same_ `City` node (`'New York'`). +The second `MERGE` creates a `BORN_IN` relationship between each person and a location corresponding to the value of the person’s `bornIn` property. +`'Charlie Sheen'`, `'Rob Reiner',` and `'Oliver Stone'` all have a `BORN_IN` relationship to the _same_ `Location` node (`'New York'`). .Result [role="queryresult",options="header,footer",cols="3*(chauffeur:Chauffeur {name: person.chauffeurNa RETURN person.name, person.chauffeurName, chauffeur ---- -As `MERGE` found no matches -- in our example graph, there are no nodes labeled with `Chauffeur` and no `HAS_CHAUFFEUR` relationships -- `MERGE` creates five nodes labeled with `Chauffeur`, each of which contains a `name` property whose value corresponds to each matched `Person` node's `chauffeurName` property value. +As `MERGE` found no matches -- in the example graph, there are no nodes labeled with `Chauffeur` and no `HAS_CHAUFFEUR` relationships -- `MERGE` creates six nodes labeled with `Chauffeur`, each of which contains a `name` property whose value corresponds to each matched `Person` node's `chauffeurName` property value. `MERGE` also creates a `HAS_CHAUFFEUR` relationship between each `Person` node and the newly-created corresponding `Chauffeur` node. As `'Charlie Sheen'` and `'Michael Douglas'` both have a chauffeur with the same name -- `'John Brown'` -- a new node is created in each case, resulting in _two_ `Chauffeur` nodes having a `name` of `'John Brown'`, correctly denoting the fact that even though the `name` property may be identical, these are two separate people. -This is in contrast to the example shown above in xref::clauses/merge.adoc#merge-merge-on-a-relationship-between-two-existing-nodes[Merge on a relationship between two existing nodes], where we used the first `MERGE` to bind the `City` nodes to prevent them from being recreated (and thus duplicated) in the second `MERGE`. +This is in contrast to the example shown above in xref::clauses/merge.adoc#merge-merge-on-a-relationship-between-two-existing-nodes[Merge on a relationship between two existing nodes], where the first `MERGE` was used to bind the `Location` nodes and to prevent them from being recreated (and thus duplicated) on the second `MERGE`. .Result [role="queryresult",options="header,footer",cols="3* Date: Wed, 22 Feb 2023 11:20:36 +0100 Subject: [PATCH 133/383] Make `Delete` operator example query plan more deterministic (#434) The example for the `Delete` operator currently has a query plan that is not guaranteed to be the same every time, even with the same indexes. Given the presence of multiple indexes that can be used for the query, the planner will try to figure out which gives best performance, but with so little data in the database all plans perform roughly the same, **so the decision over which plan is chosen is arbitrary**. The result is that we sometimes get `NodeIndexSeek` as first operator, sometimes `DirectedRelationshipIndexSeek` (which leverages the index on `WORKS_IN.duration`). This PR tweaks the example so that the ambiguity is removed, and only one query plan can come up. All in all, it's also an improvement, since: 1. it still showcases the `Delete` operator, which is the whole point, but shortens the list of operators and makes `Delete` more immediately visible 2. it makes the example more uniform with respect to the following one using `DETACH DELETE`, by using a more similar query. --- .../ROOT/pages/execution-plans/operators.adoc | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index d1fd46c2c..f3ffb715e 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -4691,8 +4691,8 @@ The `Delete` operator is used to delete a node or a relationship. [source, cypher] ---- PROFILE -MATCH (me:Person {name: 'me'})-[w:WORKS_IN {duration: 190}]->(london:Location {name: 'London'}) -DELETE w +MATCH (me:Person {name: 'me'}) +DELETE me ---- .Query Plan @@ -4706,23 +4706,17 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+-----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | | 0 | 0 | 0 | | | | | -| | +-----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | | 0 | 0 | 0 | | | | | -| | +-----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Delete | w | 0 | 1 | 1 | | | | | -| | +-----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Eager | read/delete conflict for variable: w | 0 | 1 | 0 | 112 | 1/0 | 0.247 | Fused in Pipeline 1 | -| | +-----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | london.name = $autostring_2 AND w.duration = $autoint_1 AND london:Location | 0 | 1 | 5 | | | | | -| | +-----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (me)-[w:WORKS_IN]->(london) | 1 | 1 | 5 | | | | | -| | +-----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX me:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 0.447 | Fused in Pipeline 0 | -+-----------------+-----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 0 | 0 | 0 | | | | | +| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 0 | 0 | 0 | | | | | +| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Delete | 2 | me | 0 | 0 | 0 | | | | | +| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | 3 | RANGE INDEX me:Person(name) WHERE name = $autostring_0 | 0 | 0 | 1 | 120 | 1/0 | 0.330 | Fused in Pipeline 0 | ++-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 13, total allocated memory: 216 ---- From 7302a153534943ae436fa5afb1b4b1564712adc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 22 Feb 2023 14:45:25 +0100 Subject: [PATCH 134/383] Update Optional Match page (#429) - new graph - section explaining difference between match and optional match - general edit --------- --- .../images/graph_optional_match_clause.svg | 126 +------------- .../ROOT/pages/clauses/optional-match.adoc | 162 ++++++++++++++---- 2 files changed, 134 insertions(+), 154 deletions(-) diff --git a/modules/ROOT/images/graph_optional_match_clause.svg b/modules/ROOT/images/graph_optional_match_clause.svg index dfb87f679..d5373d409 100644 --- a/modules/ROOT/images/graph_optional_match_clause.svg +++ b/modules/ROOT/images/graph_optional_match_clause.svg @@ -1,125 +1 @@ - - - - - - -L - - - -N0 - -Person - -name = 'Charlie Sheen' - - - -N1 - -Person - -name = 'Martin Sheen' - - - -N0->N1 - - -FATHER - - - -N5 - -Movie - -title = 'Wall Street' - - - -N0->N5 - - -ACTED_IN - - - -N1->N5 - - -ACTED_IN - - - -N6 - -Movie - -title = 'The American President' - - - -N1->N6 - - -ACTED_IN - - - -N2 - -Person - -name = 'Michael Douglas' - - - -N2->N5 - - -ACTED_IN - - - -N2->N6 - - -ACTED_IN - - - -N3 - -Person - -name = 'Oliver Stone' - - - -N3->N5 - - -DIRECTED - - - -N4 - -Person - -name = 'Rob Reiner' - - - -N4->N6 - - -DIRECTED - - - +DIRECTEDACTED_INACTED_INACTED_INACTED_INACTED_INDIRECTEDFATHER_OFPersonname:'Oliver Stone'Movietitle:'Wall Street'Personname:'Michael Douglas'Personname:'Martin Sheen'Movietitle:'The American President'Personname:'Charlie Sheen'Personname:'Rob Reiner' \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/optional-match.adoc b/modules/ROOT/pages/clauses/optional-match.adoc index 56f9a18d2..4c6f26ad9 100644 --- a/modules/ROOT/pages/clauses/optional-match.adoc +++ b/modules/ROOT/pages/clauses/optional-match.adoc @@ -3,29 +3,30 @@ [[query-optional-match]] = OPTIONAL MATCH -[abstract] --- -The `OPTIONAL MATCH` clause is used to search for the pattern described in it, while using nulls for missing parts of the pattern. --- +== Introduction -`OPTIONAL MATCH` matches patterns against your graph database, just like `MATCH` does. -The difference is that if no matches are found, `OPTIONAL MATCH` will use a `null` for missing parts of the pattern. -`OPTIONAL MATCH` could be considered the Cypher equivalent of the outer join in SQL. +`OPTIONAL MATCH` matches patterns against a graph database, just as `MATCH` does. +The difference is that if no matches are found, `OPTIONAL MATCH` will use a `null` for missing parts of the pattern. +`OPTIONAL MATCH` could therefore be considered the Cypher equivalent of the outer join in SQL. -Either the whole pattern is matched, or nothing is matched. -Remember that `WHERE` is part of the pattern description, and the predicates will be considered while looking for matches, not after. +When using `OPTIONAL MATCH`, either the whole pattern is matched, or nothing is matched. +The `WHERE` clause is part of the pattern description, and its predicates will be considered while looking for matches, not after. This matters especially in the case of multiple (`OPTIONAL`) `MATCH` clauses, where it is crucial to put `WHERE` together with the `MATCH` it belongs to. + [TIP] ==== To understand the patterns used in the `OPTIONAL MATCH` clause, read xref::syntax/patterns.adoc[Patterns]. ==== +== Example graph + The following graph is used for the examples below: -image:graph_optional_match_clause.svg[] +image::graph_optional_match_clause.svg[width="600",role="middle"] + +To recreate the graph, run the following query in an empty Neo4j database: -//// [source, cypher, role=test-setup] ---- CREATE @@ -43,27 +44,75 @@ CREATE (martin)-[:ACTED_IN]->(thePresident), (michael)-[:ACTED_IN]->(thePresident), (rob)-[:DIRECTED]->(thePresident), - (charlie)-[:FATHER]->(martin) + (martin)-[:FATHER_OF]->(charlie) +---- + +== OPTIONAL MATCH in more detail + +Like SQL, Cypher queries are constructed using various clauses which are chained together to feed intermediate results between each other. +For example, the matching variables from one `MATCH` clause will provide the context in which the next clause exists. +However, there are two important differences between Neo4j and SQL which helps to explain `OPTIONAL MATCH` further. + +. While it is both possible and advised to enforce partial schemas using indexes and constraints, Neo4j offers a greater degree of schema flexibility than a relational database. +Nodes and relationships in a Neo4j database do not have to have a specific property set to them because other nodes or relationships in the same graph have that property (unless there is a existence constraint created on the specific property). + +. Queries in Cypher are run as pipelines. +If a clause returns no results, it will effectively end the query as subsequent clauses will have no data to execute upon. + +For example, the following query returns no results: + +[source, cypher] +---- +MATCH (a:Person {name: 'Martin Sheen'}) +MATCH (a)-[r:DIRECTED]->() +RETURN a.name, r +---- + + +[source, result] +---- +(no changes, no records) +---- + +This is because the second `MATCH` clause returns no data (there are no `DIRECTED` relationships connected to `Martin Sheen` in the graph) to pass on to the `RETURN` clause. + +However, replacing the second `MATCH` clause with `OPTIONAL MATCH` does return results. +This is because, unlike `MATCH`, `OPTIONAL MATCH` enables the value `null` to be passed between clauses. + +[source, cypher] +---- +MATCH (p:Person {name: 'Martin Sheen'}) +OPTIONAL MATCH (p)-[r:DIRECTED]->() +RETURN p.name, r ---- -//// + +.Result +[role="queryresult",options="header,footer",cols="2*+ +2+d|Rows: 1 +|=== + +`OPTIONAL MATCH` can therefore be used to check graphs for missing as well as existing values, and to pass on rows without any data to subsequent clauses in a query. [[optional-relationships]] == Optional relationships -If a relationship is optional, use the `OPTIONAL MATCH` clause. -This is similar to how a SQL outer join works. -If the relationship is there, it is returned. -If it is not, `null` is returned in its place. +If the existence of a relationship is optional, use the `OPTIONAL MATCH` clause. +If the relationship exists, it is returned. +If it does not, `null` is returned in its place. -.Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:Movie {title: 'Wall Street'}) OPTIONAL MATCH (a)-->(x) RETURN x ---- -Returns `null`, since the node has no outgoing relationships. +Returns `null`, since the `Movie` node `Wall Street` has no outgoing relationships. .Result [role="queryresult",options="header,footer",cols="1*(x) +RETURN x +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(x) RETURN x, x.name ---- -Returns the element x (`null` in this query), and `null` as its name. +Returns the element `x` (`null` in this query), and `null` for its `name` property, because the `Movie` node `Wall Street` has no outgoing relationships. .Result [role="queryresult",options="header,footer",cols="2*(x) +RETURN x, x.name +---- + +.Result +[role="queryresult",options="header,footer",cols="2*+ +| +{"name":"Charlie Sheen"}+ | +"Charlie Sheen"+ +| +{"title":"The American President"}+ | ++ +2+d|Rows: 3 +|=== + [[optional-typed-named-relationship]] == Optional typed and named relationship -Just as with a normal relationship, you can decide which variable it goes into, and what relationship type you need. +It is also possible to look for specific relationship types when using `OPTIONAL MATCH`: -.Query -[source, cypher, indent=0] +[source, cypher] ---- MATCH (a:Movie {title: 'Wall Street'}) -OPTIONAL MATCH (a)-[r:ACTS_IN]->() +OPTIONAL MATCH (a)-[r:ACTED_IN]->() RETURN a.title, r ---- -This returns the title of the node, *'Wall Street'*, and, since the node has no outgoing `ACTS_IN` relationships, `null` is returned for the relationship denoted by `r`. +This returns the title of the `Movie` node `Wall Street`, and since this node has no outgoing `ACTED_IN` relationships, `null` is returned for the relationship denoted by the variable `r`. .Result [role="queryresult",options="header,footer",cols="2*(a) +RETURN a.title, x.name, type(r) +---- + +.Result +[role="queryresult",options="header,footer",cols="3* Date: Wed, 22 Feb 2023 16:30:26 +0100 Subject: [PATCH 135/383] New Aura and Cypher page (#435) New page in the introduction of the Cypher Manual intended for Aura users. The purpose of the page is to: - briefly explain that not all features of Cypher are available on Aura. - some features of Cypher are only available when using AuraDB Enterprise. - Introduce two new labels which will be used to clarify to Aura users throughout the Manual which features of Cypher they may not be able to use. - Link to Aura Cheat Sheet (when available) --------- --- modules/ROOT/content-nav.adoc | 1 + modules/ROOT/pages/introduction/aura.adoc | 52 +++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 modules/ROOT/pages/introduction/aura.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index e3a260fbd..9e920a6cf 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -6,6 +6,7 @@ ** xref:introduction/transactions.adoc[] ** xref:introduction/uniqueness.adoc[] ** xref:introduction/clause_composition.adoc[] +** xref:introduction/aura.adoc[] * xref:syntax/index.adoc[] ** xref:syntax/values.adoc[] diff --git a/modules/ROOT/pages/introduction/aura.adoc b/modules/ROOT/pages/introduction/aura.adoc new file mode 100644 index 000000000..449df071e --- /dev/null +++ b/modules/ROOT/pages/introduction/aura.adoc @@ -0,0 +1,52 @@ += Aura and Cypher + +== Introduction + +Aura is Neo4j's fully managed cloud service. +It consists of AuraDB and AuraDS. +AuraDB is a graph database service for developers building intelligent applications, and AuraDS is a Graph Data Science (GDS) service for data scientists building predictive models and analytics workflows. + +AuraDB is available on the following tiers: + +* AuraDB Free +* AuraDB Pro +* AuraDB Enterprise + +For more information, see link:{neo4j-docs-base-uri}/aura/auradb[Aura docs - Neo4j AuraDB overview]. + +AuraDS is available on the following tiers: + +* AuraDS Pro +* AuraDS Enterprise + +For more information, see link:{neo4j-docs-base-uri}/aura/aurads[Aura docs - Neo4j AuraDS overview] + +== Using Cypher on Aura + +Most Cypher features are available on all tiers of Aura. +There are, however, some features which are not available to Aura instances. +For example, it is not possible to create, alter, or drop databases using Aura, nor is it possible to alter or drop servers. + +There are also certain Cypher features which are only available on AuraDB Enterprise instances. +These can be categorized as the role-based access-control features of Cypher. +For example, it is not possible to create, alter, or drop roles using AuraDB Free, AuraDB Pro, AuraDS Pro, or AuraDS Enterprise, but it is possible using AuraDB Enterprise. + +The Cypher Manual uses two different labels to differentiate this distinction: + +[options="header,cols=""2a,2a"] +|=== +| Label | Description +| label:not-on-aura[] | Cypher feature not available on any tier of Aura. +| label:aura-db-enterprise[] | Cypher feature only available on AuraDB Enterprise. +|=== + +//// +TODO: remove comment blocks once Aura Cheat Sheet has been published. + +== Aura and the Cypher Cheat Sheet + +Each different tier of Aura has a customized version of the Cypher Cheat Sheet which only shows the features of Cypher available for the chosen tier. + +The Aura Cheat Sheet can be accessed here: //Add url when available +Note that the default tier is AuraDB Enterprise. +//// From 69ffda70388d0547d935ac0fc8e63b0d40c20376 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 23 Feb 2023 09:40:33 +0000 Subject: [PATCH 136/383] Add edit this page links (#258) Co-authored-by: David Oliver --- publish.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/publish.yml b/publish.yml index fd8044346..dd6f55034 100644 --- a/publish.yml +++ b/publish.yml @@ -7,6 +7,7 @@ content: sources: - url: ./ branches: ['3.5', '4.0', '4.1', '4.2', '4.3', '4.4','dev'] + edit_url: https://github.com/neo4j/docs-cypher/tree/{refname}/{path} exclude: - '!**/_includes/*' - '!**/readme.adoc' @@ -43,8 +44,8 @@ asciidoc: page-search-site: Reference Docs page-canonical-root: /docs page-pagination: true - page-no-canonical: true - page-origin-private: true + page-no-canonical: true + page-origin-private: false page-hide-toc: false page-mixpanel: 4bfb2414ab973c741b6f067bf06d5575 includePDF: false From 37b937fa4d8676e4fc2fde9ec12f2e3b8027b04a Mon Sep 17 00:00:00 2001 From: David Oliver Date: Thu, 23 Feb 2023 13:23:02 +0000 Subject: [PATCH 137/383] Remove in-page TOCs (#441) --- modules/ROOT/pages/clauses/create.adoc | 15 ----------- modules/ROOT/pages/clauses/load-csv.adoc | 13 ---------- modules/ROOT/pages/clauses/match.adoc | 33 ------------------------ 3 files changed, 61 deletions(-) diff --git a/modules/ROOT/pages/clauses/create.adoc b/modules/ROOT/pages/clauses/create.adoc index f22224e6f..746ac2cf4 100644 --- a/modules/ROOT/pages/clauses/create.adoc +++ b/modules/ROOT/pages/clauses/create.adoc @@ -8,21 +8,6 @@ The `CREATE` clause is used to create nodes and relationships. -- -* xref::clauses/create.adoc#create-nodes[Create nodes] -** xref::clauses/create.adoc#create-create-single-node[Create single node] -** xref::clauses/create.adoc#create-create-multiple-nodes[Create multiple nodes] -** xref::clauses/create.adoc#create-create-a-node-with-a-label[Create a node with a label] -** xref::clauses/create.adoc#create-create-a-node-with-multiple-labels[Create a node with multiple labels] -** xref::clauses/create.adoc#create-create-node-and-add-labels-and-properties[Create node and add labels and properties] -** xref::clauses/create.adoc#create-return-created-node[Return created node] -* xref::clauses/create.adoc#create-relationships[Create relationships] -** xref::clauses/create.adoc#create-create-a-relationship-between-two-nodes[Create a relationship between two nodes] -** xref::clauses/create.adoc#create-create-a-relationship-and-set-properties[Create a relationship and set properties] -* xref::clauses/create.adoc#create-create-a-full-path[Create a full path] -* xref::clauses/create.adoc#use-parameters-with-create[Use parameters with `CREATE`] -** xref::clauses/create.adoc#create-create-node-with-a-parameter-for-the-properties[Create node with a parameter for the properties] -** xref::clauses/create.adoc#create-create-multiple-nodes-with-a-parameter-for-their-properties[Create multiple nodes with a parameter for their properties] - [TIP] ==== In the `CREATE` clause, patterns are used extensively. diff --git a/modules/ROOT/pages/clauses/load-csv.adoc b/modules/ROOT/pages/clauses/load-csv.adoc index 4716dda66..a571bd526 100644 --- a/modules/ROOT/pages/clauses/load-csv.adoc +++ b/modules/ROOT/pages/clauses/load-csv.adoc @@ -8,19 +8,6 @@ `LOAD CSV` is used to import data from CSV files. -- -* xref::clauses/load-csv.adoc#query-load-csv-introduction[Introduction] -* xref::clauses/load-csv.adoc#csv-file-format[CSV file format] -* xref::clauses/load-csv.adoc#load-csv-import-data-from-a-csv-file[Import data from a CSV file] -* xref::clauses/load-csv.adoc#load-csv-import-data-from-a-remote-csv-file[Import data from a remote CSV file] -* xref::clauses/load-csv.adoc#load-csv-import-data-from-a-csv-file-containing-headers[Import data from a CSV file containing headers] -* xref::clauses/load-csv.adoc#load-csv-import-data-from-a-csv-file-with-a-custom-field-delimiter[Import data from a CSV file with a custom field delimiter] -* xref::clauses/load-csv.adoc#load-csv-importing-large-amounts-of-data[Importing large amounts of data] -* xref::clauses/load-csv.adoc#load-csv-setting-the-rate-of-call-in-transactions[Setting the rate of +CALL { ... } IN TRANSACTIONS+] -* xref::clauses/load-csv.adoc#load-csv-import-data-containing-escaped-characters[Import data containing escaped characters] -* xref::clauses/load-csv.adoc#load-csv-using-linenumber-with-load-csv[Using linenumber() with LOAD CSV] -* xref::clauses/load-csv.adoc#load-csv-using-file-with-load-csv[Using file() with LOAD CSV] - - [[query-load-csv-introduction]] == Introduction diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 031015903..5dc930f57 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -8,39 +8,6 @@ The `MATCH` clause is used to search for the pattern described in it. -- -* xref::clauses/match.adoc#match-introduction[Introduction] -* xref::clauses/match.adoc#basic-node-finding[Basic node finding] - ** xref::clauses/match.adoc#get-all-nodes[Get all nodes] - ** xref::clauses/match.adoc#get-all-nodes-with-label[Get all nodes with a label] - ** xref::clauses/match.adoc#related-nodes[Related nodes] - ** xref::clauses/match.adoc#match-with-labels[Match with labels] - ** xref::clauses/match.adoc#label-expression-match-or-expression[Match with a label expression for the node labels] -* xref::clauses/match.adoc#relationship-basics[Relationship basics] - ** xref::clauses/match.adoc#outgoing-relationships[Outgoing relationships] - ** xref::clauses/match.adoc#directed-rels-and-variable[Directed relationships and variable] - ** xref::clauses/match.adoc#match-on-rel-type[Match on relationship type] - ** xref::clauses/match.adoc#match-on-multiple-rel-types[Match on multiple relationship types] - ** xref::clauses/match.adoc#match-on-rel-type-use-variable[Match on relationship type and use a variable] -* xref::clauses/match.adoc#relationships-in-depth[Relationships in depth] - ** xref::clauses/match.adoc#rel-types-with-uncommon-chars[Relationship types with uncommon characters] - ** xref::clauses/match.adoc#multiple-rels[Multiple relationships] - ** xref::clauses/match.adoc#varlength-rels[Variable length relationships] - ** xref::clauses/match.adoc#varlength-rels-multiple-types[Variable length relationships with multiple relationship types] - ** xref::clauses/match.adoc#rel-variable-in-varlength-rels[Relationship variable in variable length relationships] - ** xref::clauses/match.adoc#match-props-on-varlength-path[Match with properties on a variable length path] - ** xref::clauses/match.adoc#zero-length-paths[Zero length paths] - ** xref::clauses/match.adoc#named-paths[Named paths] - ** xref::clauses/match.adoc#match-on-bound-rel[Matching on a bound relationship] -* xref::clauses/match.adoc#query-shortest-path[Shortest path] - ** xref::clauses/match.adoc#single-shortest-path[Single shortest path] - ** xref::clauses/match.adoc#single-shortest-path-with-predicates[Single shortest path with predicates] - ** xref::clauses/match.adoc#all-shortest-paths[All shortest paths] -* xref::clauses/match.adoc#get-node-rel-by-id[Get node or relationship by elementId] - ** xref::clauses/match.adoc#match-node-by-id[Node by elementId] - ** xref::clauses/match.adoc#match-rel-by-id[Relationship by elementId] - ** xref::clauses/match.adoc#match-multiple-nodes-by-id[Multiple nodes by elementId] - - [[match-introduction]] == Introduction From deeb0b025e3f12596c55d2bfc328e4daea4323a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 23 Feb 2023 15:51:44 +0100 Subject: [PATCH 138/383] Remove in-page ToC (#443) There is no need for an in-page ToC when there is also a generated ToC. --- modules/ROOT/pages/functions/aggregating.adoc | 15 -------- modules/ROOT/pages/functions/list.adoc | 15 -------- modules/ROOT/pages/functions/load-csv.adoc | 5 --- .../functions/mathematical-logarithmic.adoc | 9 ----- .../pages/functions/mathematical-numeric.adoc | 12 ------ .../functions/mathematical-trigonometric.adoc | 17 --------- modules/ROOT/pages/functions/predicate.adoc | 9 ----- modules/ROOT/pages/functions/scalar.adoc | 25 ------------ modules/ROOT/pages/functions/spatial.adoc | 9 ----- modules/ROOT/pages/functions/string.adoc | 17 --------- modules/ROOT/pages/syntax/operators.adoc | 38 ------------------- 11 files changed, 171 deletions(-) diff --git a/modules/ROOT/pages/functions/aggregating.adoc b/modules/ROOT/pages/functions/aggregating.adoc index ddee351fe..2be36cf47 100644 --- a/modules/ROOT/pages/functions/aggregating.adoc +++ b/modules/ROOT/pages/functions/aggregating.adoc @@ -8,21 +8,6 @@ Aggregating functions take a set of values and calculate an aggregated value over them. -- -Functions: - -* xref::functions/aggregating.adoc#functions-avg[avg() - Numeric values] -* xref::functions/aggregating.adoc#functions-avg-duration[avg() - Durations] -* xref::functions/aggregating.adoc#functions-collect[collect()] -* xref::functions/aggregating.adoc#functions-count[count()] -* xref::functions/aggregating.adoc#functions-max[max()] -* xref::functions/aggregating.adoc#functions-min[min()] -* xref::functions/aggregating.adoc#functions-percentilecont[percentileCont()] -* xref::functions/aggregating.adoc#functions-percentiledisc[percentileDisc()] -* xref::functions/aggregating.adoc#functions-stdev[stDev()] -* xref::functions/aggregating.adoc#functions-stdevp[stDevP()] -* xref::functions/aggregating.adoc#functions-sum[sum() - Numeric values] -* xref::functions/aggregating.adoc#functions-sum-duration[sum() - Durations] - Aggregation can be computed over all the matching paths, or it can be further divided by introducing grouping keys. Grouping keys are non-aggregate expressions, that are used to group the values going into the aggregate functions. diff --git a/modules/ROOT/pages/functions/list.adoc b/modules/ROOT/pages/functions/list.adoc index b9b59741b..2e10087b8 100644 --- a/modules/ROOT/pages/functions/list.adoc +++ b/modules/ROOT/pages/functions/list.adoc @@ -10,21 +10,6 @@ List functions return lists of things -- nodes in a path, and so on. Further details and examples of lists may be found in xref::syntax/lists.adoc[Lists] and xref::syntax/operators.adoc#query-operators-list[List operators]. -Functions: - -* xref::functions/list.adoc#functions-keys[keys()] -* xref::functions/list.adoc#functions-labels[labels()] -* xref::functions/list.adoc#functions-nodes[nodes()] -* xref::functions/list.adoc#functions-range[range()] -* xref::functions/list.adoc#functions-reduce[reduce()] -* xref::functions/list.adoc#functions-relationships[relationships()] -* xref::functions/list.adoc#functions-reverse-list[reverse()] -* xref::functions/list.adoc#functions-tail[tail()] -* xref::functions/list.adoc#functions-tobooleanlist[toBooleanList()] -* xref::functions/list.adoc#functions-tofloatlist[toFloatList()] -* xref::functions/list.adoc#functions-tointegerlist[toIntegerList()] -* xref::functions/list.adoc#functions-tostringlist[toStringList()] - == Example graph The following graph is used for the examples below: diff --git a/modules/ROOT/pages/functions/load-csv.adoc b/modules/ROOT/pages/functions/load-csv.adoc index c177ff67a..7626d5690 100644 --- a/modules/ROOT/pages/functions/load-csv.adoc +++ b/modules/ROOT/pages/functions/load-csv.adoc @@ -14,11 +14,6 @@ The functions described on this page are only useful when run on a query that us In all other contexts they will always return `null`. ==== -Functions: - -* xref::functions/load-csv.adoc#functions-linenumber[linenumber()] -* xref::functions/load-csv.adoc#functions-file[file()] - [[functions-linenumber]] == linenumber() diff --git a/modules/ROOT/pages/functions/mathematical-logarithmic.adoc b/modules/ROOT/pages/functions/mathematical-logarithmic.adoc index f40fedcd2..5bf428593 100644 --- a/modules/ROOT/pages/functions/mathematical-logarithmic.adoc +++ b/modules/ROOT/pages/functions/mathematical-logarithmic.adoc @@ -8,15 +8,6 @@ These functions all operate on numeric expressions only, and will return an error if used on any other values. See also xref::syntax/operators.adoc#query-operators-mathematical[Mathematical operators]. -- -Functions: - -* xref::functions/mathematical-logarithmic.adoc#functions-e[e()] -* xref::functions/mathematical-logarithmic.adoc#functions-exp[exp()] -* xref::functions/mathematical-logarithmic.adoc#functions-log[log()] -* xref::functions/mathematical-logarithmic.adoc#functions-log10[log10()] -* xref::functions/mathematical-logarithmic.adoc#functions-sqrt[sqrt()] - - [[functions-e]] == e() diff --git a/modules/ROOT/pages/functions/mathematical-numeric.adoc b/modules/ROOT/pages/functions/mathematical-numeric.adoc index a636474f0..f4818dc00 100644 --- a/modules/ROOT/pages/functions/mathematical-numeric.adoc +++ b/modules/ROOT/pages/functions/mathematical-numeric.adoc @@ -9,18 +9,6 @@ These functions all operate on numeric expressions only, and will return an erro See also xref::syntax/operators.adoc#query-operators-mathematical[Mathematical operators]. -- -Functions: - -* xref::functions/mathematical-numeric.adoc#functions-abs[abs()] -* xref::functions/mathematical-numeric.adoc#functions-ceil[ceil()] -* xref::functions/mathematical-numeric.adoc#functions-floor[floor()] -* xref::functions/mathematical-numeric.adoc#functions-isnan[isNaN()] -* xref::functions/mathematical-numeric.adoc#functions-rand[rand()] -* xref::functions/mathematical-numeric.adoc#functions-round[round()] -* xref::functions/mathematical-numeric.adoc#functions-round2[round(), with precision] -* xref::functions/mathematical-numeric.adoc#functions-round3[round(), with precision and rounding mode] -* xref::functions/mathematical-numeric.adoc#functions-sign[sign()] - The following graph is used for the examples below: image:graph_numeric_functions.svg[] diff --git a/modules/ROOT/pages/functions/mathematical-trigonometric.adoc b/modules/ROOT/pages/functions/mathematical-trigonometric.adoc index 7dda7f0e9..75c2c0800 100644 --- a/modules/ROOT/pages/functions/mathematical-trigonometric.adoc +++ b/modules/ROOT/pages/functions/mathematical-trigonometric.adoc @@ -8,23 +8,6 @@ These functions all operate on numeric expressions only, and will return an error if used on any other values. See also xref::syntax/operators.adoc#query-operators-mathematical[Mathematical operators]. -- -Functions: - -* xref::functions/mathematical-trigonometric.adoc#functions-acos[acos()] -* xref::functions/mathematical-trigonometric.adoc#functions-asin[asin()] -* xref::functions/mathematical-trigonometric.adoc#functions-atan[atan()] -* xref::functions/mathematical-trigonometric.adoc#functions-atan2[atan2()] -* xref::functions/mathematical-trigonometric.adoc#functions-cos[cos()] -* xref::functions/mathematical-trigonometric.adoc#functions-cot[cot()] -* xref::functions/mathematical-trigonometric.adoc#functions-degrees[degrees()] -* xref::functions/mathematical-trigonometric.adoc#functions-haversin[haversin()] -* xref::functions/mathematical-trigonometric.adoc#functions-spherical-distance-using-haversin[Spherical distance using the `haversin()` function] -* xref::functions/mathematical-trigonometric.adoc#functions-pi[pi()] -* xref::functions/mathematical-trigonometric.adoc#functions-radians[radians()] -* xref::functions/mathematical-trigonometric.adoc#functions-sin[sin()] -* xref::functions/mathematical-trigonometric.adoc#functions-tan[tan()] - - [[functions-acos]] == acos() diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 310e7e56a..7940e7921 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -9,15 +9,6 @@ Predicates are boolean functions that return `true` or `false` for a given set o They are most commonly used to filter out paths in the `WHERE` part of a query. -- -Functions: - -* xref::functions/predicate.adoc#functions-all[all()] -* xref::functions/predicate.adoc#functions-any[any()] -* xref::functions/predicate.adoc#functions-exists[exists()] -* xref::functions/predicate.adoc#functions-isempty[isEmpty()] -* xref::functions/predicate.adoc#functions-none[none()] -* xref::functions/predicate.adoc#functions-single[single()] - image:graph_predicate_functions.svg[] //// diff --git a/modules/ROOT/pages/functions/scalar.adoc b/modules/ROOT/pages/functions/scalar.adoc index a2c79f11a..011ecdf01 100644 --- a/modules/ROOT/pages/functions/scalar.adoc +++ b/modules/ROOT/pages/functions/scalar.adoc @@ -8,31 +8,6 @@ Scalar functions return a single value. -- -Functions: - -* xref::functions/scalar.adoc#functions-coalesce[coalesce()] -* xref::functions/scalar.adoc#functions-elementid[elementId()] -* xref::functions/scalar.adoc#functions-endnode[endNode()] -* xref::functions/scalar.adoc#functions-head[head()] -* xref::functions/scalar.adoc#functions-id[id()] label:deprecated[] -* xref::functions/scalar.adoc#functions-last[last()] -* xref::functions/scalar.adoc#functions-length[length()] -* xref::functions/scalar.adoc#functions-properties[properties()] -* xref::functions/scalar.adoc#functions-randomuuid[randomUUID()] -* xref::functions/scalar.adoc#functions-size[size()] -* xref::functions/scalar.adoc#functions-size-of-pattern-comprehension[Size of pattern comprehension] -* xref::functions/scalar.adoc#functions-size-of-string[Size of string] -* xref::functions/scalar.adoc#functions-startnode[startNode()] -* xref::functions/scalar.adoc#functions-timestamp[timestamp()] -* xref::functions/scalar.adoc#functions-toboolean[toBoolean()] -* xref::functions/scalar.adoc#functions-tobooleanornull[toBooleanOrNull()] -* xref::functions/scalar.adoc#functions-tofloat[toFloat()] -* xref::functions/scalar.adoc#functions-tofloatornull[toFloatOrNull()] -* xref::functions/scalar.adoc#functions-tointeger[toInteger()] -* xref::functions/scalar.adoc#functions-tointegerornull[toIntegerOrNull()] -* xref::functions/scalar.adoc#functions-type[type()] - - [IMPORTANT] ==== The `length()` and `size()` functions are quite similar, and so it is important to take note of the difference. diff --git a/modules/ROOT/pages/functions/spatial.adoc b/modules/ROOT/pages/functions/spatial.adoc index 1049bd81d..777c94178 100644 --- a/modules/ROOT/pages/functions/spatial.adoc +++ b/modules/ROOT/pages/functions/spatial.adoc @@ -8,15 +8,6 @@ These functions are used to specify 2D or 3D points in a Coordinate Reference System (CRS) and to calculate the geodesic distance between two points. -- -Functions: - -* xref::functions/spatial.adoc#functions-distance[point.distance()] -* xref::functions/spatial.adoc#functions-withinBBox[point.withinBBox()] -* xref::functions/spatial.adoc#functions-point-wgs84-2d[point() - WGS 84 2D] -* xref::functions/spatial.adoc#functions-point-wgs84-3d[point() - WGS 84 3D] -* xref::functions/spatial.adoc#functions-point-cartesian-2d[point() - Cartesian 2D] -* xref::functions/spatial.adoc#functions-point-cartesian-3d[point() - Cartesian 3D] - The following graph is used for some of the examples below. image:graph_spatial_functions.svg[] diff --git a/modules/ROOT/pages/functions/string.adoc b/modules/ROOT/pages/functions/string.adoc index f49c427a0..570cd22cb 100644 --- a/modules/ROOT/pages/functions/string.adoc +++ b/modules/ROOT/pages/functions/string.adoc @@ -20,23 +20,6 @@ This string will therefore be formatted according to the https://en.wikipedia.or See also xref::syntax/operators.adoc#query-operators-string[String operators]. -Functions: - -* xref::functions/string.adoc#functions-left[left()] -* xref::functions/string.adoc#functions-ltrim[ltrim()] -* xref::functions/string.adoc#functions-replace[replace()] -* xref::functions/string.adoc#functions-reverse[reverse()] -* xref::functions/string.adoc#functions-right[right()] -* xref::functions/string.adoc#functions-rtrim[rtrim()] -* xref::functions/string.adoc#functions-split[split()] -* xref::functions/string.adoc#functions-substring[substring()] -* xref::functions/string.adoc#functions-tolower[toLower()] -* xref::functions/string.adoc#functions-tostring[toString()] -* xref::functions/string.adoc#functions-tostringornull[toStringOrNull()] -* xref::functions/string.adoc#functions-toupper[toUpper()] -* xref::functions/string.adoc#functions-trim[trim()] - - [[functions-left]] == left() diff --git a/modules/ROOT/pages/syntax/operators.adoc b/modules/ROOT/pages/syntax/operators.adoc index 5443017fc..424a62003 100644 --- a/modules/ROOT/pages/syntax/operators.adoc +++ b/modules/ROOT/pages/syntax/operators.adoc @@ -7,44 +7,6 @@ This section contains an overview of operators. -- -* xref::syntax/operators.adoc#query-operators-summary[Operators at a glance] -* xref::syntax/operators.adoc#query-operators-aggregation[Aggregation operators] -** xref::syntax/operators.adoc#syntax-using-the-distinct-operator[Using the `DISTINCT` operator] -* xref::syntax/operators.adoc#query-operators-property[Property operators] -** xref::syntax/operators.adoc#syntax-accessing-the-property-of-a-node-or-relationship[Statically accessing a property of a node or relationship using the `.` operator] -** xref::syntax/operators.adoc#syntax-filtering-on-a-dynamically-computed-property-key[Filtering on a dynamically-computed property key using the `[\]` operator] -** xref::syntax/operators.adoc#syntax-property-replacement-operator[Replacing all properties of a node or relationship using the `=` operator] -** xref::syntax/operators.adoc#syntax-property-mutation-operator[Mutating specific properties of a node or relationship using the `+=` operator] -* xref::syntax/operators.adoc#query-operators-mathematical[Mathematical operators] -** xref::syntax/operators.adoc#syntax-using-the-exponentiation-operator[Using the exponentiation operator `^`] -** xref::syntax/operators.adoc#syntax-using-the-unary-minus-operator[Using the unary minus operator `-`] -* xref::syntax/operators.adoc#query-operators-comparison[Comparison operators] -** xref::syntax/operators.adoc#syntax-comparing-two-numbers[Comparing two numbers] -** xref::syntax/operators.adoc#syntax-using-starts-with-to-filter-names[Using `STARTS WITH` to filter names] -** xref::syntax/operators.adoc#cypher-comparison[Equality and comparison of values] -** xref::syntax/operators.adoc#cypher-ordering[Ordering and comparison of values] -** xref::syntax/operators.adoc#cypher-operations-chaining[Chaining comparison operations] -** xref::syntax/operators.adoc#syntax-using-a-regular-expression-to-filter-words[Using a regular expression with `=~` to filter words] -* xref::syntax/operators.adoc#query-operators-boolean[Boolean operators] -** xref::syntax/operators.adoc#syntax-using-boolean-operators-to-filter-numbers[Using boolean operators to filter numbers] -* xref::syntax/operators.adoc#query-operators-string[String operators] -** xref::syntax/operators.adoc#syntax-concatenating-two-strings[Concatenating two strings using `+`] -* xref::syntax/operators.adoc#query-operators-temporal[Temporal operators] -** xref::syntax/operators.adoc#syntax-add-subtract-duration-to-temporal-instant[Adding and subtracting a _Duration_ to or from a temporal instant] -** xref::syntax/operators.adoc#syntax-add-subtract-duration-to-duration[Adding and subtracting a _Duration_ to or from another _Duration_] -** xref::syntax/operators.adoc#syntax-multiply-divide-duration-number[Multiplying and dividing a _Duration_ with or by a number] -* xref::syntax/operators.adoc#query-operators-map[Map operators] -** xref::syntax/operators.adoc#syntax-accessing-the-value-of-a-nested-map[Statically accessing the value of a nested map by key using the `.` operator"] -** xref::syntax/operators.adoc#syntax-accessing-dynamic-map-parameter[Dynamically accessing the value of a map by key using the `[\]` operator and a parameter] -* xref::syntax/operators.adoc#query-operators-list[List operators] -** xref::syntax/operators.adoc#syntax-concatenating-two-lists[Concatenating two lists using `+`] -** xref::syntax/operators.adoc#syntax-using-in-to-check-if-a-number-is-in-a-list[Using `IN` to check if a number is in a list] -** xref::syntax/operators.adoc#syntax-using-in-for-more-complex-list-membership-operations[Using `IN` for more complex list membership operations] -** xref::syntax/operators.adoc#syntax-accessing-elements-in-a-list[Accessing elements in a list using the `[\]` operator] -** xref::syntax/operators.adoc#syntax-accessing-element-in-a-list-parameter[Dynamically accessing an element in a list using the `[\]` operator and a parameter] -** xref::syntax/operators.adoc#syntax-using-in-with-nested-list-subscripting[Using `IN` with `[\]` on a nested list] - - [[query-operators-summary]] == Operators at a glance From 74166139dcb9657864268c84c3506527199729c1 Mon Sep 17 00:00:00 2001 From: Jack Waudby <33488812+jackwaudby@users.noreply.github.com> Date: Fri, 24 Feb 2023 10:22:30 +0000 Subject: [PATCH 139/383] Update `server.tag` documentation (#433) - Document how the `initial.server.tags` is used from config settings when the server is enabled. - Document how `tags` can be changed at runtime via `ALTER SERVER`. --------- --- .../pages/access-control/manage-servers.adoc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index 799d7f9e7..2760fbf46 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -303,6 +303,10 @@ This may not be specified in combination with `deniedDatabases`. | list of database names | Only databases **not** matching the specified names may be hosted on the server. This may not be specified in combination with `allowedDatabases`. + +| tags +| list of server tags +| List of server tags used during database allocation and for load balancing and routing policies. |=== [NOTE] @@ -311,6 +315,11 @@ Composite databases are ignored by both `allowedDatabases` and `deniedDatabases` The composite databases are available everywhere and hold no data on their own. ==== +[NOTE] +==== +When a server is enabled, if `tags` are not provided in `OPTIONS`, the default server tags are taken from the setting `initial.server.tags`. +==== + [[server-management-alter-server]] == Modifying servers @@ -339,6 +348,10 @@ This may not be specified in combination with `deniedDatabases`. | list of database names | Only databases **not** matching the specified names may be hosted on the server. This may not be specified in combination with `allowedDatabases`. + +| tags +| list of server tags +| List of server tags used during database allocation and for load balancing and routing policies. |=== [NOTE] @@ -347,6 +360,12 @@ Composite databases are ignored by both `allowedDatabases` and `deniedDatabases` The composite databases are available everywhere and hold no data on their own. ==== +[NOTE] +==== +Input provided to `SET OPTIONS {...}` replaces **all** existing options, rather than being combined with them. +For instance, if `SET OPTIONS {modeConstraint:'SECONDARY'}` is run followed by `SET OPTIONS {allowedDatabases:['foo']}`, the second `ALTER` removes the mode constraint. +==== + [[server-management-rename-server]] == Renaming servers From 9d6bdf6e2662f16149fccb29a56b46845e02190c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 27 Feb 2023 20:20:30 +0100 Subject: [PATCH 140/383] New graph to Aggregating Functions page (#446) - new graph - general edit of text --- .../images/graph_aggregating_functions.svg | 110 +------- modules/ROOT/pages/functions/aggregating.adoc | 262 +++++++++--------- 2 files changed, 132 insertions(+), 240 deletions(-) diff --git a/modules/ROOT/images/graph_aggregating_functions.svg b/modules/ROOT/images/graph_aggregating_functions.svg index 624e6cfe2..a2ec6323e 100644 --- a/modules/ROOT/images/graph_aggregating_functions.svg +++ b/modules/ROOT/images/graph_aggregating_functions.svg @@ -1,109 +1 @@ - - - - - - -L - - - -N0 - -Person - -age = 13 -name = 'A' - - - -N1 - -Person - -eyes = 'blue' -age = 33 -name = 'B' - - - -N0->N1 - - -KNOWS - - - -N2 - -Person - -eyes = 'blue' -age = 44 -name = 'C' - - - -N0->N2 - - -KNOWS - - - -N3 - -Person - -eyes = 'brown' -name = 'D' - - - -N0->N3 - - -KNOWS - - - -N5 - -Book - -name = 'Cypher' - - - -N0->N5 - - -READS - - - -N4 - -Person - -name = 'D' - - - -N1->N4 - - -KNOWS - - - -N2->N4 - - -KNOWS - - - +KNOWSKNOWSKNOWSKNOWSKNOWSACTED_INPersonname:'Guy Pearce'age:55Personname:'Carrie Anne Moss'age:55Personname:'Keanu Reeves'age:58Personname:'Liam Neeson'age:70Personname:'Kathryn Bigelow'age:71Movietitle:'Speed' \ No newline at end of file diff --git a/modules/ROOT/pages/functions/aggregating.adoc b/modules/ROOT/pages/functions/aggregating.adoc index 2be36cf47..846dd23a9 100644 --- a/modules/ROOT/pages/functions/aggregating.adoc +++ b/modules/ROOT/pages/functions/aggregating.adoc @@ -3,31 +3,28 @@ [[query-functions-aggregating]] = Aggregating functions -[abstract] --- -Aggregating functions take a set of values and calculate an aggregated value over them. --- +== Introduction +Aggregating functions take a set of values and calculate an aggregated value over them. Aggregation can be computed over all the matching paths, or it can be further divided by introducing grouping keys. -Grouping keys are non-aggregate expressions, that are used to group the values going into the aggregate functions. +Grouping keys are non-aggregate expressions that are used to group the values going into the aggregate functions. -Assume we have the following return statement: +For example, given the following query containing two return expressions, `n` and `+count(*)+`: [source, cypher, role=test-skip] ---- RETURN n, count(*) ---- -We have two return expressions: `n`, and `+count(*)+`. -The first, `n`, is not an aggregate function, so it will be the grouping key. +The first, `n` is not an aggregate function, so it will be the grouping key. The latter, `+count(*)+` is an aggregate expression. The matching paths will be divided into different buckets, depending on the grouping key. The aggregate function will then be run on these buckets, calculating an aggregate value per bucket. The input expression of an aggregation function can contain any expression, including expressions that are not grouping keys. However, not all expressions can be composed with aggregation functions. -The example below will throw an error since we compose `n.x`, which is not a grouping key, with the aggregation expression `+count(*)+`. -For more information see xref:functions/aggregating.adoc#grouping-keys[Grouping keys]. +The example below will throw an error since `n.x`, which is not a grouping key, is combined with the aggregation expression `+count(*)+`. +For more information, see xref:functions/aggregating.adoc#grouping-keys[Grouping keys]. [source, cypher, role=test-skip] ---- @@ -38,29 +35,31 @@ To use aggregations to sort the result set, the aggregation must be included in The `DISTINCT` operator works in conjunction with aggregation. It is used to make all values unique before running them through an aggregate function. -More information about `DISTINCT` may be found in xref::syntax/operators.adoc#query-operators-aggregation[Syntax -> Aggregation operators]. +More information about `DISTINCT` can be found in xref::syntax/operators.adoc#query-operators-aggregation[Syntax -> Aggregation operators]. + +== Example graph The following graph is used for the examples below: -image:graph_aggregating_functions.svg[] +image::graph_aggregating_functions.svg[] -To recreate the graph, run the following query in an empty Neo4j database: +To recreate the graph, run the following query against an empty Neo4j database: [source, cypher, role=test-setup] ---- CREATE - (a:Person {name: 'A', age: 13}), - (b:Person {name: 'B', age: 33, eyes: 'blue'}), - (c:Person {name: 'C', age: 44, eyes: 'blue'}), - (d1:Person {name: 'D', eyes: 'brown'}), - (d2:Person {name: 'D'}), - (book:Book {name: 'Cypher'}), - (a)-[:READS]->(book), - (a)-[:KNOWS]->(d1), - (a)-[:KNOWS]->(c), - (a)-[:KNOWS]->(b), - (c)-[:KNOWS]->(d2), - (b)-[:KNOWS]->(d2) + (keanu:Person {name: 'Keanu Reeves', age: 58}), + (liam:Person {name: 'Liam Neeson', age: 70}), + (carrie:Person {name: 'Carrie Anne Moss', age: 55}), + (guy:Person {name: 'Guy Pearce', age: 55}), + (kathryn:Person {name: 'Kathryn Bigelow', age: 71}), + (speed:Movie {title: 'Speed'}), + (keanu)-[:ACTED_IN]->(speed), + (keanu)-[:KNOWS]->(carrie), + (keanu)-[:KNOWS]->(liam), + (keanu)-[:KNOWS]->(kathryn), + (carrie)-[:KNOWS]->(guy), + (liam)-[:KNOWS]->(guy) ---- [[functions-avg]] @@ -108,18 +107,18 @@ avg(expression) .Query [source, cypher] ---- -MATCH (n:Person) -RETURN avg(n.age) +MATCH (p:Person) +RETURN avg(p.age) ---- -The average of all the values in the property `age` is returned. +The average of all the values in the property `age` is returned: .Result [role="queryresult",options="header,footer",cols="1*(x) -RETURN labels(n), n.age, count(*) +MATCH (p:Person {name: 'Keanu Reeves'})-->(x) +RETURN labels(p), p.age, count(*) ---- -The labels and `age` property of the start node `n` and the number of nodes related to `n` are returned. +The labels and `age` property of the start node `Keanu Reeves` and the number of nodes related to it are returned: .Result [role="queryresult",options="header,footer",cols="3*() +MATCH (p:Person {name: 'Keanu Reeves'})-[r]->() RETURN type(r), count(*) ---- -The type of matched relationships are grouped and the group count are returned. +The type of matched relationships are grouped and the group count of relationship types is returned: .Result [role="queryresult",options="header,footer",cols="2*(friend:Person)-->(friend_of_friend:Person) -WHERE me.name = 'A' -RETURN count(DISTINCT friend_of_friend), count(friend_of_friend) +MATCH (p:Person)-->(friend:Person)-->(friendOfFriend:Person) +WHERE p.name = 'Keanu Reeves' +RETURN friendOfFriend.name, count(DISTINCT friendOfFriend), count(friendOfFriend) ---- -Both `B` and `C` know `D` and thus `D` will get counted twice when not using `DISTINCT`. +The nodes `Carrie Anne Moss` and `Liam Neeson` both have an outgoing `KNOWS` relationship to `Guy Pearce`. +The `Guy Pearce` node will, therefore, get counted twice when not using `DISTINCT`. .Result -[role="queryresult",options="header,footer",cols="2* Date: Tue, 28 Feb 2023 16:00:13 +0100 Subject: [PATCH 141/383] Small fix on MATCH page (#450) - fixes TOC - Fixes one instance of broken ascii --- modules/ROOT/pages/clauses/match.adoc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 5dc930f57..5ad15ded2 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -3,11 +3,6 @@ [[query-match]] = MATCH -[abstract] --- -The `MATCH` clause is used to search for the pattern described in it. --- - [[match-introduction]] == Introduction @@ -36,12 +31,14 @@ Read more about indexes in xref::indexes-for-search-performance.adoc[], and more To understand more about the patterns used in the `MATCH` clause, read the chapter on xref::syntax/patterns.adoc[Patterns]. ==== +[[match-example-graph]] +== Example graph + The following graph is used for the examples below: image::graph_match_clause.svg[width="600",role="middle"] - -To recreate the graph, run the following query in an empty Neo4j database: +To recreate the graph, run the following query against an empty Neo4j database: [source, cypher, role=test-setup] ---- @@ -284,6 +281,7 @@ RETURN person.name ---- Returns nodes with an `ACTED_IN` or `DIRECTED` relationship to the movie `Wall Street`. + .Result [role="queryresult",options="header,footer",cols="1* Date: Wed, 1 Mar 2023 09:47:57 +0100 Subject: [PATCH 142/383] Enable remote aliases tests (#447) We have added the AES key into test environment, so now we are support the remote aliases testes --- modules/ROOT/pages/aliases.adoc | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 593137aea..4aa002f41 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -255,8 +255,6 @@ CREATE COMPOSITE DATABASE `library`; CREATE ALIAS `library`.`sci-fi` FOR DATABASE `sci-fi-books`; CREATE COMPOSITE DATABASE garden; CREATE DATABASE `perennial-flowers`; ----- - CREATE ALIAS `library`.`romance` FOR DATABASE `romance-books` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password'; CREATE ALIAS `movie scripts` FOR DATABASE `scripts` AT "neo4j+s://location:7687" USER alice PASSWORD "password" DRIVER { @@ -268,6 +266,7 @@ DRIVER { connection_pool_max_size: 10, logging_level: 'info' }; +---- //// Available database aliases can be seen using `SHOW ALIASES FOR DATABASE`. @@ -409,9 +408,9 @@ SHOW ALIASES FOR DATABASE YIELD * The number of database aliases can be seen using a `count()` aggregation with `YIELD` and `RETURN`. -//Skip testing this example because we skip remote aliases in testing, so the count is not the same. + .Query -[source, cypher, role=test-skip] +[source, cypher] ---- SHOW ALIASES FOR DATABASE YIELD * RETURN count(*) as count @@ -680,7 +679,7 @@ Both check for any remote or local database aliases. //Skip testing all remote examples because it requires a lot of configuration, both server and client side. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- CREATE ALIAS `remote-northwind` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" @@ -697,7 +696,7 @@ Rows: 0 When a database alias pointing to a remote database has been created, its details can be shown with the `SHOW ALIASES FOR DATABASE` command. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- SHOW ALIAS `remote-northwind` FOR DATABASE @@ -721,7 +720,7 @@ It is possible to override the default driver settings per database alias, which The full list of supported driver settings can be seen xref::aliases.adoc#remote-alias-driver-settings[here]. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- CREATE ALIAS `remote-with-driver-settings` FOR DATABASE `northwind-graph-2020` AT "neo4j+s://location:7687" @@ -742,7 +741,7 @@ Rows: 0 When a database alias pointing to a remote database has been created, its details can be shown with the `SHOW ALIASES FOR DATABASE` command. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- SHOW ALIAS `remote-with-driver-settings` FOR DATABASE YIELD * ---- @@ -765,7 +764,7 @@ Just as the local database aliases, the remote database aliases can be given pro These properties can then be used in queries with the xref::functions/graph.adoc#functions-graph-propertiesByName[`graph.propertiesByName()` function]. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- CREATE ALIAS `remote-northwind-2021` FOR DATABASE `northwind-graph-2021` AT 'neo4j+s://location:7687' USER alice PASSWORD 'password' @@ -781,7 +780,7 @@ Rows: 0 The properties are then shown in the `SHOW ALIASES FOR DATABASE YIELD ...` command. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- SHOW ALIAS `remote-northwind-2021` FOR DATABASE YIELD name, properties ---- @@ -823,7 +822,7 @@ Rows: 0 ---- .Query -[source, cypher, role=test-skip] +[source, cypher] ---- CREATE ALIAS garden.trees FOR DATABASE trees AT 'neo4j+s://location:7687' @@ -993,7 +992,7 @@ SHOW DATABASE `northwind-graph-2021` Example of altering a remote database alias target. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- ALTER ALIAS `remote-northwind` SET DATABASE TARGET `northwind-graph-2020` AT "neo4j+s://other-location:7687" @@ -1015,7 +1014,7 @@ Example of altering a remote database alias credentials and driver settings. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- ALTER ALIAS `remote-with-driver-settings` SET DATABASE USER bob @@ -1047,7 +1046,7 @@ In this case, by not repeating the driver setting `connection_pool_max_size` the Example of altering a remote database alias to remove all custom driver settings. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- ALTER ALIAS `movie scripts` SET DATABASE DRIVER {} @@ -1077,7 +1076,7 @@ Rows: 0 ---- .Query -[source, cypher, role=test-skip] +[source, cypher] ---- ALTER ALIAS `movie scripts` SET DATABASE PROPERTIES { nameContainsSpace: true } ---- @@ -1108,7 +1107,7 @@ Rows: 0 ---- .Query -[source, cypher, role=test-skip] +[source, cypher] ---- ALTER ALIAS garden.trees SET DATABASE TARGET updatedTrees AT 'neo4j+s://location:7687' PROPERTIES { treeVersion: 2 } ---- @@ -1142,7 +1141,7 @@ WHERE name IN ['northwind', 'remote-northwind', 'remote-with-driver-settings', ' | +"garden.flowers"+ | +"perennial-flowers"+ | +"local"+ | ++ | ++ | ++ | +{"perennial":true}+ | +"garden.trees"+ | +"updatedtrees"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{}+ | +{"treeversion":2}+ | +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | ++ | +{"namecontainsspace":true,"moreinfo":"no, not really"}+ -| +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{}+ | +{"namecontainsspace":true}+ +| +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +{connection_pool_idle_test: PT2M, connection_pool_max_size: 10, logging_level: "INFO", ssl_enforced: TRUE, connection_pool_acquisition_timeout: PT1M, connection_timeout: PT5S, connection_max_lifetime: PT1H}+ | +{"namecontainsspace":true}+ | +"northwind"+ | +"northwind-graph-2021"+ | +"local"+ | ++ | ++ | ++ |+[]+ | +"remote-northwind"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://other-location:7687"+ | +"alice"+ | +{}+ | +{}+ | +"remote-with-driver-settings"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"bob"+ | +{logging_level -> "DEBUG", connection_timeout -> PT1M}+ |+[]+ @@ -1246,7 +1245,7 @@ SHOW DATABASE `northwind-graph-2021` Delete a remote database alias. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- DROP ALIAS `remote-northwind` FOR DATABASE ---- @@ -1289,12 +1288,13 @@ SHOW ALIASES FOR DATABASE | +name+ | +database+ | +location+ | +url+ | +user+ | +"films"+ | +"movies"+ | +"local"+ | ++ | ++ -| +"garden.trees"+ | +"updatedtrees"+ | +"local"+ | ++ | ++ +| +"garden.trees"+ | +"updatedtrees"+ | +"local"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +"library.romance"+ | +"romance-books"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +"library.sci-fi"+ | +"sci-fi-books"+ | +"local"+ | ++ | ++ | +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +"northwind-2022"+ | +"northwind-graph-2022"+ | +"local"+ | ++ | ++ +| +"remote-northwind"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://other-location:7687"+ | +"alice"+ | +"remote-northwind-2021"+ | +"northwind-graph-2021"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ | +"remote-with-driver-settings"+ | +"northwind-graph-2020"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"bob"+ 5+d|Rows: 9 @@ -1431,7 +1431,7 @@ If a composite database `some` exists, the query below will create a database al If no such database exists, however, the same query will instead create a database alias named `some.alias`: .Query -[source, cypher, role=test-skip] +[source, cypher] ---- CREATE ALIAS some.alias FOR DATABASE `northwind-graph` ---- From 77a7c38d2f644f900bc17c61158667e6ed56f328 Mon Sep 17 00:00:00 2001 From: Gem Lamont <106068376+gem-neo4j@users.noreply.github.com> Date: Wed, 1 Mar 2023 13:58:31 +0100 Subject: [PATCH 143/383] Add docs for new COLLECT subqueries (#398) Docs for Collect Subqueries: https://github.com/neo-technology/neo4j/pull/19242 --- .../images/graph_expression_subqueries.svg | 146 ++++---- ...ions-additions-removals-compatibility.adoc | 26 ++ modules/ROOT/pages/syntax/expressions.adoc | 315 +++++++++++++++++- 3 files changed, 413 insertions(+), 74 deletions(-) diff --git a/modules/ROOT/images/graph_expression_subqueries.svg b/modules/ROOT/images/graph_expression_subqueries.svg index b037298a9..0b116c781 100644 --- a/modules/ROOT/images/graph_expression_subqueries.svg +++ b/modules/ROOT/images/graph_expression_subqueries.svg @@ -3,117 +3,119 @@ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> - - - -L - + + + +H + Andy - -Swedish, Person - -age = 36 -name = 'Andy' + +Swedish, Person + +age = 36 + name = 'Andy' - + -DogAndy - -Dog - -name = 'Andy' +AndyDog + +Dog + +name = 'Andy' - + -Andy->DogAndy - - -  HAS_DOG -since = 2016 +Andy->AndyDog + + +HAS_DOG +  since = 2016 Timothy - -Person - -age = 25 -name = 'Timothy' + +Person + +age = 25 + name = 'Timothy' + nickname = 'Tim' - + Mittens - -Cat - -name = 'Mittens' + +Cat + +name = 'Mittens' - + Timothy->Mittens - - -  HAS_CAT -since = 2019 + + +HAS_CAT +  since = 2019 Peter - -Person - -age = 35 -name = 'Peter' + +Person + +age = 25 + name = 'Peter' + nickname = 'Pete' - + Ozzy - -Dog - -name = 'Ozzy' + +Dog + +name = 'Ozzy' - + Peter->Ozzy - - -  HAS_DOG -since = 2018 + + +HAS_DOG +  since = 2018 - + Fido - -Dog - -name = 'Fido' + +Dog + +name = 'Fido' - + Peter->Fido - - -  HAS_DOG -since = 2010 + + +HAS_DOG +  since = 2010 - + Banana - -Toy - -name = 'Banana' + +Toy + +name = 'Banana' - + Fido->Banana - - -  HAS_TOY + + +  HAS_TOY diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index b38744c38..dc4800177 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -10,6 +10,32 @@ New features are added to the language continuously, and occasionally, some feat This section lists all of the features that have been removed, deprecated, added, or extended in different Cypher versions. Replacement syntax for deprecated and removed features are also indicated. +[[cypher-deprecations-additions-removals-5.6]] +== Version 5.6 + + +=== New features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:added[] +[source, cypher, role="noheader"] +---- +COLLECT { + ... +} +---- +a| + +New expression which returns the results of a subquery collected in a list. + +|=== + [[cypher-deprecations-additions-removals-5.5]] == Version 5.5 diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index 2f36a1928..5a8d9ac4d 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -378,8 +378,8 @@ The following graph is used for the examples below: MATCH (n:A|B|C|D|E) DETACH DELETE n; CREATE (andy:Swedish:Person {name: 'Andy', age: 36}), -(timothy:Person {name: 'Timothy', age: 25}), -(peter:Person {name: 'Peter', age: 35}), +(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}), +(peter:Person {name: 'Peter', nickname: 'Pete', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), (timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), @@ -863,6 +863,317 @@ RETURN person.name AS name |=== +[[collect-subqueries]] +=== `COLLECT` subqueries + +A `COLLECT` subquery expression can be used to create a list with the rows returned by a given subquery. + +Any non-writing query is allowed. +`COLLECT` subqueries differ from `COUNT` and `EXISTS` subqueries in that the final `RETURN` clause is mandatory. +The `RETURN` clause must return exactly one column. + +[[collect-subquery-simple-case]] +==== Simple `COLLECT` subquery + +Variables introduced by the outside scope can be used in the `COLLECT` subquery without importing them. +In this regard, `COLLECT` subqueries are different from `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[which do require importing]. +The following query exemplifies this and outputs the owners of the dog named `Ozzy`: + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE 'Ozzy' IN COLLECT { MATCH (person)-[:HAS_DOG]->(dog:Dog) RETURN dog.name } +RETURN person.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE r.since > 2017 + RETURN dog.name +} as youngDogs +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS petNames +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(d:Dog {name: name}) + RETURN d.name +} as dogsOfTheYear +---- + +.Error message +[source, output, role="noheader"] +---- +The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 92)) +---- + +New variables can be introduced into the subquery, as long as they use a different identifier. +In the below example, a `WITH` clause introduces a new variable. +Note that the outer scope variable `person` referenced in the main query is still available after the `WITH` clause. + +.Query +[source, cypher] +---- +MATCH (person:Person) +RETURN person.name AS name, COLLECT { + WITH 2018 AS yearOfTheDog + MATCH (person)-[r:HAS_DOG]->(d:Dog) + WHERE r.since = yearOfTheDog + RETURN d.name +} as dogsOfTheYear +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(d:Dog) + MATCH (d)-[:HAS_TOY]->(t:Toy) + RETURN t.name + } as toyNames +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(d:Dog) RETURN d.name } +RETURN person.dogNames as dogNames +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } = [] THEN "No Dogs " + person.name + ELSE person.name + END AS result +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } AS dogNames, + avg(person.age) AS averageAge + ORDER BY dogNames +---- + +.Result +[role="queryresult",options="header,footer",cols="2* Date: Thu, 2 Mar 2023 13:13:28 +0100 Subject: [PATCH 144/383] Add Aura roles and labels (#437) Add Aura roles throughout the Cypher Manual Two roles/labels: - not-on-aura("Not Available on Aura"): not available on any aura tier - aura-db-enterprise("AuraDB Enterprise"): only available on AuraDB Enterprise --- .../pages/access-control/built-in-roles.adoc | 2 +- .../database-administration.adoc | 2 +- .../access-control/dbms-administration.adoc | 2 +- .../pages/access-control/limitations.adoc | 1 + .../access-control/manage-privileges.adoc | 14 +---------- .../pages/access-control/manage-roles.adoc | 2 +- .../pages/access-control/manage-servers.adoc | 6 +++++ .../access-control/privileges-immutable.adoc | 2 +- .../access-control/privileges-reads.adoc | 2 +- .../access-control/privileges-writes.adoc | 2 +- modules/ROOT/pages/aliases.adoc | 14 +---------- modules/ROOT/pages/databases.adoc | 23 ++++++++++--------- 12 files changed, 28 insertions(+), 44 deletions(-) diff --git a/modules/ROOT/pages/access-control/built-in-roles.adoc b/modules/ROOT/pages/access-control/built-in-roles.adoc index 0c3674ae3..9f009b9f3 100644 --- a/modules/ROOT/pages/access-control/built-in-roles.adoc +++ b/modules/ROOT/pages/access-control/built-in-roles.adoc @@ -1,5 +1,5 @@ :description: The default privileges of the built-in roles in Neo4j and how to recreate them if needed. -[role=enterprise-edition] +[role=enterprise-edition aura-db-enterprise] [[access-control-built-in-roles]] = Built-in roles and privileges diff --git a/modules/ROOT/pages/access-control/database-administration.adoc b/modules/ROOT/pages/access-control/database-administration.adoc index 4304748b2..a1a21df88 100644 --- a/modules/ROOT/pages/access-control/database-administration.adoc +++ b/modules/ROOT/pages/access-control/database-administration.adoc @@ -10,7 +10,7 @@ CREATE USER jake SET PASSWORD 'abcd1234' CHANGE NOT REQUIRED; ---- //// -[role=enterprise-edition] +[role=enterprise-edition aura-db-enterprise] [[access-control-database-administration]] = Database administration diff --git a/modules/ROOT/pages/access-control/dbms-administration.adoc b/modules/ROOT/pages/access-control/dbms-administration.adoc index 2c8f00e57..4c1964a63 100644 --- a/modules/ROOT/pages/access-control/dbms-administration.adoc +++ b/modules/ROOT/pages/access-control/dbms-administration.adoc @@ -58,7 +58,7 @@ CREATE ROLE dbmsManager IF NOT EXISTS; ---- //// -[role=enterprise-edition] +[role=enterprise-edition aura-db-enterprise] [[access-control-dbms-administration]] = DBMS administration diff --git a/modules/ROOT/pages/access-control/limitations.adoc b/modules/ROOT/pages/access-control/limitations.adoc index e534e8b1e..084ca2bd3 100644 --- a/modules/ROOT/pages/access-control/limitations.adoc +++ b/modules/ROOT/pages/access-control/limitations.adoc @@ -10,6 +10,7 @@ CREATE ROLE free; ---- //// +[role=enterprise-edition aura-db-enterprise] [[access-control-limitations]] = Limitations diff --git a/modules/ROOT/pages/access-control/manage-privileges.adoc b/modules/ROOT/pages/access-control/manage-privileges.adoc index 8fdcca9f3..dd0fab545 100644 --- a/modules/ROOT/pages/access-control/manage-privileges.adoc +++ b/modules/ROOT/pages/access-control/manage-privileges.adoc @@ -1,4 +1,5 @@ :description: This section explains how to use Cypher to manage privileges for Neo4j role-based access control and fine-grained security. +[role=enterprise-edition aura-db-enterprise] [[access-control-manage-privileges]] = Managing privileges @@ -36,8 +37,6 @@ Information about the database access privilege can be found in xref::access-con The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. ==== - -[role=enterprise-edition] [[access-control-graph-privileges]] == Graph privilege commands (`GRANT`, `DENY` and `REVOKE`) @@ -196,8 +195,6 @@ The following image shows the hierarchy between different graph privileges: image::privileges_hierarchy.svg[title="Graph privileges hierarchy"] - -[role=enterprise-edition] [[access-control-list-privileges]] == Listing privileges @@ -290,8 +287,6 @@ When omitting the `AS COMMANDS` clause, results will include multiple columns de * `role`: the role a privilege is granted to. * `immutable`: whether or not the privilege is immutable. - -[role=enterprise-edition] [[access-control-list-all-privileges]] === Examples for listing all privileges @@ -1009,8 +1004,6 @@ a|Rows: 35 For more info about revoking privileges, please see xref::access-control/manage-privileges.adoc#access-control-revoke-privileges[The REVOKE command]. - -[role=enterprise-edition] [[access-control-list-privileges-role]] === Examples for listing privileges for specific roles @@ -1152,8 +1145,6 @@ SHOW ROLE reader PRIVILEGES AS REVOKE COMMANDS a|Rows: 3 |=== - -[role=enterprise-edition] [[access-control-list-privileges-user]] === Examples for listing privileges for specific users @@ -1378,9 +1369,6 @@ WHERE command CONTAINS 'EXECUTE' a|Rows: 2 |=== - - -[role=enterprise-edition] [[access-control-revoke-privileges]] == Revoking privileges diff --git a/modules/ROOT/pages/access-control/manage-roles.adoc b/modules/ROOT/pages/access-control/manage-roles.adoc index 33043227e..b4cf2510e 100644 --- a/modules/ROOT/pages/access-control/manage-roles.adoc +++ b/modules/ROOT/pages/access-control/manage-roles.adoc @@ -1,6 +1,6 @@ :description: This section explains how to use Cypher to manage roles in Neo4j. -[role=enterprise-edition] +[role=enterprise-edition aura-db-enterprise] [[access-control-manage-roles]] = Managing roles diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index 2760fbf46..25c9950ad 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -272,6 +272,7 @@ A summary of all servers can be displayed using the command `SHOW SERVERS`. | "0c030000-267b-49a8-801f-78bd0b5c6445" | "localhost:20021" | "Free" | "Available" | ["system"] |=== +[role=not-on-aura] [[server-management-enable-server]] == Enabling servers @@ -315,11 +316,14 @@ Composite databases are ignored by both `allowedDatabases` and `deniedDatabases` The composite databases are available everywhere and hold no data on their own. ==== + + [NOTE] ==== When a server is enabled, if `tags` are not provided in `OPTIONS`, the default server tags are taken from the setting `initial.server.tags`. ==== +[role=not-on-aura] [[server-management-alter-server]] == Modifying servers @@ -393,6 +397,7 @@ Using `DRYRUN REALLOCATE DATABASE` returns a view of how the databases would hav `DRYRUN` is introduced in Neo4j 5.2, and thus is not available in earlier minor releases of v5. ==== +[role=not-on-aura] [[server-management-deallocate]] == Deallocate databases @@ -415,6 +420,7 @@ Using `DRYRUN DEALLOCATE DATABASES FROM 'server-1', 'server-2'` returns a view o | "db1" | "server-2" | "00000000-7e53-427c-a987-24634c4745f3" | "server-5" | "00000003-0e98-44c8-9844-f0a4eb95b0d8" | "primary" |=== +[role=not-on-aura] [[server-management-drop-server]] == Drop server diff --git a/modules/ROOT/pages/access-control/privileges-immutable.adoc b/modules/ROOT/pages/access-control/privileges-immutable.adoc index 444d8bd6d..f201a96db 100644 --- a/modules/ROOT/pages/access-control/privileges-immutable.adoc +++ b/modules/ROOT/pages/access-control/privileges-immutable.adoc @@ -1,4 +1,4 @@ -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[access-control-privileges-immutable]] = Immutable privileges :description: This section explains how to use Cypher to manage immutable privileges. diff --git a/modules/ROOT/pages/access-control/privileges-reads.adoc b/modules/ROOT/pages/access-control/privileges-reads.adoc index c6dee748c..a457dd5c3 100644 --- a/modules/ROOT/pages/access-control/privileges-reads.adoc +++ b/modules/ROOT/pages/access-control/privileges-reads.adoc @@ -7,7 +7,7 @@ CREATE ROLE regularUsers; ---- //// -[role=enterprise-edition] +[role=enterprise-edition aura-db-enterprise] [[access-control-privileges-reads]] = Read privileges diff --git a/modules/ROOT/pages/access-control/privileges-writes.adoc b/modules/ROOT/pages/access-control/privileges-writes.adoc index 1604b21fe..a2fbbb984 100644 --- a/modules/ROOT/pages/access-control/privileges-writes.adoc +++ b/modules/ROOT/pages/access-control/privileges-writes.adoc @@ -7,7 +7,7 @@ CREATE ROLE regularUsers; ---- //// -[role=enterprise-edition] +[role=enterprise-edition aura-db-enterprise] [[access-control-privileges-writes]] = Write privileges diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 4aa002f41..01b64ee67 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -1,5 +1,5 @@ :description: How to use Cypher to manage database aliases in Neo4j. - +[role=enterprise-edition aura-db-enterprise] [[alias-management]] = Database alias management @@ -237,7 +237,6 @@ This prevents issues such as a transaction executing against multiple target dat ==== -[role=enterprise-edition] [[alias-management-show-alias]] == Listing database aliases @@ -461,7 +460,6 @@ It is also possible to use `SKIP` and `LIMIT` to paginate the results. ====== -[role=enterprise-edition] [[alias-management-create-database-alias]] == Creating database aliases @@ -509,8 +507,6 @@ The `IF NOT EXISTS` and `OR REPLACE` parts of this command cannot be used togeth Database alias names are subject to the rules specified in the xref:alias-management-escaping[Alias names and escaping] section. ==== - -[role=enterprise-edition] [[database-management-create-local-database-alias]] === Creating local database aliases @@ -661,8 +657,6 @@ CREATE ALIAS `northwind` FOR DATABASE `northwind-graph-2020` ====== - -[role=enterprise-edition] [[database-management-create-remote-database-alias]] === Creating remote database aliases @@ -796,8 +790,6 @@ SHOW ALIAS `remote-northwind-2021` FOR DATABASE YIELD name, properties |=== ====== - -[role=enterprise-edition] [[alias-management-create-composite-database-alias]] === Create database aliases in composite databases @@ -1171,8 +1163,6 @@ ALTER ALIAS `no-alias` IF EXISTS SET DATABASE TARGET `northwind-graph-2021` ====== - -[role=enterprise-edition] [[alias-management-drop-database-alias]] == Deleting database aliases @@ -1320,8 +1310,6 @@ DROP ALIAS `northwind` IF EXISTS FOR DATABASE ====== - -[role=enterprise-edition] [[alias-management-escaping]] == Alias names and escaping //// diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 0e78c7f87..f1aa378d5 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -463,7 +463,7 @@ SHOW DATABASE library YIELD name, constituents ====== -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-create-database]] == Creating databases @@ -535,7 +535,7 @@ SHOW DATABASES YIELD name ====== -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-create-database-topology]] === Cluster topology @@ -557,7 +557,7 @@ Composite databases are always available on all servers. ==== -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-create-composite-database]] === Creating composite databases @@ -613,7 +613,7 @@ In order to create database aliases in the composite database, give the composit For information about creating aliases in composite databases, see xref:aliases.adoc#alias-management-create-composite-database-alias[here]. -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-create-database-existing]] === Handling Existing Databases @@ -656,7 +656,7 @@ The `IF NOT EXISTS` and `OR REPLACE` parts of these commands cannot be used toge ====== -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-create-database-options]] === Options @@ -710,12 +710,13 @@ For details about the use of these seeding options, see link:{neo4j-docs-base-ur ==== -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-alter-database]] == Altering databases Standard databases can be modified using the command `ALTER DATABASE`. +[role=enterprise-edition not-on-aura] [[administration-databases-alter-database-access]] === Access @@ -800,7 +801,7 @@ SET ACCESS READ WRITE ====== - +[role=enterprise-edition not-on-aura] [[administration-databases-alter-database-topology]] === Topology @@ -859,7 +860,7 @@ ALTER DATABASE nonExisting IF EXISTS SET TOPOLOGY 1 PRIMARY 0 SECONDARY 0 rows -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-stop-database]] == Stopping databases @@ -912,7 +913,7 @@ SHOW DATABASE customers YIELD name, requestedStatus, currentStatus ====== -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-start-database]] == Starting databases @@ -965,7 +966,7 @@ SHOW DATABASE customers YIELD name, requestedStatus, currentStatus ====== -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-databases-drop-database]] == Deleting databases @@ -1085,7 +1086,7 @@ To ensure the database to be dropped is standard and not composite, the user fir ====== -[role=enterprise-edition] +[role=enterprise-edition not-on-aura] [[administration-wait-nowait]] == Wait options From 0e37ed8b36939db09b908874776353e6de239c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 2 Mar 2023 16:28:05 +0100 Subject: [PATCH 145/383] New graph to Predicate Functions page (#449) - new graph - general text edit --- .../ROOT/images/graph_predicate_functions.svg | 108 +---------- .../images/predicate_function_example.svg | 1 + modules/ROOT/pages/functions/predicate.adoc | 172 +++++++++--------- 3 files changed, 93 insertions(+), 188 deletions(-) create mode 100644 modules/ROOT/images/predicate_function_example.svg diff --git a/modules/ROOT/images/graph_predicate_functions.svg b/modules/ROOT/images/graph_predicate_functions.svg index fdad380b8..9b8a6a64d 100644 --- a/modules/ROOT/images/graph_predicate_functions.svg +++ b/modules/ROOT/images/graph_predicate_functions.svg @@ -1,107 +1 @@ - - - - - - -L - - - -N0 - -name = 'Alice' -age = 38 -eyes = 'brown' - - - -N2 - -name = 'Charlie' -age = 53 -eyes = 'green' - - - -N0->N2 - - -KNOWS - - - -N1 - -name = 'Bob' -age = 25 -eyes = 'blue' - - - -N0->N1 - - -KNOWS - - - -N3 - -eyes = 'brown' -liked_colors = [] -name = 'Daniel' -age = 54 - - - -N2->N3 - - -KNOWS - - - -N1->N3 - - -KNOWS - - - -N4 - -eyes = 'blue' -liked_colors = ['pink', 'yellow', 'black'] -name = 'Eskil' -age = 41 - - - -N1->N4 - - -MARRIED - - - -N5 - -eyes = '' -liked_colors = ['blue', 'green'] -alias = 'Frank' -age = 61 - - - -N6 - -Person - - - - - +KNOWSKNOWSKNOWSKNOWSKNOWSACTED_INACTED_INKNOWSPersonname:'Guy Pearce'age:55nationality:'Australian'Personname:'Carrie Anne Moss'age:55nationality:'American'Personname:'Keanu Reeves'age:58nationality:'Canadian'Personname:'Liam Neeson'age:70nationality:'Northern Irish'Personname:'Kathryn Bigelow'age:71nationality:'American'Movietitle:'The Matrix'Personname:'Jessica Chastain'age:45address:'' \ No newline at end of file diff --git a/modules/ROOT/images/predicate_function_example.svg b/modules/ROOT/images/predicate_function_example.svg new file mode 100644 index 000000000..8b6919eb0 --- /dev/null +++ b/modules/ROOT/images/predicate_function_example.svg @@ -0,0 +1 @@ +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)KNOWSKNOWS Keanu Reeves Guy Pearce Carrie Anne Moss \ No newline at end of file diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 7940e7921..8ec6c6768 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -3,33 +3,38 @@ [[query-functions-predicate]] = Predicate functions -[abstract] --- +== Introduction + Predicates are boolean functions that return `true` or `false` for a given set of non-`null` input. They are most commonly used to filter out paths in the `WHERE` part of a query. --- -image:graph_predicate_functions.svg[] +== Example graph + +The following graph is used for the examples below: + +image::graph_predicate_functions.svg[[width="600",role="middle"] + +To recreate it, run the following query against an empty Neo4j database: -//// [source, cypher, role=test-setup] ---- CREATE - (alice {name:'Alice', age: 38, eyes: 'brown'}), - (bob {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel {name: 'Daniel', age: 54, eyes: 'brown', liked_colors: []}), - (eskil {name: 'Eskil', age: 41, eyes: 'blue', liked_colors: ['pink', 'yellow', 'black']}), - (frank {alias: 'Frank', age: 61, eyes: '', liked_colors: ['blue', 'green']}), - (alice)-[:KNOWS]->(bob), - (grace:Person), - (alice)-[:KNOWS]->(charlie), - (bob)-[:KNOWS]->(daniel), - (charlie)-[:KNOWS]->(daniel), - (bob)-[:MARRIED]->(eskil) ----- -//// - + (keanu:Person {name:'Keanu Reeves', age:58, nationality:'Canadian'}), + (carrie:Person {name:'Carrie Anne Moss', age:55, nationality:'American'}), + (liam:Person {name:'Liam Neeson', age:70, nationality:'Northern Irish'}), + (guy:Person {name:'Guy Pearce', age:55, nationality:'Australian'}), + (kathryn:Person {name:'Kathryn Bigelow', age:71, nationality:'American'}), + (jessica:Person {name:'Jessica Chastain', age:45, address:''}), + (theMatrix:Movie {title:'The Matrix'}), + (keanu)-[:KNOWS]->(carrie), + (keanu)-[:KNOWS]->(liam), + (keanu)-[:KNOWS]->(kathryn), + (kathryn)-[:KNOWS]->(jessica), + (carrie)-[:KNOWS]->(guy), + (liam)-[:KNOWS]->(guy), + (keanu)-[:ACTED_IN]->(theMatrix), + (carrie)-[:ACTED_IN]->(theMatrix) +---- [[functions-all]] == all() @@ -81,22 +86,24 @@ However, an implicit conversion will happen for single elements when passing nod .Query [source, cypher, indent=0] ---- -MATCH p = (a)-[*1..3]->(b) +MATCH p = (a)-[*]->(b) WHERE - a.name = 'Alice' - AND b.name = 'Daniel' - AND all(x IN nodes(p) WHERE x.age > 30) + a.name = 'Keanu Reeves' + AND b.name = 'Guy Pearce' + AND all(x IN nodes(p) WHERE x.age < 60) RETURN p ---- -All nodes in the returned paths will have a property `age` with a value larger than `30`. +All nodes in the returned paths will have a property `age` with a value lower than `60`: + +image::predicate_function_example.svg[width="300",role="middle"] .Result [role="queryresult",options="header,footer",cols="1*()) AS is_married + p.name AS name, + exists((p)-[:ACTED_IN]->()) AS has_acted_in_rel ---- -The names of all nodes with the `name` property are returned, along with a boolean (`true` or `false`) indicating if they are married. +This query returns the `name` property of every `Person` node, along with a boolean (`true` or `false`) indicating if those nodes have an `ACTED_IN` relationship in the graph. .Result [role="queryresult",options="header,footer",cols="2*(b) +MATCH p = (n)-[*]->(b) WHERE - n.name = 'Alice' - AND none(x IN nodes(p) WHERE x.age = 25) + n.name = 'Keanu Reeves' + AND none(x IN nodes(p) WHERE x.age > 60) RETURN p ---- -No node in the returned paths has a property `age` with the value `25`. +No node in the returned path has an `age` property with a greater value than `60`: + +image::predicate_function_example.svg[width="300",role="middle"] + .Result [role="queryresult",options="header,footer",cols="1*(b) WHERE - n.name = 'Alice' - AND single(var IN nodes(p) WHERE var.eyes = 'blue') + n.name = 'Keanu Reeves' + AND single(x IN nodes(p) WHERE x.nationality = 'Northern Irish') RETURN p ---- -In every returned path there is exactly one node that has a property `eyes` with the value `'blue'`. +In every returned path there is exactly one node which the `nationality` property value `Northern Irish`: .Result [role="queryresult",options="header,footer",cols="1* Date: Mon, 6 Mar 2023 09:46:09 +0100 Subject: [PATCH 146/383] Describing equijoins in patterns (#453) --- .../ROOT/pages/introduction/uniqueness.adoc | 2 +- modules/ROOT/pages/syntax/patterns.adoc | 102 +++++++++++++++++- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/modules/ROOT/pages/introduction/uniqueness.adoc b/modules/ROOT/pages/introduction/uniqueness.adoc index f25ebc999..bca12046c 100644 --- a/modules/ROOT/pages/introduction/uniqueness.adoc +++ b/modules/ROOT/pages/introduction/uniqueness.adoc @@ -8,7 +8,7 @@ Cypher path matching uses relationship isomorphism, the same relationship cannot be returned more than once in the same result record. -- -**Neo4j Cypher** makes use of **relationship isomorphism** for path matching and is a very effective way of reducing the result set size and preventing infinite traversals. +**Neo4j Cypher** makes use of **relationship isomorphism** for path matching, which is a very effective way of reducing the result set size and preventing infinite traversals. [NOTE] ==== diff --git a/modules/ROOT/pages/syntax/patterns.adoc b/modules/ROOT/pages/syntax/patterns.adoc index 0476f28ce..8a674a0f7 100644 --- a/modules/ROOT/pages/syntax/patterns.adoc +++ b/modules/ROOT/pages/syntax/patterns.adoc @@ -80,6 +80,21 @@ If this is not necessary, then the name may be omitted, as follows: (a)-->()<--(c) ---- +=== Equijoins + +If a node variable is declared more than once in the query, this is called an _equijoin_. +This is an operation that requires each node pattern with the same node variable to be bound to the same node. +For example, the following pattern refers to the same node twice with the variable `a`, forming a cycle: +[source, role=noplay, indent=0] +---- +(a)-->(b)-->(c)-->(a) +---- + +The following pattern refers twice to the same node with the variable `b`, forming a T-shaped pattern: +[source, role=noplay, indent=0] +---- +(a)-->(b)-->(c), (b)-->(e) +---- [[cypher-pattern-label]] == Patterns for labels @@ -180,14 +195,20 @@ As with nodes, the name of the relationship can always be omitted, as exemplifie (a)-[:REL_TYPE]->(b) ---- -It is not possible to use the same name for a relationship multiple times within a pattern due to xref::introduction/uniqueness.adoc#relationship-isomorphism[relationship isomorphism]. +To specify additional constraints, introduce a xref::clauses/where.adoc#relationship-pattern-predicates[relationship pattern predicate]. + +=== Equijoins + +Similar to nodes, it is possible to use the same name for a relationship multiple times within a pattern. +However, due to xref::introduction/uniqueness.adoc#relationship-isomorphism[relationship isomorphism], this will not yield any results if done in the same pattern. +It can be useful across separate `MATCH` statements, though. .Relationship isomorphism ====== -Using the same variable name for relationships multiple times within a pattern is not allowed. +Using the same variable name for relationships multiple times in a query would require that particular relationship to be traversed several times. -The following example is therefore not allowed. +Therefore, the following example will lead to no results: [source, syntax] ---- @@ -196,8 +217,16 @@ The following example is therefore not allowed. ====== -You can specify additional constraints by introducing a xref::clauses/where.adoc#relationship-pattern-predicates[relationship pattern predicate]. +If, on the other hand, the two relationships are spread over two `MATCH`-clauses, then the relationship isomorphism does not pose an obstacle any longer. +The following query would, therefore, return results: +[source, syntax] +---- +MATCH ()-[r:REL_TYPE]-() +MATCH ()-[r:REL_TYPE]-() +---- + +Note, that the two instances of `r` refer to the same relationship. [[cypher-pattern-varlength]] == Variable-length pattern matching @@ -296,8 +325,71 @@ This is a typical example of finding first and second degree friends. Note that variable length relationships cannot be used with `CREATE` and `MERGE`. -Under certain circumstances variable length relationships can be planned with an optimisation, see xref::execution-plans/operators.adoc#query-plan-varlength-expand-pruning[VarLength Expand Pruning] query plan. +Under certain circumstances, variable-length relationships can be planned with an optimisation, see xref::execution-plans/operators.adoc#query-plan-varlength-expand-pruning[VarLength Expand Pruning] query plan. + +=== Equijoins + +Like simple relationships, the variable of variable-length relationships can be used more than once to refer to the same variable-length relationship. Just as with simple relationships, this yields no results if used in the same `MATCH` statement. + +In addition, the results must observe the bounds of both declarations of that variable. +In the following example, the bounds of `r` require it to be of length 2 and therefore only `'Anders'` is returned: + +.Query +[source, cypher, indent=0] +---- +MATCH (me {name: 'Filipa'})-[r:KNOWS*1..2]-(remote_friend) +MATCH ()-[r:KNOWS*2..3]-() +RETURN remote_friend.name +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(me) +RETURN remote_friend.name +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(me) +RETURN remote_friend.name +---- + +.Result +[role="queryresult",options="header,footer",cols="1* Date: Mon, 6 Mar 2023 11:26:40 +0100 Subject: [PATCH 147/383] Fix MATCH examples (#458) Fix to two result tables and shortestpath example --- .../MATCH_shortestpath_with_predicates_example.svg | 2 +- modules/ROOT/pages/clauses/match.adoc | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg b/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg index 8ee63a467..595f9b802 100644 --- a/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg +++ b/modules/ROOT/images/MATCH_shortestpath_with_predicates_example.svg @@ -1 +1 @@ -Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)OLD FRIENDSACTED_INACTED_IN Rob Reiner Michael Douglas Martin Sheen The American Pre… \ No newline at end of file +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)OLD FRIENDSACTED_IN Rob Reiner The American Pre… Martin Sheen \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 5ad15ded2..c330c1234 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -634,7 +634,7 @@ image::MATCH_shortestpath_example.svg[width="400",role="middle"] [role="queryresult",options="header,footer",cols="1* Date: Mon, 6 Mar 2023 13:27:33 +0000 Subject: [PATCH 148/383] Add SHOW SETTING clause and privileges (#440) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds information about SHOW SETTING privileges. --------- Co-authored-by: Therese Magnusson Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- modules/ROOT/content-nav.adoc | 1 + .../privileges_grant_and_deny_syntax.png | Bin 45489 -> 0 bytes ...nt_and_deny_syntax_database_privileges.png | Bin 74599 -> 0 bytes ..._grant_and_deny_syntax_dbms_privileges.png | Bin 112745 -> 0 bytes ..._grant_and_deny_syntax_dbms_privileges.svg | 10 +- modules/ROOT/images/privileges_hierarchy.png | Bin 21405 -> 0 bytes .../images/privileges_hierarchy_database.png | Bin 55044 -> 0 bytes .../ROOT/images/privileges_hierarchy_dbms.png | Bin 150556 -> 0 bytes .../ROOT/images/privileges_hierarchy_dbms.svg | 15 +- .../images/privileges_on_graph_syntax.png | Bin 57154 -> 0 bytes .../access-control/dbms-administration.adoc | 177 +++++++++--- modules/ROOT/pages/clauses/index.adoc | 12 +- .../ROOT/pages/clauses/listing-settings.adoc | 271 ++++++++++++++++++ ...ions-additions-removals-compatibility.adoc | 31 ++ .../execution-plans/operator-summary.adoc | 6 + .../ROOT/pages/execution-plans/operators.adoc | 37 +++ modules/ROOT/pages/index.adoc | 6 + modules/ROOT/pages/keyword-glossary.adoc | 10 + 18 files changed, 509 insertions(+), 67 deletions(-) delete mode 100644 modules/ROOT/images/privileges_grant_and_deny_syntax.png delete mode 100644 modules/ROOT/images/privileges_grant_and_deny_syntax_database_privileges.png delete mode 100644 modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.png delete mode 100644 modules/ROOT/images/privileges_hierarchy.png delete mode 100644 modules/ROOT/images/privileges_hierarchy_database.png delete mode 100644 modules/ROOT/images/privileges_hierarchy_dbms.png delete mode 100644 modules/ROOT/images/privileges_on_graph_syntax.png create mode 100644 modules/ROOT/pages/clauses/listing-settings.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 9e920a6cf..a2063f57c 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -47,6 +47,7 @@ ** xref:clauses/load-csv.adoc[] ** xref:clauses/listing-functions.adoc[] ** xref:clauses/listing-procedures.adoc[] +** xref:clauses/listing-settings.adoc[] ** xref:clauses/transaction-clauses.adoc#query-listing-transactions[SHOW TRANSACTIONS] ** xref:clauses/transaction-clauses.adoc#query-terminate-transactions[TERMINATE TRANSACTIONS] diff --git a/modules/ROOT/images/privileges_grant_and_deny_syntax.png b/modules/ROOT/images/privileges_grant_and_deny_syntax.png deleted file mode 100644 index 6482f2d84566f7e72e56b9a0292465706ef655fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45489 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 diff --git a/modules/ROOT/images/privileges_grant_and_deny_syntax_database_privileges.png b/modules/ROOT/images/privileges_grant_and_deny_syntax_database_privileges.png deleted file mode 100644 index 9f9d72486aa38cc96ad3a7bbfc97f06256c25810..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74599 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¥F@*X2((P&l;T{!s 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%0D67f3VWf2TdXz-M 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 gzx|i; - - - - - - - - + \ No newline at end of file diff --git a/modules/ROOT/images/privileges_hierarchy.png b/modules/ROOT/images/privileges_hierarchy.png deleted file mode 100644 index 3d6db618764c7e986edcb26ef98cb9ccf3db82ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21405 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

3DS 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 Date: Mon, 6 Mar 2023 15:03:05 +0100 Subject: [PATCH 149/383] Remove repeated var-length relationship deprecation (#455) - It's no longer deprecated --- ...ions-additions-removals-compatibility.adoc | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 91a6978fe..f832259e2 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -254,33 +254,6 @@ The existing `UNIQUENESS` filter will now return both node and relationship prop [[cypher-deprecations-additions-removals-5.2]] == Version 5.2 -=== Deprecated features - -[cols="2", options="header"] -|=== -| Feature -| Details - -a| -label:syntax[] -label:deprecated[] -[source, cypher, role="noheader"] ----- -MATCH ()-[r*]-()-[r*]-() ----- ----- -MATCH p = ()-[r*]-(), q = ()-[r*]-() ----- - ----- -MATCH ()-[r*]-() MATCH ()-[r*]-() ----- -a| - -The use of the same relationship variable for multiple variable length relationships is deprecated. - -|=== - === Updated features [cols="2", options="header"] From bd0e667db975fdfbc1d3805113924fc941a31df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 7 Mar 2023 17:13:41 +0100 Subject: [PATCH 150/383] fix exampleId (#463) This PR - removes elementId examples from MATCH page - fixes elementId examples on parameters page --- modules/ROOT/pages/clauses/match.adoc | 140 +++++----------------- modules/ROOT/pages/functions/scalar.adoc | 13 +- modules/ROOT/pages/syntax/parameters.adoc | 8 +- 3 files changed, 38 insertions(+), 123 deletions(-) diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index c330c1234..f9fc71819 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -218,11 +218,11 @@ Returns any nodes connected by an outgoing relationship to the `Person` node wit [[directed-rels-and-variable]] -=== Directed relationships and variable +=== Relationship variables It is possible to introduce a variable to a pattern, either for filtering on relationship properties or to return a relationship. - For example: + .Query [source, cypher, indent=0] ---- @@ -240,6 +240,31 @@ Returns the type of each outgoing relationship from `Oliver Stone`. 1+d|Rows: 1 |=== +=== Match on an undirected relationship + +When a pattern contains a bound relationship, and that relationship pattern does not specify direction, Cypher will try to match the relationship in both directions. + +.Query +[source, cypher, indent=0] +---- +MATCH (a)-[:ACTED_IN {role: 'Bud Fox'}]-(b) +RETURN a, b +---- + +.Result +[role="queryresult",options="header,footer",cols="2*() -WHERE split(elementId(r), ":")[2] = "0" -RETURN r ----- - -The relationship with the elementId `0` is returned. - -.Result -[role="queryresult",options="header,footer",cols="1* Date: Thu, 9 Mar 2023 09:04:45 +0100 Subject: [PATCH 151/383] Update query parameters for test (#445) See related testing code commit: https://github.com/neo4j/docs-testing/commit/decbc314e853d18aca4acc1e3ee6a4454e152453 --- modules/ROOT/pages/aliases.adoc | 30 +++++++++---------- modules/ROOT/pages/clauses/call.adoc | 8 ++--- modules/ROOT/pages/clauses/use.adoc | 4 +-- modules/ROOT/pages/query-tuning/indexes.adoc | 2 +- modules/ROOT/pages/styleguide.adoc | 10 +++---- modules/ROOT/pages/syntax/operators.adoc | 8 ----- modules/ROOT/pages/syntax/parameters.adoc | 10 +++++-- .../ROOT/pages/syntax/working-with-null.adoc | 2 +- 8 files changed, 36 insertions(+), 38 deletions(-) diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 01b64ee67..8fb0bd909 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -1257,6 +1257,8 @@ Delete an alias in a composite database. ---- DROP ALIAS garden.flowers FOR DATABASE ---- + +[source, result, role="noheader"] ---- System updates: 1 Rows: 0 @@ -1438,15 +1440,7 @@ Rows: 0 When using parameters, names cannot be escaped. When the given parameter includes dots, the first dot will be considered the divider for the composite database. -Consider the query: - -.Query -[source, cypher] ----- -CREATE ALIAS $aliasname FOR DATABASE `northwind-graph` ----- - -And parameter +Consider the query with parameter: .Parameters [source, javascript] @@ -1456,6 +1450,12 @@ And parameter } ---- +.Query +[source, cypher] +---- +CREATE ALIAS $aliasname FOR DATABASE `northwind-graph` +---- + If the composite database `mysimplecompositedatabase` exists, then a database alias `myalias` will be created in that composite database. If no such composite database exists, then a database alias `mysimplecompositedatabase.myalias` will be created. @@ -1495,12 +1495,6 @@ The alias `foo.bar` does not belong to the composite database `foo`. Dropping this alias using parameters fails with an error about a missing alias: -.Query -[source, cypher] ----- -DROP ALIAS $aliasname FOR DATABASE ----- - .Parameters [source, javascript] ---- @@ -1509,6 +1503,12 @@ DROP ALIAS $aliasname FOR DATABASE } ---- +.Query +[source, cypher, role=test-fail] +---- +DROP ALIAS $aliasname FOR DATABASE +---- + .Error message [source, output, role="noheader"] ---- diff --git a/modules/ROOT/pages/clauses/call.adoc b/modules/ROOT/pages/clauses/call.adoc index db00a017e..d5ccc3459 100644 --- a/modules/ROOT/pages/clauses/call.adoc +++ b/modules/ROOT/pages/clauses/call.adoc @@ -200,7 +200,7 @@ See xref::syntax/parameters.adoc[], for more about querying with parameters. ---- { "setting": "server.bolt.enabled", - "value": "true", + "value": "true" } ---- @@ -228,16 +228,16 @@ Omission of parantheses is available only in a so-called standalone procedure ca .Parameters -[source,javascript, indent=0] +[source,javascript] ---- { "setting": "server.bolt.enabled", - "value": "true", + "value": "true" } ---- .Query -[source, cypher, role=test-skip] +[source, cypher] ---- CALL dbms.checkConfigValue ---- diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index 946b89523..49987cb67 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -102,9 +102,9 @@ MATCH (n) RETURN n ---- The argument can be any expression that evaluates to the name of a constituent graph - for example a parameter: - +// this can only run under composite databases .Query -[source, cypher] +[source, cypher, role=test-skip] ---- USE graph.byName($graphName) MATCH (n) RETURN n diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index ed6c44e32..4557ba658 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -236,7 +236,7 @@ CONTAINS In some cases, the system cannot determine whether an expression is of type string. For example when the compared value is a parameter: -[source, cypher, role="noheader"] +[source, cypher, role="noheader", role=test-skip] ---- MATCH (n:Label) WHERE n.prop = $param ---- diff --git a/modules/ROOT/pages/styleguide.adoc b/modules/ROOT/pages/styleguide.adoc index 1276b883e..b99be669d 100644 --- a/modules/ROOT/pages/styleguide.adoc +++ b/modules/ROOT/pages/styleguide.adoc @@ -84,7 +84,7 @@ Leave the closing brace on its own line. ---- MATCH (a:A) WHERE - EXISTS { MATCH (a)-->(b:B) WHERE b.prop = $param } + EXISTS { MATCH (a)-->(b:B) WHERE b.prop = 'yellow' } RETURN a.foo ---- + @@ -94,7 +94,7 @@ RETURN a.foo MATCH (a:A) WHERE EXISTS {MATCH (a)-->(b:B) -WHERE b.prop = $param} +WHERE b.prop = 'yellow'} RETURN a.foo ---- + @@ -104,7 +104,7 @@ RETURN a.foo MATCH (a:A) WHERE EXISTS { MATCH (a)-->(b:B) - WHERE b.prop = $param + WHERE b.prop = 'yellow' } RETURN a.foo ---- @@ -190,7 +190,7 @@ RETURN b1 AND b2 ** parameters + .Bad -[source, cypher] +[source, cypher, role=test-skip] ---- CREATE (N {Prop: 0}) WITH RAND() AS Rand, $pArAm AS MAP @@ -198,7 +198,7 @@ RETURN Rand, MAP.property_key, Count(N) ---- + .Good -[source, cypher] +[source, cypher, role=test-skip] ---- CREATE (n {prop: 0}) WITH rand() AS rand, $param AS map diff --git a/modules/ROOT/pages/syntax/operators.adoc b/modules/ROOT/pages/syntax/operators.adoc index 424a62003..92b0bbb55 100644 --- a/modules/ROOT/pages/syntax/operators.adoc +++ b/modules/ROOT/pages/syntax/operators.adoc @@ -958,14 +958,6 @@ RETURN names[$myIndex] AS result `IN` can be used in conjunction with `[]` to test whether an element exists in a nested list: -.Parameters -[source,javascript, indent=0] ----- -{ - "myIndex" : 1 -} ----- - .Query [source, cypher] ---- diff --git a/modules/ROOT/pages/syntax/parameters.adoc b/modules/ROOT/pages/syntax/parameters.adoc index 9f43156c0..9fd772f96 100644 --- a/modules/ROOT/pages/syntax/parameters.adoc +++ b/modules/ROOT/pages/syntax/parameters.adoc @@ -255,7 +255,7 @@ RETURN n.name [source,javascript, indent=0] ---- { - "ids" : [ “4:1fd57deb-355d-47bb-a80a-d39ac2d2bcdb:0", “4:1fd57deb-355d-47bb-a80a-d39ac2d2bcdb:1” ] + "ids" : [ "4:1fd57deb-355d-47bb-a80a-d39ac2d2bcdb:0", "4:1fd57deb-355d-47bb-a80a-d39ac2d2bcdb:1" ] } ---- @@ -271,13 +271,19 @@ RETURN n.name [[cypher-parameters-call-procedure]] == Calling procedures +//// +[source, cypher, role=test-setup] +---- +CREATE INDEX My_index FOR (c:Country) on c.name +---- +//// // example with parameter procedure call .Parameters [source,javascript, indent=0] ---- { - "indexname" : "My index" + "indexname" : "My_index" } ---- diff --git a/modules/ROOT/pages/syntax/working-with-null.adoc b/modules/ROOT/pages/syntax/working-with-null.adoc index 5fb5ae299..05cc7c144 100644 --- a/modules/ROOT/pages/syntax/working-with-null.adoc +++ b/modules/ROOT/pages/syntax/working-with-null.adoc @@ -89,7 +89,7 @@ Accessing a list or a map with `null` will result in `null`: Using parameters to pass in the bounds, such as `a[$lower..$upper]`, may result in a `null` for the lower or upper bound (or both). The following workaround will prevent this from happening by setting the absolute minimum and maximum bound values: -[source, cypher, role=noplay, indent=0] +[source, syntax, role=noheader] ---- a[coalesce($lower,0)..coalesce($upper,size(a))] ---- From f492e40654601a00e7f02e90b0321c850febbbc4 Mon Sep 17 00:00:00 2001 From: Jane Yang Date: Thu, 9 Mar 2023 13:18:47 +0100 Subject: [PATCH 152/383] Enable load csv test in docs (#452) - Enable query with 'LOAD CSV' in test. - Skip those block has 'cypher-shell' tag. - Add csv files under /examples folder TC job result: https://live.neo4j-build.io/buildConfiguration/Documentation_DocsTools_CypherTe_CypherTestJane/15775722?hideProblemsFromDependencies=false&hideTestsFromDependencies=false PR for update test code: https://github.com/neo4j/docs-testing/pull/15 Screenshot 2023-03-01 at 11 58 10 --- modules/ROOT/examples/README.md | 3 + modules/ROOT/examples/actors.csv | 173 +++++++ .../ROOT/examples/artists-fieldterminator.csv | 4 + .../examples/artists-with-escaped-char.csv | 1 + .../ROOT/examples/artists-with-headers.csv | 5 + modules/ROOT/examples/artists.csv | 4 + modules/ROOT/examples/directors.csv | 45 ++ modules/ROOT/examples/friends.csv | 5 + modules/ROOT/examples/movies.csv | 248 +++++++++ modules/ROOT/pages/clauses/load-csv.adoc | 31 +- .../ROOT/pages/functions/temporal/index.adoc | 2 +- .../pages/query-tuning/advanced-example.adoc | 490 +++++------------- .../pages/query-tuning/basic-example.adoc | 194 +++---- modules/ROOT/pages/syntax/temporal.adoc | 2 +- 14 files changed, 705 insertions(+), 502 deletions(-) create mode 100644 modules/ROOT/examples/README.md create mode 100644 modules/ROOT/examples/actors.csv create mode 100644 modules/ROOT/examples/artists-fieldterminator.csv create mode 100644 modules/ROOT/examples/artists-with-escaped-char.csv create mode 100644 modules/ROOT/examples/artists-with-headers.csv create mode 100644 modules/ROOT/examples/artists.csv create mode 100644 modules/ROOT/examples/directors.csv create mode 100644 modules/ROOT/examples/friends.csv create mode 100644 modules/ROOT/examples/movies.csv diff --git a/modules/ROOT/examples/README.md b/modules/ROOT/examples/README.md new file mode 100644 index 000000000..1efa1c085 --- /dev/null +++ b/modules/ROOT/examples/README.md @@ -0,0 +1,3 @@ +# CSV files for Cypher testing +- *.csv files here are the testing resources for `LOAD CSV` command in docs. Before test run, copy those file under the `/import` folder in your neo4j database. +- if you changing the content under `[source, csv]` block, please remember to take a look at those csv files here as well. \ No newline at end of file diff --git a/modules/ROOT/examples/actors.csv b/modules/ROOT/examples/actors.csv new file mode 100644 index 000000000..695f453dd --- /dev/null +++ b/modules/ROOT/examples/actors.csv @@ -0,0 +1,173 @@ +title,roles,name,born +Something's Gotta Give,Julian Mercer,Keanu Reeves,1964 +Johnny Mnemonic,Johnny Mnemonic,Keanu Reeves,1964 +The Replacements,Shane Falco,Keanu Reeves,1964 +The Devil's Advocate,Kevin Lomax,Keanu Reeves,1964 +The Matrix Revolutions,Neo,Keanu Reeves,1964 +The Matrix Reloaded,Neo,Keanu Reeves,1964 +The Matrix,Neo,Keanu Reeves,1964 +The Matrix Revolutions,Trinity,Carrie-Anne Moss,1967 +The Matrix Reloaded,Trinity,Carrie-Anne Moss,1967 +The Matrix,Trinity,Carrie-Anne Moss,1967 +The Matrix Revolutions,Morpheus,Laurence Fishburne,1961 +The Matrix Reloaded,Morpheus,Laurence Fishburne,1961 +The Matrix,Morpheus,Laurence Fishburne,1961 +V for Vendetta,V,Hugo Weaving,1960 +Cloud Atlas,Bill Smoke;Haskell Moore;Tadeusz Kesselring;Nurse Noakes;Boardman Mephi;Old Georgie,Hugo Weaving,1960 +The Matrix Revolutions,Agent Smith,Hugo Weaving,1960 +The Matrix Reloaded,Agent Smith,Hugo Weaving,1960 +The Matrix,Agent Smith,Hugo Weaving,1960 +The Matrix,Emil,Emil Eifrem,1978 +That Thing You Do,Tina,Charlize Theron,1975 +The Devil's Advocate,Mary Ann Lomax,Charlize Theron,1975 +The Devil's Advocate,John Milton,Al Pacino,1940 +Jerry Maguire,Jerry Maguire,Tom Cruise,1962 +Top Gun,Maverick,Tom Cruise,1962 +A Few Good Men,Lt. Daniel Kaffee,Tom Cruise,1962 +Something's Gotta Give,Harry Sanborn,Jack Nicholson,1937 +One Flew Over the Cuckoo's Nest,Randle McMurphy,Jack Nicholson,1937 +Hoffa,Hoffa,Jack Nicholson,1937 +As Good as It Gets,Melvin Udall,Jack Nicholson,1937 +A Few Good Men,Col. Nathan R. Jessup,Jack Nicholson,1937 +A Few Good Men,Lt. Cdr. JoAnne Galloway,Demi Moore,1962 +Apollo 13,Jack Swigert,Kevin Bacon,1958 +Frost/Nixon,Jack Brennan,Kevin Bacon,1958 +A Few Good Men,Capt. Jack Ross,Kevin Bacon,1958 +Stand By Me,Ace Merrill,Kiefer Sutherland,1966 +A Few Good Men,Lt. Jonathan Kendrick,Kiefer Sutherland,1966 +A Few Good Men,Cpl. Jeffrey Barnes,Noah Wyle,1971 +What Dreams May Come,Albert Lewis,Cuba Gooding Jr.,1968 +As Good as It Gets,Frank Sachs,Cuba Gooding Jr.,1968 +Jerry Maguire,Rod Tidwell,Cuba Gooding Jr.,1968 +A Few Good Men,Cpl. Carl Hammaker,Cuba Gooding Jr.,1968 +A Few Good Men,Lt. Sam Weinberg,Kevin Pollak,1957 +Hoffa,Frank Fitzsimmons,J.T. Walsh,1943 +A Few Good Men,Lt. Col. Matthew Andrew Markinson,J.T. Walsh,1943 +A Few Good Men,Pfc. Louden Downey,James Marshall,1967 +A Few Good Men,Dr. Stone,Christopher Guest,1948 +A Few Good Men,Man in Bar,Aaron Sorkin,1961 +Top Gun,Charlie,Kelly McGillis,1957 +Top Gun,Iceman,Val Kilmer,1959 +Top Gun,Goose,Anthony Edwards,1962 +Top Gun,Viper,Tom Skerritt,1933 +When Harry Met Sally,Sally Albright,Meg Ryan,1961 +Joe Versus the Volcano,DeDe;Angelica Graynamore;Patricia Graynamore,Meg Ryan,1961 +Sleepless in Seattle,Annie Reed,Meg Ryan,1961 +You've Got Mail,Kathleen Kelly,Meg Ryan,1961 +Top Gun,Carole,Meg Ryan,1961 +Jerry Maguire,Dorothy Boyd,Renee Zellweger,1969 +Jerry Maguire,Avery Bishop,Kelly Preston,1962 +Stand By Me,Vern Tessio,Jerry O'Connell,1974 +Jerry Maguire,Frank Cushman,Jerry O'Connell,1974 +Jerry Maguire,Bob Sugar,Jay Mohr,1970 +The Green Mile,Jan Edgecomb,Bonnie Hunt,1961 +Jerry Maguire,Laurel Boyd,Bonnie Hunt,1961 +Jerry Maguire,Marcee Tidwell,Regina King,1971 +Jerry Maguire,Ray Boyd,Jonathan Lipnicki,1990 +Stand By Me,Chris Chambers,River Phoenix,1970 +Stand By Me,Teddy Duchamp,Corey Feldman,1971 +Stand By Me,Gordie Lachance,Wil Wheaton,1972 +Stand By Me,Denny Lachance,John Cusack,1966 +RescueDawn,Admiral,Marshall Bell,1942 +Stand By Me,Mr. Lachance,Marshall Bell,1942 +Cast Away,Kelly Frears,Helen Hunt,1963 +Twister,Dr. Jo Harding,Helen Hunt,1963 +As Good as It Gets,Carol Connelly,Helen Hunt,1963 +You've Got Mail,Frank Navasky,Greg Kinnear,1963 +As Good as It Gets,Simon Bishop,Greg Kinnear,1963 +What Dreams May Come,Simon Bishop,Annabella Sciorra,1960 +Snow Falling on Cedars,Nels Gudmundsson,Max von Sydow,1929 +What Dreams May Come,The Tracker,Max von Sydow,1929 +What Dreams May Come,The Face,Werner Herzog,1942 +Bicentennial Man,Andrew Marin,Robin Williams,1951 +The Birdcage,Armand Goldman,Robin Williams,1951 +What Dreams May Come,Chris Nielsen,Robin Williams,1951 +Snow Falling on Cedars,Ishmael Chambers,Ethan Hawke,1970 +Ninja Assassin,Takeshi,Rick Yune,1971 +Snow Falling on Cedars,Kazuo Miyamoto,Rick Yune,1971 +The Green Mile,Warden Hal Moores,James Cromwell,1940 +Snow Falling on Cedars,Judge Fielding,James Cromwell,1940 +You've Got Mail,Patricia Eden,Parker Posey,1968 +You've Got Mail,Kevin Jackson,Dave Chappelle,1973 +RescueDawn,Duane,Steve Zahn,1967 +You've Got Mail,George Pappas,Steve Zahn,1967 +A League of Their Own,Jimmy Dugan,Tom Hanks,1956 +The Polar Express,Hero Boy;Father;Conductor;Hobo;Scrooge;Santa Claus,Tom Hanks,1956 +Charlie Wilson's War,Rep. Charlie Wilson,Tom Hanks,1956 +Cast Away,Chuck Noland,Tom Hanks,1956 +Apollo 13,Jim Lovell,Tom Hanks,1956 +The Green Mile,Paul Edgecomb,Tom Hanks,1956 +The Da Vinci Code,Dr. Robert Langdon,Tom Hanks,1956 +Cloud Atlas,Zachry;Dr. Henry Goose;Isaac Sachs;Dermot Hoggins,Tom Hanks,1956 +That Thing You Do,Mr. White,Tom Hanks,1956 +Joe Versus the Volcano,Joe Banks,Tom Hanks,1956 +Sleepless in Seattle,Sam Baldwin,Tom Hanks,1956 +You've Got Mail,Joe Fox,Tom Hanks,1956 +Sleepless in Seattle,Suzy,Rita Wilson,1956 +Sleepless in Seattle,Walter,Bill Pullman,1953 +Sleepless in Seattle,Greg,Victor Garber,1949 +A League of Their Own,Doris Murphy,Rosie O'Donnell,1962 +Sleepless in Seattle,Becky,Rosie O'Donnell,1962 +The Birdcage,Albert Goldman,Nathan Lane,1956 +Joe Versus the Volcano,Baw,Nathan Lane,1956 +When Harry Met Sally,Harry Burns,Billy Crystal,1948 +When Harry Met Sally,Marie,Carrie Fisher,1956 +When Harry Met Sally,Jess,Bruno Kirby,1949 +That Thing You Do,Faye Dolan,Liv Tyler,1977 +The Replacements,Annabelle Farrell,Brooke Langton,1970 +Unforgiven,Little Bill Daggett,Gene Hackman,1930 +The Birdcage,Sen. Kevin Keeley,Gene Hackman,1930 +The Replacements,Jimmy McGinty,Gene Hackman,1930 +The Replacements,Clifford Franklin,Orlando Jones,1968 +RescueDawn,Dieter Dengler,Christian Bale,1974 +Twister,Eddie,Zach Grenier,1954 +RescueDawn,Squad Leader,Zach Grenier,1954 +Unforgiven,English Bob,Richard Harris,1930 +Unforgiven,Bill Munny,Clint Eastwood,1930 +Johnny Mnemonic,Takahashi,Takeshi Kitano,1947 +Johnny Mnemonic,Jane,Dina Meyer,1968 +Johnny Mnemonic,J-Bone,Ice-T,1958 +Cloud Atlas,Luisa Rey;Jocasta Ayrs;Ovid;Meronym,Halle Berry,1966 +Cloud Atlas,Vyvyan Ayrs;Captain Molyneux;Timothy Cavendish,Jim Broadbent,1949 +The Da Vinci Code,Sir Leight Teabing,Ian McKellen,1939 +The Da Vinci Code,Sophie Neveu,Audrey Tautou,1976 +The Da Vinci Code,Silas,Paul Bettany,1971 +V for Vendetta,Evey Hammond,Natalie Portman,1981 +V for Vendetta,Eric Finch,Stephen Rea,1946 +V for Vendetta,High Chancellor Adam Sutler,John Hurt,1940 +Ninja Assassin,Ryan Maslow,Ben Miles,1967 +Speed Racer,Cass Jones,Ben Miles,1967 +V for Vendetta,Dascomb,Ben Miles,1967 +Speed Racer,Speed Racer,Emile Hirsch,1985 +Speed Racer,Pops,John Goodman,1960 +Speed Racer,Mom,Susan Sarandon,1946 +Speed Racer,Racer X,Matthew Fox,1966 +Speed Racer,Trixie,Christina Ricci,1980 +Ninja Assassin,Raizo,Rain,1982 +Speed Racer,Taejo Togokahn,Rain,1982 +Ninja Assassin,Mika Coretti,Naomie Harris,null +The Green Mile,John Coffey,Michael Clarke Duncan,1957 +The Green Mile,Brutus 'Brutal' Howell,David Morse,1953 +Frost/Nixon,"James Reston, Jr.",Sam Rockwell,1968 +The Green Mile,'Wild Bill' Wharton,Sam Rockwell,1968 +Apollo 13,Ken Mattingly,Gary Sinise,1955 +The Green Mile,Burt Hammersmith,Gary Sinise,1955 +The Green Mile,Melinda Moores,Patricia Clarkson,1959 +Frost/Nixon,Richard Nixon,Frank Langella,1938 +Frost/Nixon,David Frost,Michael Sheen,1969 +Bicentennial Man,Rupert Burns,Oliver Platt,1960 +Frost/Nixon,Bob Zelnick,Oliver Platt,1960 +One Flew Over the Cuckoo's Nest,Martini,Danny DeVito,1944 +Hoffa,Robert 'Bobby' Ciaro,Danny DeVito,1944 +Hoffa,Peter 'Pete' Connelly,John C. Reilly,1965 +Apollo 13,Gene Kranz,Ed Harris,1950 +A League of Their Own,Bob Hinson,Bill Paxton,1955 +Twister,Bill Harding,Bill Paxton,1955 +Apollo 13,Fred Haise,Bill Paxton,1955 +Charlie Wilson's War,Gust Avrakotos,Philip Seymour Hoffman,1967 +Twister,Dustin 'Dusty' Davis,Philip Seymour Hoffman,1967 +Something's Gotta Give,Erica Barry,Diane Keaton,1946 +Charlie Wilson's War,Joanne Herring,Julia Roberts,1967 +A League of Their Own,'All the Way' Mae Mordabito,Madonna,1954 +A League of Their Own,Dottie Hinson,Geena Davis,1956 +A League of Their Own,Kit Keller,Lori Petty,1963 diff --git a/modules/ROOT/examples/artists-fieldterminator.csv b/modules/ROOT/examples/artists-fieldterminator.csv new file mode 100644 index 000000000..a4f3fb00b --- /dev/null +++ b/modules/ROOT/examples/artists-fieldterminator.csv @@ -0,0 +1,4 @@ +1;ABBA;1992 +2;Roxette;1986 +3;Europe;1979 +4;The Cardigans;1992 diff --git a/modules/ROOT/examples/artists-with-escaped-char.csv b/modules/ROOT/examples/artists-with-escaped-char.csv new file mode 100644 index 000000000..30d7376f2 --- /dev/null +++ b/modules/ROOT/examples/artists-with-escaped-char.csv @@ -0,0 +1 @@ +1,"The """"Symbol""""",1992 diff --git a/modules/ROOT/examples/artists-with-headers.csv b/modules/ROOT/examples/artists-with-headers.csv new file mode 100644 index 000000000..7add2665e --- /dev/null +++ b/modules/ROOT/examples/artists-with-headers.csv @@ -0,0 +1,5 @@ +Id,Name,Year +1,ABBA,1992 +2,Roxette,1986 +3,Europe,1979 +4,The Cardigans,1992 diff --git a/modules/ROOT/examples/artists.csv b/modules/ROOT/examples/artists.csv new file mode 100644 index 000000000..9ef4ddf39 --- /dev/null +++ b/modules/ROOT/examples/artists.csv @@ -0,0 +1,4 @@ +1,ABBA,1992 +2,Roxette,1986 +3,Europe,1979 +4,The Cardigans,1992 diff --git a/modules/ROOT/examples/directors.csv b/modules/ROOT/examples/directors.csv new file mode 100644 index 000000000..1a7afd88c --- /dev/null +++ b/modules/ROOT/examples/directors.csv @@ -0,0 +1,45 @@ +title,name,born +Speed Racer,Andy Wachowski,1967 +Cloud Atlas,Andy Wachowski,1967 +The Matrix Revolutions,Andy Wachowski,1967 +The Matrix Reloaded,Andy Wachowski,1967 +The Matrix,Andy Wachowski,1967 +Speed Racer,Lana Wachowski,1965 +Cloud Atlas,Lana Wachowski,1965 +The Matrix Revolutions,Lana Wachowski,1965 +The Matrix Reloaded,Lana Wachowski,1965 +The Matrix,Lana Wachowski,1965 +The Devil's Advocate,Taylor Hackford,1944 +Ninja Assassin,James Marshall,1967 +V for Vendetta,James Marshall,1967 +When Harry Met Sally,Rob Reiner,1947 +Stand By Me,Rob Reiner,1947 +A Few Good Men,Rob Reiner,1947 +Top Gun,Tony Scott,1944 +Jerry Maguire,Cameron Crowe,1957 +As Good as It Gets,James L. Brooks,1940 +RescueDawn,Werner Herzog,1942 +What Dreams May Come,Vincent Ward,1956 +Snow Falling on Cedars,Scott Hicks,1953 +That Thing You Do,Tom Hanks,1956 +Sleepless in Seattle,Nora Ephron,1941 +You've Got Mail,Nora Ephron,1941 +Joe Versus the Volcano,John Patrick Stanley,1950 +The Replacements,Howard Deutch,1950 +Charlie Wilson's War,Mike Nichols,1931 +The Birdcage,Mike Nichols,1931 +Unforgiven,Clint Eastwood,1930 +Johnny Mnemonic,Robert Longo,1953 +Cloud Atlas,Tom Tykwer,1965 +Apollo 13,Ron Howard,1954 +Frost/Nixon,Ron Howard,1954 +The Da Vinci Code,Ron Howard,1954 +The Green Mile,Frank Darabont,1959 +Hoffa,Danny DeVito,1944 +Twister,Jan de Bont,1943 +The Polar Express,Robert Zemeckis,1951 +Cast Away,Robert Zemeckis,1951 +One Flew Over the Cuckoo's Nest,Milos Forman,1932 +Something's Gotta Give,Nancy Meyers,1949 +Bicentennial Man,Chris Columbus,1958 +A League of Their Own,Penny Marshall,1943 diff --git a/modules/ROOT/examples/friends.csv b/modules/ROOT/examples/friends.csv new file mode 100644 index 000000000..bc66cb1ef --- /dev/null +++ b/modules/ROOT/examples/friends.csv @@ -0,0 +1,5 @@ +1,Bill,26 +2,Max,27 +3,Anna,22 +4,Gladys,29 +5,Summer,24 diff --git a/modules/ROOT/examples/movies.csv b/modules/ROOT/examples/movies.csv new file mode 100644 index 000000000..ed5890a8e --- /dev/null +++ b/modules/ROOT/examples/movies.csv @@ -0,0 +1,248 @@ +title,released,tagline +Something's Gotta Give,1975,null +Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town +The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" +The Devil's Advocate,1997,Evil has its winning ways +The Matrix Revolutions,2003,Everything that has a beginning has an end +The Matrix Reloaded,2003,Free your mind +The Matrix,1999,Welcome to the Real World +The Matrix Revolutions,2003,Everything that has a beginning has an end +The Matrix Reloaded,2003,Free your mind +The Matrix,1999,Welcome to the Real World +The Matrix Revolutions,2003,Everything that has a beginning has an end +The Matrix Reloaded,2003,Free your mind +The Matrix,1999,Welcome to the Real World +V for Vendetta,2006,Freedom! Forever! +Cloud Atlas,2012,Everything is connected +The Matrix Revolutions,2003,Everything that has a beginning has an end +The Matrix Reloaded,2003,Free your mind +The Matrix,1999,Welcome to the Real World +Speed Racer,2008,Speed has no limits +Cloud Atlas,2012,Everything is connected +The Matrix Revolutions,2003,Everything that has a beginning has an end +The Matrix Reloaded,2003,Free your mind +The Matrix,1999,Welcome to the Real World +Ninja Assassin,2009,Prepare to enter a secret world of assassins +V for Vendetta,2006,Freedom! Forever! +Speed Racer,2008,Speed has no limits +V for Vendetta,2006,Freedom! Forever! +Speed Racer,2008,Speed has no limits +Cloud Atlas,2012,Everything is connected +The Matrix Revolutions,2003,Everything that has a beginning has an end +The Matrix Reloaded,2003,Free your mind +The Matrix,1999,Welcome to the Real World +Ninja Assassin,2009,Prepare to enter a secret world of assassins +V for Vendetta,2006,Freedom! Forever! +Speed Racer,2008,Speed has no limits +V for Vendetta,2006,Freedom! Forever! +Ninja Assassin,2009,Prepare to enter a secret world of assassins +Speed Racer,2008,Speed has no limits +V for Vendetta,2006,Freedom! Forever! +The Matrix Revolutions,2003,Everything that has a beginning has an end +The Matrix Reloaded,2003,Free your mind +The Matrix,1999,Welcome to the Real World +The Matrix,1999,Welcome to the Real World +That Thing You Do,1996,In every life there comes a time when that thing you dream becomes that thing you do +The Devil's Advocate,1997,Evil has its winning ways +The Devil's Advocate,1997,Evil has its winning ways +The Devil's Advocate,1997,Evil has its winning ways +Jerry Maguire,2000,The rest of his life begins now. +Top Gun,1986,"I feel the need, the need for speed." +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +Something's Gotta Give,1975,null +One Flew Over the Cuckoo's Nest,1975,"If he's crazy, what does that make you?" +Hoffa,1992,He didn't want law. He wanted justice. +As Good as It Gets,1997,A comedy from the heart that goes for the throat. +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +Apollo 13,1995,"Houston, we have a problem." +Frost/Nixon,2008,400 million people were waiting for the truth. +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +What Dreams May Come,1998,After life there is more. The end is just the beginning. +As Good as It Gets,1997,A comedy from the heart that goes for the throat. +Jerry Maguire,2000,The rest of his life begins now. +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +Hoffa,1992,He didn't want law. He wanted justice. +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +Ninja Assassin,2009,Prepare to enter a secret world of assassins +V for Vendetta,2006,Freedom! Forever! +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +When Harry Met Sally,1998,At odds in life... in love on-line. +Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +When Harry Met Sally,1998,At odds in life... in love on-line. +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." +Top Gun,1986,"I feel the need, the need for speed." +Top Gun,1986,"I feel the need, the need for speed." +Top Gun,1986,"I feel the need, the need for speed." +Top Gun,1986,"I feel the need, the need for speed." +When Harry Met Sally,1998,At odds in life... in love on-line. +Joe Versus the Volcano,1990,"A story of love, lava and burning desire." +Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" +You've Got Mail,1998,At odds in life... in love on-line. +Top Gun,1986,"I feel the need, the need for speed." +Top Gun,1986,"I feel the need, the need for speed." +Top Gun,1986,"I feel the need, the need for speed." +Jerry Maguire,2000,The rest of his life begins now. +Jerry Maguire,2000,The rest of his life begins now. +Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." +Jerry Maguire,2000,The rest of his life begins now. +Jerry Maguire,2000,The rest of his life begins now. +The Green Mile,1999,Walk a mile you'll never forget. +Jerry Maguire,2000,The rest of his life begins now. +Jerry Maguire,2000,The rest of his life begins now. +Jerry Maguire,2000,The rest of his life begins now. +Jerry Maguire,2000,The rest of his life begins now. +Jerry Maguire,2000,The rest of his life begins now. +Jerry Maguire,2000,The rest of his life begins now. +Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." +Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." +Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." +Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." +RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom +Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." +Cast Away,2000,"At the edge of the world, his journey begins." +Twister,1996,Don't Breathe. Don't Look Back. +As Good as It Gets,1997,A comedy from the heart that goes for the throat. +You've Got Mail,1998,At odds in life... in love on-line. +As Good as It Gets,1997,A comedy from the heart that goes for the throat. +As Good as It Gets,1997,A comedy from the heart that goes for the throat. +What Dreams May Come,1998,After life there is more. The end is just the beginning. +Snow Falling on Cedars,1999,First loves last. Forever. +What Dreams May Come,1998,After life there is more. The end is just the beginning. +What Dreams May Come,1998,After life there is more. The end is just the beginning. +RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom +Bicentennial Man,1999,One robot's 200 year journey to become an ordinary man. +The Birdcage,1996,Come as you are +What Dreams May Come,1998,After life there is more. The end is just the beginning. +What Dreams May Come,1998,After life there is more. The end is just the beginning. +Snow Falling on Cedars,1999,First loves last. Forever. +Ninja Assassin,2009,Prepare to enter a secret world of assassins +Snow Falling on Cedars,1999,First loves last. Forever. +The Green Mile,1999,Walk a mile you'll never forget. +Snow Falling on Cedars,1999,First loves last. Forever. +Snow Falling on Cedars,1999,First loves last. Forever. +You've Got Mail,1998,At odds in life... in love on-line. +You've Got Mail,1998,At odds in life... in love on-line. +RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom +You've Got Mail,1998,At odds in life... in love on-line. +A League of Their Own,1992,Once in a lifetime you get a chance to do something different. +The Polar Express,2004,This Holiday Season… Believe +Charlie Wilson's War,2007,A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire. +Cast Away,2000,"At the edge of the world, his journey begins." +Apollo 13,1995,"Houston, we have a problem." +The Green Mile,1999,Walk a mile you'll never forget. +The Da Vinci Code,2006,Break The Codes +Cloud Atlas,2012,Everything is connected +That Thing You Do,1996,In every life there comes a time when that thing you dream becomes that thing you do +Joe Versus the Volcano,1990,"A story of love, lava and burning desire." +Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" +You've Got Mail,1998,At odds in life... in love on-line. +That Thing You Do,1996,In every life there comes a time when that thing you dream becomes that thing you do +Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" +You've Got Mail,1998,At odds in life... in love on-line. +When Harry Met Sally,1998,At odds in life... in love on-line. +When Harry Met Sally,1998,At odds in life... in love on-line. +Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" +Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" +Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" +A League of Their Own,1992,Once in a lifetime you get a chance to do something different. +Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" +Joe Versus the Volcano,1990,"A story of love, lava and burning desire." +The Birdcage,1996,Come as you are +Joe Versus the Volcano,1990,"A story of love, lava and burning desire." +When Harry Met Sally,1998,At odds in life... in love on-line. +When Harry Met Sally,1998,At odds in life... in love on-line. +When Harry Met Sally,1998,At odds in life... in love on-line. +That Thing You Do,1996,In every life there comes a time when that thing you dream becomes that thing you do +The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" +Unforgiven,1992,"It's a hell of a thing, killing a man" +The Birdcage,1996,Come as you are +The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" +The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" +The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" +RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom +Twister,1996,Don't Breathe. Don't Look Back. +RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom +Charlie Wilson's War,2007,A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire. +The Birdcage,1996,Come as you are +Unforgiven,1992,"It's a hell of a thing, killing a man" +Unforgiven,1992,"It's a hell of a thing, killing a man" +Unforgiven,1992,"It's a hell of a thing, killing a man" +Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town +Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town +Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town +Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town +Cloud Atlas,2012,Everything is connected +Cloud Atlas,2012,Everything is connected +Cloud Atlas,2012,Everything is connected +The Da Vinci Code,2006,Break The Codes +The Da Vinci Code,2006,Break The Codes +The Da Vinci Code,2006,Break The Codes +Apollo 13,1995,"Houston, we have a problem." +Frost/Nixon,2008,400 million people were waiting for the truth. +The Da Vinci Code,2006,Break The Codes +V for Vendetta,2006,Freedom! Forever! +V for Vendetta,2006,Freedom! Forever! +V for Vendetta,2006,Freedom! Forever! +Ninja Assassin,2009,Prepare to enter a secret world of assassins +Speed Racer,2008,Speed has no limits +V for Vendetta,2006,Freedom! Forever! +Speed Racer,2008,Speed has no limits +Speed Racer,2008,Speed has no limits +Speed Racer,2008,Speed has no limits +Speed Racer,2008,Speed has no limits +Speed Racer,2008,Speed has no limits +Ninja Assassin,2009,Prepare to enter a secret world of assassins +Speed Racer,2008,Speed has no limits +Ninja Assassin,2009,Prepare to enter a secret world of assassins +The Green Mile,1999,Walk a mile you'll never forget. +The Green Mile,1999,Walk a mile you'll never forget. +Frost/Nixon,2008,400 million people were waiting for the truth. +The Green Mile,1999,Walk a mile you'll never forget. +Apollo 13,1995,"Houston, we have a problem." +The Green Mile,1999,Walk a mile you'll never forget. +The Green Mile,1999,Walk a mile you'll never forget. +The Green Mile,1999,Walk a mile you'll never forget. +Frost/Nixon,2008,400 million people were waiting for the truth. +Frost/Nixon,2008,400 million people were waiting for the truth. +Bicentennial Man,1999,One robot's 200 year journey to become an ordinary man. +Frost/Nixon,2008,400 million people were waiting for the truth. +One Flew Over the Cuckoo's Nest,1975,"If he's crazy, what does that make you?" +Hoffa,1992,He didn't want law. He wanted justice. +Hoffa,1992,He didn't want law. He wanted justice. +Hoffa,1992,He didn't want law. He wanted justice. +Apollo 13,1995,"Houston, we have a problem." +A League of Their Own,1992,Once in a lifetime you get a chance to do something different. +Twister,1996,Don't Breathe. Don't Look Back. +Apollo 13,1995,"Houston, we have a problem." +Charlie Wilson's War,2007,A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire. +Twister,1996,Don't Breathe. Don't Look Back. +Twister,1996,Don't Breathe. Don't Look Back. +The Polar Express,2004,This Holiday Season… Believe +Cast Away,2000,"At the edge of the world, his journey begins." +One Flew Over the Cuckoo's Nest,1975,"If he's crazy, what does that make you?" +Something's Gotta Give,1975,null +Something's Gotta Give,1975,null +Something's Gotta Give,1975,null +Something's Gotta Give,1975,null +Bicentennial Man,1999,One robot's 200 year journey to become an ordinary man. +Charlie Wilson's War,2007,A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire. +A League of Their Own,1992,Once in a lifetime you get a chance to do something different. +A League of Their Own,1992,Once in a lifetime you get a chance to do something different. +A League of Their Own,1992,Once in a lifetime you get a chance to do something different. +A League of Their Own,1992,Once in a lifetime you get a chance to do something different. +The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" +The Da Vinci Code,2006,Break The Codes +The Birdcage,1996,Come as you are +Unforgiven,1992,"It's a hell of a thing, killing a man" +The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" +Cloud Atlas,2012,Everything is connected +The Da Vinci Code,2006,Break The Codes +The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" diff --git a/modules/ROOT/pages/clauses/load-csv.adoc b/modules/ROOT/pages/clauses/load-csv.adoc index a571bd526..003367ddf 100644 --- a/modules/ROOT/pages/clauses/load-csv.adoc +++ b/modules/ROOT/pages/clauses/load-csv.adoc @@ -95,7 +95,7 @@ A new node with the `Artist` label is created for each row in the CSV file. In addition, two columns from the CSV file are set as properties on the nodes. .Result -[queryresult] +[role="queryresult"] ---- +-------------------+ | No data returned. | @@ -129,7 +129,7 @@ CREATE (:Artist {name: line[1], year: toInteger(line[2])}) ---- .Result -[queryresult] +[role="queryresult"] ---- +-------------------+ | No data returned. | @@ -165,7 +165,7 @@ This time, the file starts with a single row containing column names. Indicate this using `WITH HEADERS` and you can access specific fields by their corresponding column name. .Result -[queryresult] +[role="queryresult"] ---- +-------------------+ | No data returned. | @@ -203,7 +203,7 @@ CREATE (:Artist {name: line[1], year: toInteger(line[2])}) As values in this file are separated by a semicolon, a custom `FIELDTERMINATOR` is specified in the `LOAD CSV` clause. .Result -[queryresult] +[role="queryresult"] ---- +-------------------+ | No data returned. | @@ -246,7 +246,7 @@ CALL { ---- .Result -[queryresult] +[role="queryresult"] ---- +-------------------+ | No data returned. | @@ -284,7 +284,7 @@ CALL { ---- .Result -[queryresult] +[role="queryresult"] ---- +-------------------+ | No data returned. | @@ -321,18 +321,15 @@ Note that strings are wrapped in quotes in the output here. You can see that when comparing to the length of the string in this case! .Result -[queryresult] ----- -+------------------------------+ -| name | year | size | -+------------------------------+ -| "The "Symbol"" | 1992 | 12 | -+------------------------------+ +[role="queryresult",options="header,footer",cols="3*(m) ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] ----- -bin/cypher-shell --database=neo4j --user=neo4j -"LOAD CSV WITH HEADERS FROM 'file:///actors.csv' AS line -MATCH (m:Movie {title: line.title}) -MERGE (p:Person {name: line.name}) -ON CREATE SET p.born = toInteger(line.born) -MERGE (p)-[:ACTED_IN {roles:split(line.roles, ';')}]->(m)" ----- -//// - -[source, output, role="noheader", indent=0] +.Result +[role="queryresult"] ---- Added 102 nodes, Created 172 relationships, Set 375 properties, Added 102 labels ---- @@ -619,19 +596,8 @@ ON CREATE SET p.born = toInteger(line.born) MERGE (p)-[:DIRECTED]->(m) ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] ----- -bin/cypher-shell --database=neo4j --user=neo4j -"LOAD CSV WITH HEADERS FROM 'file:///directors.csv' AS line -MATCH (m:Movie {title: line.title}) -MERGE (p:Person {name: line.name}) -ON CREATE SET p.born = toInteger(line.born) -MERGE (p)-[:DIRECTED]->(m)" ----- -//// - -[source, output, role="noheader", indent=0] +.Result +[role="queryresult"] ---- Added 23 nodes, Created 44 relationships, Set 46 properties, Added 23 labels ---- @@ -644,7 +610,8 @@ CREATE INDEX FOR (p:Person) ON (p.name) ---- -[source, output, role="noheader", indent=0] +.Result +[role="queryresult"] ---- Added 1 indexes ---- @@ -669,29 +636,15 @@ RETURN count(m) AS count ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] ----- -bin/cypher-shell --database=neo4j --user=neo4j -"MATCH (p:Person)-[:ACTED_IN]->(m:Movie) -WHERE p.name STARTS WITH 'Tom' -RETURN - p.name AS name, - count(m) AS count" ----- -//// - -[source, output, role="noheader", indent=0] ----- -+---------------------------+ -| name | count | -+---------------------------+ -| "Tom Cruise" | 3 | -| "Tom Hanks" | 12 | -| "Tom Skerritt" | 1 | -+---------------------------+ -3 rows ----- +.Result +[role="queryresult",options="header,footer",cols="2*(m:Movie) -WHERE p.name STARTS WITH 'Tom' -RETURN - p.name AS name, - count(m) AS count" ----- -//// +Planner COST -[source, output, role="noheader", indent=0] ----- -+------------------------+ -| name | count | -+------------------------+ -| "Tom Cruise" | 3 | -| "Tom Hanks" | 12 | -| "Tom Skerritt" | 1 | -+------------------------+ - -+--------------------------------------------------------------------------------------------------------+ -| Plan | Statement | Version | Planner | Runtime | Time | DbHits | Rows | Memory (Bytes) | -+--------------------------------------------------------------------------------------------------------+ -| "PROFILE" | "READ_ONLY" | "CYPHER 4.3" | "COST" | "PIPELINED" | 2 | 43 | 3 | 1768 | -+--------------------------------------------------------------------------------------------------------+ - - -+-----------------------------+--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Other | -+-----------------------------+--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults@neo4j | name, count | 1 | 3 | 0 | | 0/0 | 0.049 | name ASC | In Pipeline 1 | -| | +--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +OrderedAggregation@neo4j | cache[p.name] AS name, count(m) AS count | 1 | 3 | 0 | 1688 | 0/0 | 0.188 | name ASC | In Pipeline 1 | -| | +--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Filter@neo4j | m:Movie | 1 | 16 | 16 | | | | p.name ASC | Fused in Pipeline 0 | -| | +--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Expand(All)@neo4j | (p)-[anon_16:ACTED_IN]->(m) | 1 | 16 | 22 | | | | p.name ASC | Fused in Pipeline 0 | -| | +--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +NodeIndexSeekByRange@neo4j | p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 1 | 4 | 5 | 72 | 4/0 | 0.340 | p.name ASC | Fused in Pipeline 0 | -+-----------------------------+--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------------+----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------+----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | 0 | name, count | 1 | 3 | 0 | | 0/0 | 0.060 | | | +| | +----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +OrderedAggregation | 1 | cache[p.name] AS name, count(m) AS count | 1 | 3 | 0 | 1520 | 0/0 | 3.119 | name ASC | In Pipeline 1 | +| | +----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +Filter | 2 | m:Movie | 1 | 16 | 32 | | | | | | +| | +----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | | +| +Expand(All) | 3 | (p)-[anon_0:ACTED_IN]->(m) | 1 | 16 | 22 | | | | | | +| | +----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | | +| +NodeIndexSeekByRange | 4 | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 4 | 5 | 120 | 7/0 | 0.611 | p.name ASC | Fused in Pipeline 0 | ++-----------------------+----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ + +Total database accesses: 59, total allocated memory: 1600 3 rows ---- @@ -773,147 +709,32 @@ RETURN count(m) AS count ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] ----- -bin/cypher-shell --database=neo4j --user=neo4j -"PROFILE -MATCH (p:Person)-[:ACTED_IN]->(m:Movie) -RETURN - p.name AS name, - count(m) AS count" +.Query Plan +[role="queryplan", subs="attributes+"] ---- -//// +Planner COST -[source, output, role="noheader", indent=0] ----- -+----------------------------------+ -| name | count | -+----------------------------------+ -| "Diane Keaton" | 1 | -| "Jack Nicholson" | 5 | -| "Keanu Reeves" | 7 | -| "Ice-T" | 1 | -| "Takeshi Kitano" | 1 | -| "Dina Meyer" | 1 | -| "Brooke Langton" | 1 | -| "Gene Hackman" | 3 | -| "Orlando Jones" | 1 | -| "Al Pacino" | 1 | -| "Charlize Theron" | 2 | -| "Hugo Weaving" | 5 | -| "Laurence Fishburne" | 3 | -| "Carrie-Anne Moss" | 3 | -| "Emil Eifrem" | 1 | -| "John Hurt" | 1 | -| "Stephen Rea" | 1 | -| "Natalie Portman" | 1 | -| "Ben Miles" | 3 | -| "Jim Broadbent" | 1 | -| "Tom Hanks" | 12 | -| "Halle Berry" | 1 | -| "John Goodman" | 1 | -| "Susan Sarandon" | 1 | -| "Christina Ricci" | 1 | -| "Rain" | 2 | -| "Emile Hirsch" | 1 | -| "Matthew Fox" | 1 | -| "Rick Yune" | 2 | -| "Naomie Harris" | 1 | -| "Liv Tyler" | 1 | -| "Kelly Preston" | 1 | -| "Bonnie Hunt" | 2 | -| "Jerry O'Connell" | 2 | -| "Renee Zellweger" | 1 | -| "Jay Mohr" | 1 | -| "Jonathan Lipnicki" | 1 | -| "Cuba Gooding Jr." | 4 | -| "Regina King" | 1 | -| "Tom Cruise" | 3 | -| "Kelly McGillis" | 1 | -| "Anthony Edwards" | 1 | -| "Tom Skerritt" | 1 | -| "Meg Ryan" | 5 | -| "Val Kilmer" | 1 | -| "Kiefer Sutherland" | 2 | -| "Kevin Bacon" | 3 | -| "Aaron Sorkin" | 1 | -| "Christopher Guest" | 1 | -| "Noah Wyle" | 1 | -| "James Marshall" | 1 | -| "Kevin Pollak" | 1 | -| "J.T. Walsh" | 2 | -| "Demi Moore" | 1 | -| "Danny DeVito" | 2 | -| "John C. Reilly" | 1 | -| "Helen Hunt" | 3 | -| "Greg Kinnear" | 2 | -| "Ed Harris" | 1 | -| "Bill Paxton" | 3 | -| "Gary Sinise" | 2 | -| "Oliver Platt" | 2 | -| "Frank Langella" | 1 | -| "Michael Sheen" | 1 | -| "Sam Rockwell" | 2 | -| "John Cusack" | 1 | -| "Wil Wheaton" | 1 | -| "Corey Feldman" | 1 | -| "River Phoenix" | 1 | -| "Marshall Bell" | 2 | -| "Max von Sydow" | 2 | -| "Annabella Sciorra" | 1 | -| "Werner Herzog" | 1 | -| "Robin Williams" | 3 | -| "Billy Crystal" | 1 | -| "Carrie Fisher" | 1 | -| "Bruno Kirby" | 1 | -| "Nathan Lane" | 2 | -| "Rita Wilson" | 1 | -| "Rosie O'Donnell" | 2 | -| "Bill Pullman" | 1 | -| "Victor Garber" | 1 | -| "Steve Zahn" | 2 | -| "Dave Chappelle" | 1 | -| "Parker Posey" | 1 | -| "James Cromwell" | 2 | -| "Patricia Clarkson" | 1 | -| "Michael Clarke Duncan" | 1 | -| "David Morse" | 1 | -| "Zach Grenier" | 2 | -| "Christian Bale" | 1 | -| "Philip Seymour Hoffman" | 2 | -| "Ethan Hawke" | 1 | -| "Geena Davis" | 1 | -| "Madonna" | 1 | -| "Lori Petty" | 1 | -| "Julia Roberts" | 1 | -| "Ian McKellen" | 1 | -| "Paul Bettany" | 1 | -| "Audrey Tautou" | 1 | -| "Clint Eastwood" | 1 | -| "Richard Harris" | 1 | -+----------------------------------+ - -+--------------------------------------------------------------------------------------------------------+ -| Plan | Statement | Version | Planner | Runtime | Time | DbHits | Rows | Memory (Bytes) | -+--------------------------------------------------------------------------------------------------------+ -| "PROFILE" | "READ_ONLY" | "CYPHER 4.3" | "COST" | "PIPELINED" | 70 | 809 | 102 | 17376 | -+--------------------------------------------------------------------------------------------------------+ - - -+-------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other | -+-------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults@neo4j | name, count | 13 | 102 | 0 | | 0/0 | 0.536 | In Pipeline 1 | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +EagerAggregation@neo4j | p.name AS name, count(m) AS count | 13 | 102 | 344 | 17296 | | | Fused in Pipeline 0 | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter@neo4j | p:Person | 172 | 172 | 172 | | | | Fused in Pipeline 0 | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Expand(All)@neo4j | (m)<-[anon_16:ACTED_IN]-(p) | 172 | 172 | 254 | | | | Fused in Pipeline 0 | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan@neo4j | m:Movie | 38 | 38 | 39 | 72 | 5/0 | 12.818 | Fused in Pipeline 0 | -+-------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-------------------+----+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name, count | 13 | 102 | 0 | | 0/0 | 0.155 | In Pipeline 1 | +| | +----+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | p.name AS name, count(m) AS count | 13 | 102 | 344 | 17296 | | | | +| | +----+-----------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | p:Person | 172 | 172 | 344 | | | | | +| | +----+-----------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | 3 | (m)<-[anon_0:ACTED_IN]-(p) | 172 | 172 | 254 | | | | | +| | +----+-----------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 4 | m:Movie | 38 | 38 | 39 | 120 | 29/0 | 1.444 | Fused in Pipeline 0 | ++-------------------+----+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 981, total allocated memory: 17376 102 rows ---- @@ -952,40 +773,29 @@ MATCH (p:Person) RETURN count(DISTINCT p.name) AS numberOfNames ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] +.Query Plan +[role="queryplan", subs="attributes+"] ---- -bin/cypher-shell --database=neo4j --user=neo4j -"PROFILE -MATCH (p:Person) -RETURN count(DISTINCT p.name) AS numberOfNames" ----- -//// -[source, output, role="noheader", indent=0] ----- -+---------------+ -| numberOfNames | -+---------------+ -| 125 | -+---------------+ - -+--------------------------------------------------------------------------------------------------------+ -| Plan | Statement | Version | Planner | Runtime | Time | DbHits | Rows | Memory (Bytes) | -+--------------------------------------------------------------------------------------------------------+ -| "PROFILE" | "READ_ONLY" | "CYPHER 4.3" | "COST" | "PIPELINED" | 45 | 126 | 1 | 9952 | -+--------------------------------------------------------------------------------------------------------+ - - -+-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other | -+-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults@neo4j | numberOfNames | 1 | 1 | 0 | | 0/0 | 0.048 | In Pipeline 1 | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +EagerAggregation@neo4j | count(DISTINCT cache[p.name]) AS numberOfNames | 1 | 1 | 0 | 9888 | | | Fused in Pipeline 0 | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeIndexScan@neo4j | p:Person(name) WHERE name IS NOT NULL, cache[p.name] | 125 | 125 | 126 | 72 | 1/0 | 1.569 | Fused in Pipeline 0 | -+-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-------------------+----+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | numberOfNames | 1 | 1 | 0 | | 0/0 | 0.026 | In Pipeline 1 | +| | +----+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(DISTINCT cache[p.name]) AS numberOfNames | 1 | 1 | 0 | 9888 | | | | +| | +----+------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexScan | 2 | RANGE INDEX p:Person(name) WHERE name IS NOT NULL, cache[p.name] | 125 | 125 | 126 | 120 | 1/0 | 1.400 | Fused in Pipeline 0 | ++-------------------+----+------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 126, total allocated memory: 9952 1 row ---- @@ -1010,52 +820,31 @@ RETURN ORDER BY name ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] +.Query Plan +[role="queryplan", subs="attributes+"] ---- -bin/cypher-shell --database=neo4j --user=neo4j -"PROFILE -MATCH (p:Person)-[:ACTED_IN]->(m:Movie) -WHERE p.name STARTS WITH 'Tom' -RETURN - p.name AS name, - count(m) AS count -ORDER BY name" ----- -//// -[source, output, role="noheader", indent=0] ----- -+------------------------+ -| name | count | -+------------------------+ -| "Tom Cruise" | 3 | -| "Tom Hanks" | 12 | -| "Tom Skerritt" | 1 | -+------------------------+ - -+--------------------------------------------------------------------------------------------------------+ -| Plan | Statement | Version | Planner | Runtime | Time | DbHits | Rows | Memory (Bytes) | -+--------------------------------------------------------------------------------------------------------+ -| "PROFILE" | "READ_ONLY" | "CYPHER 4.3" | "COST" | "PIPELINED" | 48 | 43 | 3 | 1768 | -+--------------------------------------------------------------------------------------------------------+ - - -+-----------------------------+--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Other | -+-----------------------------+--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults@neo4j | name, count | 1 | 3 | 0 | | 0/0 | 0.045 | name ASC | In Pipeline 1 | -| | +--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +OrderedAggregation@neo4j | cache[p.name] AS name, count(m) AS count | 1 | 3 | 0 | 1688 | 0/0 | 0.173 | name ASC | In Pipeline 1 | -| | +--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Filter@neo4j | m:Movie | 1 | 16 | 16 | | | | p.name ASC | Fused in Pipeline 0 | -| | +--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Expand(All)@neo4j | (p)-[anon_16:ACTED_IN]->(m) | 1 | 16 | 22 | | | | p.name ASC | Fused in Pipeline 0 | -| | +--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +NodeIndexSeekByRange@neo4j | p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 1 | 4 | 5 | 72 | 4/0 | 0.459 | p.name ASC | Fused in Pipeline 0 | -+-----------------------------+--------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +Planner COST -3 rows +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------------+----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------+----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | 0 | name, count | 1 | 3 | 0 | | 0/0 | 0.025 | | | +| | +----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +OrderedAggregation | 1 | cache[p.name] AS name, count(m) AS count | 1 | 3 | 0 | 1520 | 0/0 | 0.097 | name ASC | In Pipeline 1 | +| | +----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +Filter | 2 | m:Movie | 1 | 16 | 32 | | | | | | +| | +----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | | +| +Expand(All) | 3 | (p)-[anon_0:ACTED_IN]->(m) | 1 | 16 | 22 | | | | | | +| | +----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | | +| +NodeIndexSeekByRange | 4 | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 4 | 5 | 120 | 7/0 | 0.406 | p.name ASC | Fused in Pipeline 0 | ++-----------------------+----+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ---- We are asking for the results in ascending alphabetical order. @@ -1086,44 +875,33 @@ MATCH (p:Person)-[:ACTED_IN]->(m:Movie) RETURN min(p.name) AS name ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] ----- -bin/cypher-shell --database=neo4j --user=neo4j -"PROFILE -MATCH (p:Person)-[:ACTED_IN]->(m:Movie) -RETURN min(p.name) AS name" +.Query Plan +[role="queryplan", subs="attributes+"] ---- -//// -[source, output, role="noheader", indent=0] ----- -+----------------+ -| name | -+----------------+ -| "Aaron Sorkin" | -+----------------+ - -+--------------------------------------------------------------------------------------------------------+ -| Plan | Statement | Version | Planner | Runtime | Time | DbHits | Rows | Memory (Bytes) | -+--------------------------------------------------------------------------------------------------------+ -| "PROFILE" | "READ_ONLY" | "CYPHER 4.3" | "COST" | "PIPELINED" | 38 | 809 | 1 | 184 | -+--------------------------------------------------------------------------------------------------------+ - - -+-------------------------+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other | -+-------------------------+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults@neo4j | name | 1 | 1 | 0 | | 0/0 | 0.041 | In Pipeline 1 | -| | +-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +EagerAggregation@neo4j | min(p.name) AS name | 1 | 1 | 344 | 32 | | | Fused in Pipeline 0 | -| | +-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter@neo4j | p:Person | 172 | 172 | 172 | | | | Fused in Pipeline 0 | -| | +-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Expand(All)@neo4j | (m)<-[anon_16:ACTED_IN]-(p) | 172 | 172 | 254 | | | | Fused in Pipeline 0 | -| | +-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan@neo4j | m:Movie | 38 | 38 | 39 | 72 | 5/0 | 1.636 | Fused in Pipeline 0 | -+-------------------------+-----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name | 1 | 1 | 0 | | 0/0 | 0.027 | In Pipeline 1 | +| | +----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | min(p.name) AS name | 1 | 1 | 344 | 32 | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | p:Person | 172 | 172 | 344 | | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | 3 | (m)<-[anon_0:ACTED_IN]-(p) | 172 | 172 | 254 | | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 4 | m:Movie | 38 | 38 | 39 | 120 | 29/0 | 0.990 | Fused in Pipeline 0 | ++-------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 981, total allocated memory: 232 1 row ---- diff --git a/modules/ROOT/pages/query-tuning/basic-example.adoc b/modules/ROOT/pages/query-tuning/basic-example.adoc index fcc029d07..33cefab9d 100644 --- a/modules/ROOT/pages/query-tuning/basic-example.adoc +++ b/modules/ROOT/pages/query-tuning/basic-example.adoc @@ -1,5 +1,4 @@ :description: Example how to profile a query, by using optimizations based on native index capabilities. -:test-skip: true [[cypherdoc-basic-query-tuning-example]] = Basic query tuning example @@ -549,26 +548,14 @@ ON CREATE SET m.tagline = line.tagline ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] ----- -bin/cypher-shell --database=neo4j --user=neo4j -"LOAD CSV WITH HEADERS FROM 'file:///movies.csv' AS line -MERGE (m:Movie {title: line.title}) -ON CREATE SET - m.released = toInteger(line.released), - m.tagline = line.tagline" ----- -//// - -[source, output, role="noheader", indent=0] +.Result +[role="queryresult"] ---- Added 38 nodes, Set 114 properties, Added 38 labels ---- Import the _actors.csv_ file:: - [source, cypher, indent=0] ---- LOAD CSV WITH HEADERS FROM 'file:///actors.csv' AS line @@ -578,19 +565,8 @@ ON CREATE SET p.born = toInteger(line.born) MERGE (p)-[:ACTED_IN {roles:split(line.roles, ';')}]->(m) ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] ----- -bin/cypher-shell --database=neo4j --user=neo4j -"LOAD CSV WITH HEADERS FROM 'file:///actors.csv' AS line -MATCH (m:Movie {title: line.title}) -MERGE (p:Person {name: line.name}) -ON CREATE SET p.born = toInteger(line.born) -MERGE (p)-[:ACTED_IN {roles:split(line.roles, ';')}]->(m)" ----- -//// - -[source, output, role="noheader", indent=0] +.Result +[role="queryresult"] ---- Added 102 nodes, Created 172 relationships, Set 375 properties, Added 102 labels ---- @@ -606,19 +582,8 @@ ON CREATE SET p.born = toInteger(line.born) MERGE (p)-[:DIRECTED]->(m) ---- -//// -[source, cypher-shell, role="nocopy,norun", indent=0] ----- -bin/cypher-shell --database=neo4j --user=neo4j -"LOAD CSV WITH HEADERS FROM 'file:///directors.csv' AS line -MATCH (m:Movie {title: line.title}) -MERGE (p:Person {name: line.name}) -ON CREATE SET p.born = toInteger(line.born) -MERGE (p)-[:DIRECTED]->(m)" ----- -//// - -[source, output, role="noheader", indent=0] +.Result +[role="queryresult"] ---- Added 23 nodes, Created 44 relationships, Set 46 properties, Added 23 labels ---- @@ -636,6 +601,15 @@ MATCH (p {name: 'Tom Hanks'}) RETURN p ---- +.Result +[role="queryresult",options="header,footer",cols="1* Date: Fri, 10 Mar 2023 08:35:37 +0100 Subject: [PATCH 153/383] Make csv examples ready for autoextraction from tests (#469) This basically adds an extra attribute `filename` to all `[source, csv]` blocks, that tells the testing infrastructure into what file to store the csv so that the subsequent `LOAD CSV` examples can work with them. Static csv files not needed anymore. (Plus some cleanup of attributes on the way.) --- modules/ROOT/examples/README.md | 3 - modules/ROOT/examples/actors.csv | 173 ------------ .../ROOT/examples/artists-fieldterminator.csv | 4 - .../examples/artists-with-escaped-char.csv | 1 - .../ROOT/examples/artists-with-headers.csv | 5 - modules/ROOT/examples/artists.csv | 4 - modules/ROOT/examples/directors.csv | 45 ---- modules/ROOT/examples/friends.csv | 5 - modules/ROOT/examples/movies.csv | 248 ------------------ modules/ROOT/pages/clauses/call-subquery.adoc | 4 +- modules/ROOT/pages/clauses/load-csv.adoc | 38 +-- .../pages/query-tuning/advanced-example.adoc | 6 +- .../pages/query-tuning/basic-example.adoc | 28 +- 13 files changed, 38 insertions(+), 526 deletions(-) delete mode 100644 modules/ROOT/examples/README.md delete mode 100644 modules/ROOT/examples/actors.csv delete mode 100644 modules/ROOT/examples/artists-fieldterminator.csv delete mode 100644 modules/ROOT/examples/artists-with-escaped-char.csv delete mode 100644 modules/ROOT/examples/artists-with-headers.csv delete mode 100644 modules/ROOT/examples/artists.csv delete mode 100644 modules/ROOT/examples/directors.csv delete mode 100644 modules/ROOT/examples/friends.csv delete mode 100644 modules/ROOT/examples/movies.csv diff --git a/modules/ROOT/examples/README.md b/modules/ROOT/examples/README.md deleted file mode 100644 index 1efa1c085..000000000 --- a/modules/ROOT/examples/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# CSV files for Cypher testing -- *.csv files here are the testing resources for `LOAD CSV` command in docs. Before test run, copy those file under the `/import` folder in your neo4j database. -- if you changing the content under `[source, csv]` block, please remember to take a look at those csv files here as well. \ No newline at end of file diff --git a/modules/ROOT/examples/actors.csv b/modules/ROOT/examples/actors.csv deleted file mode 100644 index 695f453dd..000000000 --- a/modules/ROOT/examples/actors.csv +++ /dev/null @@ -1,173 +0,0 @@ -title,roles,name,born -Something's Gotta Give,Julian Mercer,Keanu Reeves,1964 -Johnny Mnemonic,Johnny Mnemonic,Keanu Reeves,1964 -The Replacements,Shane Falco,Keanu Reeves,1964 -The Devil's Advocate,Kevin Lomax,Keanu Reeves,1964 -The Matrix Revolutions,Neo,Keanu Reeves,1964 -The Matrix Reloaded,Neo,Keanu Reeves,1964 -The Matrix,Neo,Keanu Reeves,1964 -The Matrix Revolutions,Trinity,Carrie-Anne Moss,1967 -The Matrix Reloaded,Trinity,Carrie-Anne Moss,1967 -The Matrix,Trinity,Carrie-Anne Moss,1967 -The Matrix Revolutions,Morpheus,Laurence Fishburne,1961 -The Matrix Reloaded,Morpheus,Laurence Fishburne,1961 -The Matrix,Morpheus,Laurence Fishburne,1961 -V for Vendetta,V,Hugo Weaving,1960 -Cloud Atlas,Bill Smoke;Haskell Moore;Tadeusz Kesselring;Nurse Noakes;Boardman Mephi;Old Georgie,Hugo Weaving,1960 -The Matrix Revolutions,Agent Smith,Hugo Weaving,1960 -The Matrix Reloaded,Agent Smith,Hugo Weaving,1960 -The Matrix,Agent Smith,Hugo Weaving,1960 -The Matrix,Emil,Emil Eifrem,1978 -That Thing You Do,Tina,Charlize Theron,1975 -The Devil's Advocate,Mary Ann Lomax,Charlize Theron,1975 -The Devil's Advocate,John Milton,Al Pacino,1940 -Jerry Maguire,Jerry Maguire,Tom Cruise,1962 -Top Gun,Maverick,Tom Cruise,1962 -A Few Good Men,Lt. Daniel Kaffee,Tom Cruise,1962 -Something's Gotta Give,Harry Sanborn,Jack Nicholson,1937 -One Flew Over the Cuckoo's Nest,Randle McMurphy,Jack Nicholson,1937 -Hoffa,Hoffa,Jack Nicholson,1937 -As Good as It Gets,Melvin Udall,Jack Nicholson,1937 -A Few Good Men,Col. Nathan R. Jessup,Jack Nicholson,1937 -A Few Good Men,Lt. Cdr. JoAnne Galloway,Demi Moore,1962 -Apollo 13,Jack Swigert,Kevin Bacon,1958 -Frost/Nixon,Jack Brennan,Kevin Bacon,1958 -A Few Good Men,Capt. Jack Ross,Kevin Bacon,1958 -Stand By Me,Ace Merrill,Kiefer Sutherland,1966 -A Few Good Men,Lt. Jonathan Kendrick,Kiefer Sutherland,1966 -A Few Good Men,Cpl. Jeffrey Barnes,Noah Wyle,1971 -What Dreams May Come,Albert Lewis,Cuba Gooding Jr.,1968 -As Good as It Gets,Frank Sachs,Cuba Gooding Jr.,1968 -Jerry Maguire,Rod Tidwell,Cuba Gooding Jr.,1968 -A Few Good Men,Cpl. Carl Hammaker,Cuba Gooding Jr.,1968 -A Few Good Men,Lt. Sam Weinberg,Kevin Pollak,1957 -Hoffa,Frank Fitzsimmons,J.T. Walsh,1943 -A Few Good Men,Lt. Col. Matthew Andrew Markinson,J.T. Walsh,1943 -A Few Good Men,Pfc. Louden Downey,James Marshall,1967 -A Few Good Men,Dr. Stone,Christopher Guest,1948 -A Few Good Men,Man in Bar,Aaron Sorkin,1961 -Top Gun,Charlie,Kelly McGillis,1957 -Top Gun,Iceman,Val Kilmer,1959 -Top Gun,Goose,Anthony Edwards,1962 -Top Gun,Viper,Tom Skerritt,1933 -When Harry Met Sally,Sally Albright,Meg Ryan,1961 -Joe Versus the Volcano,DeDe;Angelica Graynamore;Patricia Graynamore,Meg Ryan,1961 -Sleepless in Seattle,Annie Reed,Meg Ryan,1961 -You've Got Mail,Kathleen Kelly,Meg Ryan,1961 -Top Gun,Carole,Meg Ryan,1961 -Jerry Maguire,Dorothy Boyd,Renee Zellweger,1969 -Jerry Maguire,Avery Bishop,Kelly Preston,1962 -Stand By Me,Vern Tessio,Jerry O'Connell,1974 -Jerry Maguire,Frank Cushman,Jerry O'Connell,1974 -Jerry Maguire,Bob Sugar,Jay Mohr,1970 -The Green Mile,Jan Edgecomb,Bonnie Hunt,1961 -Jerry Maguire,Laurel Boyd,Bonnie Hunt,1961 -Jerry Maguire,Marcee Tidwell,Regina King,1971 -Jerry Maguire,Ray Boyd,Jonathan Lipnicki,1990 -Stand By Me,Chris Chambers,River Phoenix,1970 -Stand By Me,Teddy Duchamp,Corey Feldman,1971 -Stand By Me,Gordie Lachance,Wil Wheaton,1972 -Stand By Me,Denny Lachance,John Cusack,1966 -RescueDawn,Admiral,Marshall Bell,1942 -Stand By Me,Mr. Lachance,Marshall Bell,1942 -Cast Away,Kelly Frears,Helen Hunt,1963 -Twister,Dr. Jo Harding,Helen Hunt,1963 -As Good as It Gets,Carol Connelly,Helen Hunt,1963 -You've Got Mail,Frank Navasky,Greg Kinnear,1963 -As Good as It Gets,Simon Bishop,Greg Kinnear,1963 -What Dreams May Come,Simon Bishop,Annabella Sciorra,1960 -Snow Falling on Cedars,Nels Gudmundsson,Max von Sydow,1929 -What Dreams May Come,The Tracker,Max von Sydow,1929 -What Dreams May Come,The Face,Werner Herzog,1942 -Bicentennial Man,Andrew Marin,Robin Williams,1951 -The Birdcage,Armand Goldman,Robin Williams,1951 -What Dreams May Come,Chris Nielsen,Robin Williams,1951 -Snow Falling on Cedars,Ishmael Chambers,Ethan Hawke,1970 -Ninja Assassin,Takeshi,Rick Yune,1971 -Snow Falling on Cedars,Kazuo Miyamoto,Rick Yune,1971 -The Green Mile,Warden Hal Moores,James Cromwell,1940 -Snow Falling on Cedars,Judge Fielding,James Cromwell,1940 -You've Got Mail,Patricia Eden,Parker Posey,1968 -You've Got Mail,Kevin Jackson,Dave Chappelle,1973 -RescueDawn,Duane,Steve Zahn,1967 -You've Got Mail,George Pappas,Steve Zahn,1967 -A League of Their Own,Jimmy Dugan,Tom Hanks,1956 -The Polar Express,Hero Boy;Father;Conductor;Hobo;Scrooge;Santa Claus,Tom Hanks,1956 -Charlie Wilson's War,Rep. Charlie Wilson,Tom Hanks,1956 -Cast Away,Chuck Noland,Tom Hanks,1956 -Apollo 13,Jim Lovell,Tom Hanks,1956 -The Green Mile,Paul Edgecomb,Tom Hanks,1956 -The Da Vinci Code,Dr. Robert Langdon,Tom Hanks,1956 -Cloud Atlas,Zachry;Dr. Henry Goose;Isaac Sachs;Dermot Hoggins,Tom Hanks,1956 -That Thing You Do,Mr. White,Tom Hanks,1956 -Joe Versus the Volcano,Joe Banks,Tom Hanks,1956 -Sleepless in Seattle,Sam Baldwin,Tom Hanks,1956 -You've Got Mail,Joe Fox,Tom Hanks,1956 -Sleepless in Seattle,Suzy,Rita Wilson,1956 -Sleepless in Seattle,Walter,Bill Pullman,1953 -Sleepless in Seattle,Greg,Victor Garber,1949 -A League of Their Own,Doris Murphy,Rosie O'Donnell,1962 -Sleepless in Seattle,Becky,Rosie O'Donnell,1962 -The Birdcage,Albert Goldman,Nathan Lane,1956 -Joe Versus the Volcano,Baw,Nathan Lane,1956 -When Harry Met Sally,Harry Burns,Billy Crystal,1948 -When Harry Met Sally,Marie,Carrie Fisher,1956 -When Harry Met Sally,Jess,Bruno Kirby,1949 -That Thing You Do,Faye Dolan,Liv Tyler,1977 -The Replacements,Annabelle Farrell,Brooke Langton,1970 -Unforgiven,Little Bill Daggett,Gene Hackman,1930 -The Birdcage,Sen. Kevin Keeley,Gene Hackman,1930 -The Replacements,Jimmy McGinty,Gene Hackman,1930 -The Replacements,Clifford Franklin,Orlando Jones,1968 -RescueDawn,Dieter Dengler,Christian Bale,1974 -Twister,Eddie,Zach Grenier,1954 -RescueDawn,Squad Leader,Zach Grenier,1954 -Unforgiven,English Bob,Richard Harris,1930 -Unforgiven,Bill Munny,Clint Eastwood,1930 -Johnny Mnemonic,Takahashi,Takeshi Kitano,1947 -Johnny Mnemonic,Jane,Dina Meyer,1968 -Johnny Mnemonic,J-Bone,Ice-T,1958 -Cloud Atlas,Luisa Rey;Jocasta Ayrs;Ovid;Meronym,Halle Berry,1966 -Cloud Atlas,Vyvyan Ayrs;Captain Molyneux;Timothy Cavendish,Jim Broadbent,1949 -The Da Vinci Code,Sir Leight Teabing,Ian McKellen,1939 -The Da Vinci Code,Sophie Neveu,Audrey Tautou,1976 -The Da Vinci Code,Silas,Paul Bettany,1971 -V for Vendetta,Evey Hammond,Natalie Portman,1981 -V for Vendetta,Eric Finch,Stephen Rea,1946 -V for Vendetta,High Chancellor Adam Sutler,John Hurt,1940 -Ninja Assassin,Ryan Maslow,Ben Miles,1967 -Speed Racer,Cass Jones,Ben Miles,1967 -V for Vendetta,Dascomb,Ben Miles,1967 -Speed Racer,Speed Racer,Emile Hirsch,1985 -Speed Racer,Pops,John Goodman,1960 -Speed Racer,Mom,Susan Sarandon,1946 -Speed Racer,Racer X,Matthew Fox,1966 -Speed Racer,Trixie,Christina Ricci,1980 -Ninja Assassin,Raizo,Rain,1982 -Speed Racer,Taejo Togokahn,Rain,1982 -Ninja Assassin,Mika Coretti,Naomie Harris,null -The Green Mile,John Coffey,Michael Clarke Duncan,1957 -The Green Mile,Brutus 'Brutal' Howell,David Morse,1953 -Frost/Nixon,"James Reston, Jr.",Sam Rockwell,1968 -The Green Mile,'Wild Bill' Wharton,Sam Rockwell,1968 -Apollo 13,Ken Mattingly,Gary Sinise,1955 -The Green Mile,Burt Hammersmith,Gary Sinise,1955 -The Green Mile,Melinda Moores,Patricia Clarkson,1959 -Frost/Nixon,Richard Nixon,Frank Langella,1938 -Frost/Nixon,David Frost,Michael Sheen,1969 -Bicentennial Man,Rupert Burns,Oliver Platt,1960 -Frost/Nixon,Bob Zelnick,Oliver Platt,1960 -One Flew Over the Cuckoo's Nest,Martini,Danny DeVito,1944 -Hoffa,Robert 'Bobby' Ciaro,Danny DeVito,1944 -Hoffa,Peter 'Pete' Connelly,John C. Reilly,1965 -Apollo 13,Gene Kranz,Ed Harris,1950 -A League of Their Own,Bob Hinson,Bill Paxton,1955 -Twister,Bill Harding,Bill Paxton,1955 -Apollo 13,Fred Haise,Bill Paxton,1955 -Charlie Wilson's War,Gust Avrakotos,Philip Seymour Hoffman,1967 -Twister,Dustin 'Dusty' Davis,Philip Seymour Hoffman,1967 -Something's Gotta Give,Erica Barry,Diane Keaton,1946 -Charlie Wilson's War,Joanne Herring,Julia Roberts,1967 -A League of Their Own,'All the Way' Mae Mordabito,Madonna,1954 -A League of Their Own,Dottie Hinson,Geena Davis,1956 -A League of Their Own,Kit Keller,Lori Petty,1963 diff --git a/modules/ROOT/examples/artists-fieldterminator.csv b/modules/ROOT/examples/artists-fieldterminator.csv deleted file mode 100644 index a4f3fb00b..000000000 --- a/modules/ROOT/examples/artists-fieldterminator.csv +++ /dev/null @@ -1,4 +0,0 @@ -1;ABBA;1992 -2;Roxette;1986 -3;Europe;1979 -4;The Cardigans;1992 diff --git a/modules/ROOT/examples/artists-with-escaped-char.csv b/modules/ROOT/examples/artists-with-escaped-char.csv deleted file mode 100644 index 30d7376f2..000000000 --- a/modules/ROOT/examples/artists-with-escaped-char.csv +++ /dev/null @@ -1 +0,0 @@ -1,"The """"Symbol""""",1992 diff --git a/modules/ROOT/examples/artists-with-headers.csv b/modules/ROOT/examples/artists-with-headers.csv deleted file mode 100644 index 7add2665e..000000000 --- a/modules/ROOT/examples/artists-with-headers.csv +++ /dev/null @@ -1,5 +0,0 @@ -Id,Name,Year -1,ABBA,1992 -2,Roxette,1986 -3,Europe,1979 -4,The Cardigans,1992 diff --git a/modules/ROOT/examples/artists.csv b/modules/ROOT/examples/artists.csv deleted file mode 100644 index 9ef4ddf39..000000000 --- a/modules/ROOT/examples/artists.csv +++ /dev/null @@ -1,4 +0,0 @@ -1,ABBA,1992 -2,Roxette,1986 -3,Europe,1979 -4,The Cardigans,1992 diff --git a/modules/ROOT/examples/directors.csv b/modules/ROOT/examples/directors.csv deleted file mode 100644 index 1a7afd88c..000000000 --- a/modules/ROOT/examples/directors.csv +++ /dev/null @@ -1,45 +0,0 @@ -title,name,born -Speed Racer,Andy Wachowski,1967 -Cloud Atlas,Andy Wachowski,1967 -The Matrix Revolutions,Andy Wachowski,1967 -The Matrix Reloaded,Andy Wachowski,1967 -The Matrix,Andy Wachowski,1967 -Speed Racer,Lana Wachowski,1965 -Cloud Atlas,Lana Wachowski,1965 -The Matrix Revolutions,Lana Wachowski,1965 -The Matrix Reloaded,Lana Wachowski,1965 -The Matrix,Lana Wachowski,1965 -The Devil's Advocate,Taylor Hackford,1944 -Ninja Assassin,James Marshall,1967 -V for Vendetta,James Marshall,1967 -When Harry Met Sally,Rob Reiner,1947 -Stand By Me,Rob Reiner,1947 -A Few Good Men,Rob Reiner,1947 -Top Gun,Tony Scott,1944 -Jerry Maguire,Cameron Crowe,1957 -As Good as It Gets,James L. Brooks,1940 -RescueDawn,Werner Herzog,1942 -What Dreams May Come,Vincent Ward,1956 -Snow Falling on Cedars,Scott Hicks,1953 -That Thing You Do,Tom Hanks,1956 -Sleepless in Seattle,Nora Ephron,1941 -You've Got Mail,Nora Ephron,1941 -Joe Versus the Volcano,John Patrick Stanley,1950 -The Replacements,Howard Deutch,1950 -Charlie Wilson's War,Mike Nichols,1931 -The Birdcage,Mike Nichols,1931 -Unforgiven,Clint Eastwood,1930 -Johnny Mnemonic,Robert Longo,1953 -Cloud Atlas,Tom Tykwer,1965 -Apollo 13,Ron Howard,1954 -Frost/Nixon,Ron Howard,1954 -The Da Vinci Code,Ron Howard,1954 -The Green Mile,Frank Darabont,1959 -Hoffa,Danny DeVito,1944 -Twister,Jan de Bont,1943 -The Polar Express,Robert Zemeckis,1951 -Cast Away,Robert Zemeckis,1951 -One Flew Over the Cuckoo's Nest,Milos Forman,1932 -Something's Gotta Give,Nancy Meyers,1949 -Bicentennial Man,Chris Columbus,1958 -A League of Their Own,Penny Marshall,1943 diff --git a/modules/ROOT/examples/friends.csv b/modules/ROOT/examples/friends.csv deleted file mode 100644 index bc66cb1ef..000000000 --- a/modules/ROOT/examples/friends.csv +++ /dev/null @@ -1,5 +0,0 @@ -1,Bill,26 -2,Max,27 -3,Anna,22 -4,Gladys,29 -5,Summer,24 diff --git a/modules/ROOT/examples/movies.csv b/modules/ROOT/examples/movies.csv deleted file mode 100644 index ed5890a8e..000000000 --- a/modules/ROOT/examples/movies.csv +++ /dev/null @@ -1,248 +0,0 @@ -title,released,tagline -Something's Gotta Give,1975,null -Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town -The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" -The Devil's Advocate,1997,Evil has its winning ways -The Matrix Revolutions,2003,Everything that has a beginning has an end -The Matrix Reloaded,2003,Free your mind -The Matrix,1999,Welcome to the Real World -The Matrix Revolutions,2003,Everything that has a beginning has an end -The Matrix Reloaded,2003,Free your mind -The Matrix,1999,Welcome to the Real World -The Matrix Revolutions,2003,Everything that has a beginning has an end -The Matrix Reloaded,2003,Free your mind -The Matrix,1999,Welcome to the Real World -V for Vendetta,2006,Freedom! Forever! -Cloud Atlas,2012,Everything is connected -The Matrix Revolutions,2003,Everything that has a beginning has an end -The Matrix Reloaded,2003,Free your mind -The Matrix,1999,Welcome to the Real World -Speed Racer,2008,Speed has no limits -Cloud Atlas,2012,Everything is connected -The Matrix Revolutions,2003,Everything that has a beginning has an end -The Matrix Reloaded,2003,Free your mind -The Matrix,1999,Welcome to the Real World -Ninja Assassin,2009,Prepare to enter a secret world of assassins -V for Vendetta,2006,Freedom! Forever! -Speed Racer,2008,Speed has no limits -V for Vendetta,2006,Freedom! Forever! -Speed Racer,2008,Speed has no limits -Cloud Atlas,2012,Everything is connected -The Matrix Revolutions,2003,Everything that has a beginning has an end -The Matrix Reloaded,2003,Free your mind -The Matrix,1999,Welcome to the Real World -Ninja Assassin,2009,Prepare to enter a secret world of assassins -V for Vendetta,2006,Freedom! Forever! -Speed Racer,2008,Speed has no limits -V for Vendetta,2006,Freedom! Forever! -Ninja Assassin,2009,Prepare to enter a secret world of assassins -Speed Racer,2008,Speed has no limits -V for Vendetta,2006,Freedom! Forever! -The Matrix Revolutions,2003,Everything that has a beginning has an end -The Matrix Reloaded,2003,Free your mind -The Matrix,1999,Welcome to the Real World -The Matrix,1999,Welcome to the Real World -That Thing You Do,1996,In every life there comes a time when that thing you dream becomes that thing you do -The Devil's Advocate,1997,Evil has its winning ways -The Devil's Advocate,1997,Evil has its winning ways -The Devil's Advocate,1997,Evil has its winning ways -Jerry Maguire,2000,The rest of his life begins now. -Top Gun,1986,"I feel the need, the need for speed." -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -Something's Gotta Give,1975,null -One Flew Over the Cuckoo's Nest,1975,"If he's crazy, what does that make you?" -Hoffa,1992,He didn't want law. He wanted justice. -As Good as It Gets,1997,A comedy from the heart that goes for the throat. -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -Apollo 13,1995,"Houston, we have a problem." -Frost/Nixon,2008,400 million people were waiting for the truth. -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -What Dreams May Come,1998,After life there is more. The end is just the beginning. -As Good as It Gets,1997,A comedy from the heart that goes for the throat. -Jerry Maguire,2000,The rest of his life begins now. -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -Hoffa,1992,He didn't want law. He wanted justice. -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -Ninja Assassin,2009,Prepare to enter a secret world of assassins -V for Vendetta,2006,Freedom! Forever! -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -When Harry Met Sally,1998,At odds in life... in love on-line. -Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -When Harry Met Sally,1998,At odds in life... in love on-line. -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -A Few Good Men,1992,"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth." -Top Gun,1986,"I feel the need, the need for speed." -Top Gun,1986,"I feel the need, the need for speed." -Top Gun,1986,"I feel the need, the need for speed." -Top Gun,1986,"I feel the need, the need for speed." -When Harry Met Sally,1998,At odds in life... in love on-line. -Joe Versus the Volcano,1990,"A story of love, lava and burning desire." -Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" -You've Got Mail,1998,At odds in life... in love on-line. -Top Gun,1986,"I feel the need, the need for speed." -Top Gun,1986,"I feel the need, the need for speed." -Top Gun,1986,"I feel the need, the need for speed." -Jerry Maguire,2000,The rest of his life begins now. -Jerry Maguire,2000,The rest of his life begins now. -Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." -Jerry Maguire,2000,The rest of his life begins now. -Jerry Maguire,2000,The rest of his life begins now. -The Green Mile,1999,Walk a mile you'll never forget. -Jerry Maguire,2000,The rest of his life begins now. -Jerry Maguire,2000,The rest of his life begins now. -Jerry Maguire,2000,The rest of his life begins now. -Jerry Maguire,2000,The rest of his life begins now. -Jerry Maguire,2000,The rest of his life begins now. -Jerry Maguire,2000,The rest of his life begins now. -Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." -Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." -Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." -Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." -RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom -Stand By Me,1995,"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of." -Cast Away,2000,"At the edge of the world, his journey begins." -Twister,1996,Don't Breathe. Don't Look Back. -As Good as It Gets,1997,A comedy from the heart that goes for the throat. -You've Got Mail,1998,At odds in life... in love on-line. -As Good as It Gets,1997,A comedy from the heart that goes for the throat. -As Good as It Gets,1997,A comedy from the heart that goes for the throat. -What Dreams May Come,1998,After life there is more. The end is just the beginning. -Snow Falling on Cedars,1999,First loves last. Forever. -What Dreams May Come,1998,After life there is more. The end is just the beginning. -What Dreams May Come,1998,After life there is more. The end is just the beginning. -RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom -Bicentennial Man,1999,One robot's 200 year journey to become an ordinary man. -The Birdcage,1996,Come as you are -What Dreams May Come,1998,After life there is more. The end is just the beginning. -What Dreams May Come,1998,After life there is more. The end is just the beginning. -Snow Falling on Cedars,1999,First loves last. Forever. -Ninja Assassin,2009,Prepare to enter a secret world of assassins -Snow Falling on Cedars,1999,First loves last. Forever. -The Green Mile,1999,Walk a mile you'll never forget. -Snow Falling on Cedars,1999,First loves last. Forever. -Snow Falling on Cedars,1999,First loves last. Forever. -You've Got Mail,1998,At odds in life... in love on-line. -You've Got Mail,1998,At odds in life... in love on-line. -RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom -You've Got Mail,1998,At odds in life... in love on-line. -A League of Their Own,1992,Once in a lifetime you get a chance to do something different. -The Polar Express,2004,This Holiday Season… Believe -Charlie Wilson's War,2007,A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire. -Cast Away,2000,"At the edge of the world, his journey begins." -Apollo 13,1995,"Houston, we have a problem." -The Green Mile,1999,Walk a mile you'll never forget. -The Da Vinci Code,2006,Break The Codes -Cloud Atlas,2012,Everything is connected -That Thing You Do,1996,In every life there comes a time when that thing you dream becomes that thing you do -Joe Versus the Volcano,1990,"A story of love, lava and burning desire." -Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" -You've Got Mail,1998,At odds in life... in love on-line. -That Thing You Do,1996,In every life there comes a time when that thing you dream becomes that thing you do -Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" -You've Got Mail,1998,At odds in life... in love on-line. -When Harry Met Sally,1998,At odds in life... in love on-line. -When Harry Met Sally,1998,At odds in life... in love on-line. -Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" -Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" -Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" -A League of Their Own,1992,Once in a lifetime you get a chance to do something different. -Sleepless in Seattle,1993,"What if someone you never met, someone you never saw, someone you never knew was the only someone for you?" -Joe Versus the Volcano,1990,"A story of love, lava and burning desire." -The Birdcage,1996,Come as you are -Joe Versus the Volcano,1990,"A story of love, lava and burning desire." -When Harry Met Sally,1998,At odds in life... in love on-line. -When Harry Met Sally,1998,At odds in life... in love on-line. -When Harry Met Sally,1998,At odds in life... in love on-line. -That Thing You Do,1996,In every life there comes a time when that thing you dream becomes that thing you do -The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" -Unforgiven,1992,"It's a hell of a thing, killing a man" -The Birdcage,1996,Come as you are -The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" -The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" -The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" -RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom -Twister,1996,Don't Breathe. Don't Look Back. -RescueDawn,2006,Based on the extraordinary true story of one man's fight for freedom -Charlie Wilson's War,2007,A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire. -The Birdcage,1996,Come as you are -Unforgiven,1992,"It's a hell of a thing, killing a man" -Unforgiven,1992,"It's a hell of a thing, killing a man" -Unforgiven,1992,"It's a hell of a thing, killing a man" -Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town -Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town -Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town -Johnny Mnemonic,1995,The hottest data on earth. In the coolest head in town -Cloud Atlas,2012,Everything is connected -Cloud Atlas,2012,Everything is connected -Cloud Atlas,2012,Everything is connected -The Da Vinci Code,2006,Break The Codes -The Da Vinci Code,2006,Break The Codes -The Da Vinci Code,2006,Break The Codes -Apollo 13,1995,"Houston, we have a problem." -Frost/Nixon,2008,400 million people were waiting for the truth. -The Da Vinci Code,2006,Break The Codes -V for Vendetta,2006,Freedom! Forever! -V for Vendetta,2006,Freedom! Forever! -V for Vendetta,2006,Freedom! Forever! -Ninja Assassin,2009,Prepare to enter a secret world of assassins -Speed Racer,2008,Speed has no limits -V for Vendetta,2006,Freedom! Forever! -Speed Racer,2008,Speed has no limits -Speed Racer,2008,Speed has no limits -Speed Racer,2008,Speed has no limits -Speed Racer,2008,Speed has no limits -Speed Racer,2008,Speed has no limits -Ninja Assassin,2009,Prepare to enter a secret world of assassins -Speed Racer,2008,Speed has no limits -Ninja Assassin,2009,Prepare to enter a secret world of assassins -The Green Mile,1999,Walk a mile you'll never forget. -The Green Mile,1999,Walk a mile you'll never forget. -Frost/Nixon,2008,400 million people were waiting for the truth. -The Green Mile,1999,Walk a mile you'll never forget. -Apollo 13,1995,"Houston, we have a problem." -The Green Mile,1999,Walk a mile you'll never forget. -The Green Mile,1999,Walk a mile you'll never forget. -The Green Mile,1999,Walk a mile you'll never forget. -Frost/Nixon,2008,400 million people were waiting for the truth. -Frost/Nixon,2008,400 million people were waiting for the truth. -Bicentennial Man,1999,One robot's 200 year journey to become an ordinary man. -Frost/Nixon,2008,400 million people were waiting for the truth. -One Flew Over the Cuckoo's Nest,1975,"If he's crazy, what does that make you?" -Hoffa,1992,He didn't want law. He wanted justice. -Hoffa,1992,He didn't want law. He wanted justice. -Hoffa,1992,He didn't want law. He wanted justice. -Apollo 13,1995,"Houston, we have a problem." -A League of Their Own,1992,Once in a lifetime you get a chance to do something different. -Twister,1996,Don't Breathe. Don't Look Back. -Apollo 13,1995,"Houston, we have a problem." -Charlie Wilson's War,2007,A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire. -Twister,1996,Don't Breathe. Don't Look Back. -Twister,1996,Don't Breathe. Don't Look Back. -The Polar Express,2004,This Holiday Season… Believe -Cast Away,2000,"At the edge of the world, his journey begins." -One Flew Over the Cuckoo's Nest,1975,"If he's crazy, what does that make you?" -Something's Gotta Give,1975,null -Something's Gotta Give,1975,null -Something's Gotta Give,1975,null -Something's Gotta Give,1975,null -Bicentennial Man,1999,One robot's 200 year journey to become an ordinary man. -Charlie Wilson's War,2007,A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire. -A League of Their Own,1992,Once in a lifetime you get a chance to do something different. -A League of Their Own,1992,Once in a lifetime you get a chance to do something different. -A League of Their Own,1992,Once in a lifetime you get a chance to do something different. -A League of Their Own,1992,Once in a lifetime you get a chance to do something different. -The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" -The Da Vinci Code,2006,Break The Codes -The Birdcage,1996,Come as you are -Unforgiven,1992,"It's a hell of a thing, killing a man" -The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" -Cloud Atlas,2012,Everything is connected -The Da Vinci Code,2006,Break The Codes -The Replacements,2000,"Pain heals, Chicks dig scars... Glory lasts forever" diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/clauses/call-subquery.adoc index 50a3dbb8e..1df778181 100644 --- a/modules/ROOT/pages/clauses/call-subquery.adoc +++ b/modules/ROOT/pages/clauses/call-subquery.adoc @@ -420,7 +420,7 @@ The following example uses a CSV file and the `LOAD CSV` clause to import more d It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+`: .friends.csv -[source, csv] +[source, csv, role="noheader" filename="friends.csv"] ---- 1,Bill,26 2,Max,27 @@ -532,7 +532,7 @@ If omitted, the default batch size is `1000` rows. The following is the same example but with one transaction every `2` input rows: .friends.csv -[source, csv, role="noheader"] +[source, csv, role="noheader", filename="friends.csv"] ---- 1,Bill,26 2,Max,27 diff --git a/modules/ROOT/pages/clauses/load-csv.adoc b/modules/ROOT/pages/clauses/load-csv.adoc index 003367ddf..5f077a7ca 100644 --- a/modules/ROOT/pages/clauses/load-csv.adoc +++ b/modules/ROOT/pages/clauses/load-csv.adoc @@ -76,7 +76,7 @@ To import data from a CSV file into Neo4j, you can use `LOAD CSV` to get the dat Then you write it to your database using the normal updating clauses of Cypher. .artists.csv -[source, csv, role="noheader"] +[source, csv, role="noheader", filename="artists.csv"] ---- 1,ABBA,1992 2,Roxette,1986 @@ -85,7 +85,7 @@ Then you write it to your database using the normal updating clauses of Cypher. ---- .Query -[source, cypher, subs=attributes+, indent=0] +[source, cypher] ---- LOAD CSV FROM 'file:///artists.csv' AS line CREATE (:Artist {name: line[1], year: toInteger(line[2])}) @@ -113,7 +113,7 @@ Accordingly, you can import data from a CSV file in a remote location into Neo4j Note that this applies to all variations of CSV files (see examples below for other variations). .data.neo4j.com/bands/artists.csv -[source, csv, role="noheader"] +[source, csv, role="noheader", filename="artists.csv"] ---- 1,ABBA,1992 2,Roxette,1986 @@ -122,7 +122,7 @@ Note that this applies to all variations of CSV files (see examples below for ot ---- .Query -[source, cypher, subs=attributes+, indent=0] +[source, cypher] ---- LOAD CSV FROM 'https://data.neo4j.com/bands/artists.csv' AS line CREATE (:Artist {name: line[1], year: toInteger(line[2])}) @@ -145,7 +145,7 @@ Labels added: 4 When your CSV file has headers, you can view each row in the file as a map instead of as an array of strings. .artists-with-headers.csv -[source] +[source, csv, role="noheaders", filename="artists-with-headers.csv"] ---- Id,Name,Year 1,ABBA,1992 @@ -155,7 +155,7 @@ Id,Name,Year ---- .Query -[source, cypher, subs=attributes+, indent=0] +[source, cypher] ---- LOAD CSV WITH HEADERS FROM 'file:///artists-with-headers.csv' AS line CREATE (:Artist {name: line.Name, year: toInteger(line.Year)}) @@ -185,7 +185,7 @@ The encoding must be written with four digits. For example, `{backslash}u003B` is equivalent to `;` (SEMICOLON). .artists-fieldterminator.csv -[source] +[source, csv, role="noheaders", filename="artists-fieldterminator.csv"] ---- 1;ABBA;1992 2;Roxette;1986 @@ -194,7 +194,7 @@ For example, `{backslash}u003B` is equivalent to `;` (SEMICOLON). ---- .Query -[source, cypher, subs=attributes+, indent=0] +[source, cypher] ---- LOAD CSV FROM 'file:///artists-fieldterminator.csv' AS line FIELDTERMINATOR ';' CREATE (:Artist {name: line[1], year: toInteger(line[2])}) @@ -227,7 +227,7 @@ For more information, see xref::clauses/call-subquery#subquery-call-in-transacti ==== .+artists.csv+ -[source, csv, role="noheader"] +[source, csv, role="noheaders", filename="artists.csv"] ---- 1,ABBA,1992 2,Roxette,1986 @@ -236,7 +236,7 @@ For more information, see xref::clauses/call-subquery#subquery-call-in-transacti ---- .Query -[source, cypher, subs=attributes+] +[source, cypher] ---- LOAD CSV FROM 'file:///artists.csv' AS line CALL { @@ -265,7 +265,7 @@ You can set the number of rows as in the example, where it is set to `500` rows. .+artists.csv+ -[source, csv, role="noheader"] +[source, csv, role="noheaders", filename="artists.csv"] ---- 1,ABBA,1992 2,Roxette,1986 @@ -274,7 +274,7 @@ You can set the number of rows as in the example, where it is set to `500` rows. ---- .Query -[source, cypher, subs=attributes+] +[source, cypher] ---- LOAD CSV FROM 'file:///artists.csv' AS line CALL { @@ -301,13 +301,13 @@ Transactions committed: 1 In this example, we both have additional quotes around the values, as well as escaped quotes inside one value. .artists-with-escaped-char.csv -[source] +[source, csv, role="noheaders", filename="artists-with-escaped-char.csv"] ---- "1","The ""Symbol""","1992" ---- .Query -[source, cypher, subs=attributes+, indent=0] +[source, cypher] ---- LOAD CSV FROM 'file:///artists-with-escaped-char.csv' AS line CREATE (a:Artist {name: line[1], year: toInteger(line[2])}) @@ -324,7 +324,7 @@ You can see that when comparing to the length of the string in this case! [role="queryresult",options="header,footer",cols="3*_ directory of t Import the _movies.csv_ file:: -[source, cypher, indent=0] +[source, cypher] ---- LOAD CSV WITH HEADERS FROM 'file:///movies.csv' AS line MERGE (m:Movie {title: line.title}) @@ -556,7 +556,7 @@ Added 38 nodes, Set 114 properties, Added 38 labels Import the _actors.csv_ file:: -[source, cypher, indent=0] +[source, cypher] ---- LOAD CSV WITH HEADERS FROM 'file:///actors.csv' AS line MATCH (m:Movie {title: line.title}) @@ -573,7 +573,7 @@ Added 102 nodes, Created 172 relationships, Set 375 properties, Added 102 labels Import the _directors.csv_ file:: -[source, cypher, indent=0] +[source, cypher] ---- LOAD CSV WITH HEADERS FROM 'file:///directors.csv' AS line MATCH (m:Movie {title: line.title}) @@ -595,7 +595,7 @@ Let's say you want to write a query to find *'Tom Hanks'*. The naive way of doing this would be to write the following: -[source, cypher, indent=0] +[source, cypher] ---- MATCH (p {name: 'Tom Hanks'}) RETURN p @@ -615,7 +615,7 @@ We can profile the query to find out why that is. You can learn more about the options for profiling queries in xref::query-tuning/query-options.adoc[] but in this case you are going to prefix our query with `PROFILE`: -[source, cypher, indent=0] +[source, cypher] ---- PROFILE MATCH (p {name: 'Tom Hanks'}) @@ -662,7 +662,7 @@ The solution to this problem is that whenever you are looking for a node you sho For this query you need to add a `Person` label. -[source, cypher, indent=0] +[source, cypher] ---- MATCH (p:Person {name: 'Tom Hanks'}) RETURN p @@ -672,7 +672,7 @@ This query will be faster than the first one, but as the number of people in you Again you can profile the query to work out why: -[source, cypher, indent=0] +[source, cypher] ---- PROFILE MATCH (p:Person {name: 'Tom Hanks'}) @@ -712,7 +712,7 @@ Once you have done that, you can again scan through all those nodes using the `F This might be acceptable in some cases but if you are going to be looking up people by name frequently then you will see better performance if you create an index on the `name` property for the `Person` label: -[source, cypher, indent=0] +[source, cypher] ---- CREATE INDEX FOR (p:Person) ON (p.name) @@ -724,14 +724,14 @@ ON (p.name) Added 1 indexes ---- -[source, cypher, indent=0] +[source, cypher] ---- CALL db.awaitIndexes ---- Now if you run the query again it will run more quickly: -[source, cypher, indent=0] +[source, cypher] ---- MATCH (p:Person {name: 'Tom Hanks'}) RETURN p @@ -739,7 +739,7 @@ RETURN p A profile for the query to see why that is: -[source, cypher, indent=0] +[source, cypher] ---- PROFILE MATCH (p:Person {name: 'Tom Hanks'}) From 4091d8d047932ad83a441b9e6c5025332c249c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Cord=C3=B3n?= Date: Tue, 14 Mar 2023 16:51:20 +0000 Subject: [PATCH 154/383] Adds docs for status report and error behaviour in CALL IN TXs (#312) --- modules/ROOT/pages/clauses/call-subquery.adoc | 255 +++++++++++++++++- 1 file changed, 247 insertions(+), 8 deletions(-) diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/clauses/call-subquery.adoc index 1df778181..e95141e26 100644 --- a/modules/ROOT/pages/clauses/call-subquery.adoc +++ b/modules/ROOT/pages/clauses/call-subquery.adoc @@ -415,6 +415,11 @@ RETURN p.name, youngerPersonsCount Subqueries can be made to execute in separate, inner transactions, producing intermediate commits. This can come in handy when doing large write operations, like batch updates, imports, and deletes. To execute a subquery in separate transactions, you add the modifier `IN TRANSACTIONS` after the subquery. +An outer transaction is opened to report back the accumulated statistics for the inner transactions +(created and deleted nodes, relationships, etc) and it will succeed or fail depending on the results +of those inner transactions. +For more information, see <>. +Canceling that outer transaction will cancel the inner ones. The following example uses a CSV file and the `LOAD CSV` clause to import more data to the example graph. It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+`: @@ -435,7 +440,7 @@ It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+` LOAD CSV FROM 'file:///friends.csv' AS line CALL { WITH line - CREATE (:PERSON {name: line[1], age: toInteger(line[2])}) + CREATE (:Person {name: line[1], age: toInteger(line[2])}) } IN TRANSACTIONS ---- @@ -601,15 +606,20 @@ The batch size of `2 ROWS` is an example given the small data set used here. For larger data sets, you might want to use larger batch sizes, such as `10000 ROWS`. ==== +=== Error behaviour [[txs_error_behaviour]] +Users can choose one of three different option flags to control the behaviour +in case of an error occurring in any of the inner transactions of `+CALL { ... } IN TRANSACTIONS+`: -=== Errors - -If an error occurs in `+CALL { ... } IN TRANSACTIONS+` the entire query fails and -both the current inner transaction and the outer transaction are rolled back. +* `ON ERROR CONTINUE` to ignore a recoverable error and continue the execution of subsequent inner transactions. +The outer transaction succeeds. +It will cause the expected variables from the failed inner query to be bound as null for that specific transaction. +* `ON ERROR BREAK` to ignore a recoverable error and stop the execution of subsequent inner transactions. The outer transaction succeeds. +It will cause expected variables from the failed inner query to be bound as null for all onward transactions (including the failed one). +* `ON ERROR FAIL` to acknowledge a recoverable error and stop the execution of subsequent inner transactions. The outer transaction fails. This is the default behaviour if no flag is explicitly specified. [IMPORTANT] ==== -On error, any previously committed inner transactions remain committed, and are not rolled back. +On error, any previously committed inner transactions remain committed, and are not rolled back. Any failed inner transactions are rolled back. ==== In the following example, the last subquery execution in the second inner transaction fails @@ -621,7 +631,7 @@ due to division by zero. UNWIND [4, 2, 1, 0] AS i CALL { WITH i - CREATE (:Example {num: 100/i}) + CREATE (:Person {num: 100/i}) } IN TRANSACTIONS OF 2 ROWS RETURN i ---- @@ -637,7 +647,7 @@ When the failure occurred, the first transaction had already been committed, so .Query [source, cypher] ---- -MATCH (e:Example) +MATCH (e:Person) RETURN e.num ---- @@ -650,6 +660,235 @@ RETURN e.num 1+d|Rows: 2 |=== +In the following example, `ON ERROR CONTINUE` is used after a failed inner transaction to execute the remaining inner transactions and not fail the outer transaction: + +.Query +[source, cypher] +---- +UNWIND [1, 0, 2, 4] AS i +CALL { + WITH i + CREATE (n:Person {num: 100/i}) // Note, fails when i = 0 + RETURN n +} IN TRANSACTIONS + OF 1 ROW + ON ERROR CONTINUE +RETURN n.num; +---- + +.Result +[role="queryresult",options="header,footer",cols="1*>. + +After each execution of the inner query finishes (successfully or not), a status value is created that records information about the execution and the transaction that executed it: + +* If the inner execution produces one or more rows as output, then a binding to this status value is added to each row, under the selected variable name. +* If the inner execution fails then a single row is produced containing a binding to this status value under the selected variable, and null bindings for all variables that should have been returned by the inner query (if any). + +The status value is a map value with the following fields: + +* `started`: `true` when the inner transaction was started, `false` otherwise. +* `committed`, true when the inner transaction changes were successfully committed, false otherwise. +* `transactionId`: the inner transaction id, or null if the transaction was not started. +* `errorMessage`, the inner transaction error message, or null in case of no error. + +Example of reporting status with `ON ERROR CONTINUE`: + +.Query +[source, cypher, indent=0] +---- +UNWIND [1, 0, 2, 4] AS i +CALL { + WITH i + CREATE (n:Person {num: 100/i}) // Note, fails when i = 0 + RETURN n +} IN TRANSACTIONS + OF 1 ROW + ON ERROR CONTINUE + REPORT STATUS AS s +RETURN n.num, s; +---- + +.Result +[role="queryresult",options="header,footer",cols="2* Date: Fri, 17 Mar 2023 10:22:16 +0100 Subject: [PATCH 155/383] Update show output column lists (#464) - some 5.0 changes that had been missed - add column type --- .../access-control/manage-privileges.adoc | 52 ++++++++++++++++--- .../pages/access-control/manage-roles.adoc | 29 ++++++----- .../pages/access-control/manage-servers.adoc | 17 +++++- .../pages/access-control/manage-users.adoc | 28 +++++++++- modules/ROOT/pages/aliases.adoc | 11 +++- .../ROOT/pages/clauses/listing-functions.adoc | 14 ++++- .../pages/clauses/listing-procedures.adoc | 15 +++++- .../ROOT/pages/clauses/listing-settings.adoc | 12 ++++- .../pages/clauses/transaction-clauses.adoc | 20 +++---- modules/ROOT/pages/constraints/syntax.adoc | 13 ++++- modules/ROOT/pages/databases.adoc | 33 ++++++++++-- .../pages/indexes-for-search-performance.adoc | 25 ++++++--- 12 files changed, 215 insertions(+), 54 deletions(-) diff --git a/modules/ROOT/pages/access-control/manage-privileges.adoc b/modules/ROOT/pages/access-control/manage-privileges.adoc index dd0fab545..1b867d74d 100644 --- a/modules/ROOT/pages/access-control/manage-privileges.adoc +++ b/modules/ROOT/pages/access-control/manage-privileges.adoc @@ -275,17 +275,53 @@ Other users' privileges cannot be listed when using a non-native auth provider. When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. For an easy overview of the existing privileges, it is recommended to use the `AS COMMANDS` version of the `SHOW` command. -This returns the privileges as the commands that are granted or denied. +This returns the column `command` of type `STRING` containing the privileges as the commands that are granted or denied. When omitting the `AS COMMANDS` clause, results will include multiple columns describing privileges: -* `access`: whether the privilege is granted or denied. -* `action`: which type of privilege this is, for example traverse, read, index management or role management. -* `resource`: what type of scope this privilege applies to, i.e. the entire DBMS, a specific database, a graph or sub-graph access. -* `graph`: the specific database or graph this privilege applies to. -* `segment`: when applicable, this privilege applies to labels, relationship types, procedures, functions or transactions. -* `role`: the role a privilege is granted to. -* `immutable`: whether or not the privilege is immutable. +[options="header", width="100%", cols="4m,6a,2m"] +|=== +| Column | Description | Type + +| access +| Whether the privilege is granted or denied. +| STRING + +| action +| The type of the privilege. +E.g., traverse, read, index management, or role management. +| STRING + +| resource +| The scope of the privilege. +E.g., the entire DBMS, a specific database, a graph, or sub-graph access. +| STRING + +| graph +| The specific database or graph the privilege applies to. +| STRING + +| segment +| The labels, relationship types, procedures, functions, transactions or settings the privilege applies to (if applicable). +| STRING + +| role +| The role the privilege is granted to. +| STRING + +| immutable +| Whether or not the privilege is immutable. + +This column is also available for the `AS COMMAND` variant using `YIELD`. +| BOOLEAN + +| user +| The user the privilege belongs to. + +Note that this is only returned for `SHOW USER [username] PRIVILEGES`. +| STRING + +|=== [[access-control-list-all-privileges]] === Examples for listing all privileges diff --git a/modules/ROOT/pages/access-control/manage-roles.adoc b/modules/ROOT/pages/access-control/manage-roles.adoc index b4cf2510e..e12aa550a 100644 --- a/modules/ROOT/pages/access-control/manage-roles.adoc +++ b/modules/ROOT/pages/access-control/manage-roles.adoc @@ -344,7 +344,9 @@ GRANT REMOVE ROLE [[access-control-list-roles]] == Listing roles -Available roles can be seen using `SHOW ROLES`: + +Available roles can be seen using `SHOW ROLES`. +This returns a single column `role` of type `STRING`, containing the role name. [source, cypher, role=noplay] ---- @@ -353,16 +355,6 @@ SHOW ROLES This is the same command as `SHOW ALL ROLES`. -When first starting a Neo4j DBMS, there are a number of built-in roles: - -* `PUBLIC` - a role that all users have granted. -By default it gives access to the home database and to execute privileges for procedures and functions. -* `reader` - can perform traverse and read operations in all databases except `system`. -* `editor` - can perform traverse, read, and write operations in all databases except `system`, but cannot create new labels or relationship types. -* `publisher` - can do the same as `editor`, but also create new labels and relationship types. -* `architect` - can do the same as `publisher` as well as create and manage indexes and constraints. -* `admin` - can do the same as all the above, as well as manage databases, aliases, users, roles, and privileges. - .Result [options="header,footer", width="100%", cols="m"] |=== @@ -378,12 +370,23 @@ By default it gives access to the home database and to execute privileges for pr 1+a|Rows: 6 |=== +When first starting a Neo4j DBMS, there are a number of built-in roles: + +* `PUBLIC` - a role that all users have granted. +By default it gives access to the home database and to execute privileges for procedures and functions. +* `reader` - can perform traverse and read operations in all databases except `system`. +* `editor` - can perform traverse, read, and write operations in all databases except `system`, but cannot create new labels or relationship types. +* `publisher` - can do the same as `editor`, but also create new labels and relationship types. +* `architect` - can do the same as `publisher` as well as create and manage indexes and constraints. +* `admin` - can do the same as all the above, as well as manage databases, aliases, users, roles, and privileges. + More information about the built-in roles can be found in link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/built-in-roles[Operations Manual -> Built-in roles] There are multiple versions of this command, the default being `SHOW ALL ROLES`. To only show roles that are assigned to users, the command is `SHOW POPULATED ROLES`. -To see which users are assigned to roles, `WITH USERS` can be added to the command. -This will give a result with one row for each user, so if a role is assigned to two users, then it will show up twice. +To see which users are assigned to which roles, `WITH USERS` can be added to the command. +This will return an additional `STRING` column, `member`, containing the username. +Since this gives a result with one row for each user, if a role is assigned to two users it will show up twice. [source, cypher, role=noplay] ---- diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index 25c9950ad..c3f5a5242 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -181,80 +181,95 @@ a| `GRANT SHOW SERVERS` The table of results shows information about the servers: -[options="header", width="100%", cols="2a,4,1,1"] +[options="header", width="100%", cols="2a,4,2m,1,1"] |=== | Column | Description +| Type | Default output | Full output | name | Name of the server. +| STRING | {check-mark} | {check-mark} | serverId | Id of the server. +| STRING | | {check-mark} | address | Bolt address of the server (if enabled). +| STRING | {check-mark} | {check-mark} | httpAddress | Http address of the server (if enabled). +| STRING | | {check-mark} | httpsAddress | Https address of the server (if enabled). +| STRING | | {check-mark} | state | Information of the state of the server: `free`, `enabled`, `deallocating`, or `dropped`. +| STRING | {check-mark} | {check-mark} | health | The availability of the server: `available` or `unavailable`. +| STRING | {check-mark} | {check-mark} | hosting | A list of databases currently hosted on the server. +| LIST OF STRING | {check-mark} | {check-mark} | requestedHosting | A list of databases that should be hosted on the server, decided by the allocator. +| LIST OF STRING | | {check-mark} | tags | Tags are user provided strings that can be used while allocating databases. +| LIST OF STRING | | {check-mark} | allowedDatabases | A list of databases allowed to be hosted on the server. +| LIST OF STRING | | {check-mark} | deniedDatabases | A list of databases not allowed to be hosted on the server. +| LIST OF STRING | | {check-mark} | modeConstraint | Constraint for the allocator to allocate only databases in this mode on the server. +| STRING | | {check-mark} | version | Neo4j version the server is running. +| STRING | | {check-mark} |=== diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/access-control/manage-users.adoc index 17f6d764b..c7042a895 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/access-control/manage-users.adoc @@ -344,36 +344,48 @@ The `SHOW USER[S] PRIVILEGES` command is only available in Neo4j Enterprise Edit The currently logged-in user can be seen using `SHOW CURRENT USER`, which will produce a table with the following columns: -[options="header", width="100%", cols="2a,4,^.^,^.^"] +[options="header", width="100%", cols="2a,4,2m,^.^,^.^"] |=== | Column | Description +| Type | Community Edition | Enterprise Edition | user | User name +| STRING | {check-mark} | {check-mark} | roles | Roles granted to the user. + +Will return `null` in community edition. +| LIST OF STRING | {cross-mark} | {check-mark} | passwordChangeRequired | If `true`, the user must change their password at the next login. +| BOOLEAN | {check-mark} | {check-mark} | suspended | If `true`, the user is currently suspended (cannot log in). + +Will return `null` in community edition. +| BOOLEAN | {cross-mark} | {check-mark} | home | The home database configured by the user, or `null` if no home database has been configured. If this database is unavailable and the user does not specify a database to use, they will not be able to log in. + +Will return `null` in community edition. +| STRING | {cross-mark} | {check-mark} |=== @@ -412,30 +424,39 @@ This command is only supported for a logged-in user and will return an empty res Available users can be seen using `SHOW USERS`, which will produce a table of users with the following columns: -[options="header", width="100%", cols="2a,4,^.^,^.^"] +[options="header", width="100%", cols="2a,4,2m,^.^,^.^"] |=== | Column | Description +| Type | Community Edition | Enterprise Edition | user | User name +| STRING | {check-mark} | {check-mark} | roles | Roles granted to the user. + +Will return `null` in community edition. +| LIST OF STRING | {cross-mark} | {check-mark} | passwordChangeRequired | If `true`, the user must change their password at the next login. +| BOOLEAN | {check-mark} | {check-mark} | suspended | If `true`, the user is currently suspended (cannot log in). + +Will return `null` in community edition. +| BOOLEAN | {cross-mark} | {check-mark} @@ -443,6 +464,9 @@ Available users can be seen using `SHOW USERS`, which will produce a table of us | The home database configured by the user, or `null` if no home database has been configured. A home database will be resolved if it is either pointing to a database or a database alias. If this database is unavailable and the user does not specify a database to use, they will not be able to log in. + +Will return `null` in community edition. +| STRING | {cross-mark} | {check-mark} |=== diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/aliases.adoc index 8fb0bd909..aa9bffb76 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/aliases.adoc @@ -273,32 +273,39 @@ The required privileges are described xref::access-control/dbms-administration.a `SHOW ALIASES FOR DATABASE` will produce a table of database aliases with the following columns: -[options="header" cols="2m,4a"] +[options="header" cols="2m,4a,2m"] |=== -| Column | Description +| Column | Description | Type | name | The fully qualified name of the database alias. label:default-output[] +| STRING | database | The name of the target database. label:default-output[] +| STRING | location | The location of the database, either `local` or `remote`. label:default-output[] +| STRING | url | Target location or `null` if the target is local. label:default-output[] +| STRING | user | User connecting to the remote database or `null` if the target database is local. label:default-output[] +| STRING | driver | The driver options for connection to the remote database or `null` if the target database is local or if no driver settings are added. List of xref::aliases.adoc#remote-alias-driver-settings[driver settings] allowed for remote database aliases. +| MAP | properties | Any properties set on the database alias. +| MAP |=== diff --git a/modules/ROOT/pages/clauses/listing-functions.adoc b/modules/ROOT/pages/clauses/listing-functions.adoc index cf8b91d26..e0c4d5ea6 100644 --- a/modules/ROOT/pages/clauses/listing-functions.adoc +++ b/modules/ROOT/pages/clauses/listing-functions.adoc @@ -21,43 +21,53 @@ The `SHOW FUNCTIONS` command will produce a table with the following columns: .List functions output -[options="header", cols="4,6"] +[options="header", cols="4,6,2"] |=== -| Column | Description +| Column | Description | Type m| name a| The name of the function. label:default-output[] +m| STRING m| category a| The function category, for example `scalar` or `string`. label:default-output[] +m| STRING m| description a| The function description. label:default-output[] +m| STRING m| signature a| The signature of the function. +m| STRING m| isBuiltIn a| Whether the function is built-in or user-defined. +m| BOOLEAN m| argumentDescription a| List of the arguments for the function, as map of strings with name, type, default, and description. +m| LIST OF MAP m| returnDescription a| The return value type. +m| STRING m| aggregating a| Whether the function is aggregating or not. +m| BOOLEAN m| rolesExecution a| List of roles permitted to execute this function. Is `null` without the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +m| LIST OF STRING m| rolesBoostedExecution a| List of roles permitted to use boosted mode when executing this function. Is `null` without the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +m| LIST OF STRING |=== diff --git a/modules/ROOT/pages/clauses/listing-procedures.adoc b/modules/ROOT/pages/clauses/listing-procedures.adoc index 9f31ba932..f11b03808 100644 --- a/modules/ROOT/pages/clauses/listing-procedures.adoc +++ b/modules/ROOT/pages/clauses/listing-procedures.adoc @@ -19,46 +19,57 @@ Full output: `SHOW PROCEDURES YIELD *`. This command will produce a table with the following columns: .List procedures output -[options="header", cols="4,6"] +[options="header", cols="4,6,2"] |=== -| Column | Description +| Column | Description | Type m| name a| The name of the procedure. label:default-output[] +m| STRING m| description a| The procedure description. label:default-output[] +m| STRING m| mode a| The procedure mode, for example `READ` or `WRITE`. label:default-output[] +m| STRING m| worksOnSystem a| Whether the procedure can be run on the `system` database or not. label:default-output[] +m| BOOLEAN m| signature a| The signature of the procedure. +m| STRING m| argumentDescription a| List of the arguments for the procedure, as map of strings with name, type, default, and description. +m| LIST OF MAP m| returnDescription a| List of the returned values for the procedure, as map of strings with name, type, and description. +m| LIST OF MAP m| admin a| `true` if this procedure is an admin procedure. +m| BOOLEAN m| rolesExecution a| List of roles permitted to execute this procedure. Is `null` without the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +m| LIST OF STRING m| rolesBoostedExecution a| List of roles permitted to use boosted mode when executing this procedure. Is `null` without the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +m| LIST OF STRING m| option a| Map of extra output, e.g. if the procedure is deprecated. +m| MAP |=== diff --git a/modules/ROOT/pages/clauses/listing-settings.adoc b/modules/ROOT/pages/clauses/listing-settings.adoc index 16c51d2a6..39b8d8e1f 100644 --- a/modules/ROOT/pages/clauses/listing-settings.adoc +++ b/modules/ROOT/pages/clauses/listing-settings.adoc @@ -27,36 +27,44 @@ The `SHOW SETTINGS` command will produce a table with the following columns: .Show settings output -[options="header", cols="4,6"] +[options="header", cols="4,6,2"] |=== -| Column | Description +| Column | Description | Type m| name a| The name of the setting. label:default-output[] +m| STRING m| value a| The current value of the setting. label:default-output[] +m| STRING m| isDynamic a| Whether the value of the setting can be updated dynamically, without restarting the server. For dynamically updating a setting value, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/dynamic-settings/[Update dynamic settings]. label:default-output[] +m| BOOLEAN m| defaultValue a| The default value of the setting. label:default-output[] +m| STRING m| description a| The setting description. label:default-output[] +m| STRING m| startupValue a| The value of the setting at last startup. +m| STRING m| isExplicitlySet a| Whether the value of the setting is explicitly set by the user, either through configuration or dynamically. +m| BOOLEAN m| validValues a| A description of valid values for the setting. +m| STRING |=== diff --git a/modules/ROOT/pages/clauses/transaction-clauses.adoc b/modules/ROOT/pages/clauses/transaction-clauses.adoc index d7ad742e4..5053bb3a7 100644 --- a/modules/ROOT/pages/clauses/transaction-clauses.adoc +++ b/modules/ROOT/pages/clauses/transaction-clauses.adoc @@ -127,12 +127,12 @@ m| MAP m| activeLockCount a| Count of active locks held by the transaction. -m| LONG +m| INTEGER // New in 5.0 m| currentQueryActiveLockCount a| Count of active locks held by the query currently executing in this transaction. -m| LONG +m| INTEGER m| cpuTime a| CPU time that has been actively spent executing the transaction or `null` if unavailable. @@ -164,38 +164,38 @@ m| DURATION // New in 5.0 m| currentQueryIdleTime a| Idle time for the query currently executing in this transaction, or `null` if unavailable or no query is currently executing. -m| LONG +m| DURATION // New in 5.0 m| currentQueryAllocatedBytes a| The number of bytes allocated on the heap so far by the query currently executing in this transaction, or `null` if unavailable or no query is currently executing. -m| LONG +m| INTEGER m| allocatedDirectBytes a| Amount of off-heap (native) memory allocated by the transaction in bytes or `null` if unavailable. -m| LONG +m| INTEGER m| estimatedUsedHeapMemory a| The estimated amount of used heap memory allocated by the transaction in bytes or `null` if unavailable. -m| LONG +m| INTEGER m| pageHits a| The total number of page cache hits that the transaction performed. -m| LONG +m| INTEGER m| pageFaults a| The total number of page cache faults that the transaction performed. -m| LONG +m| INTEGER // New in 5.0 m| currentQueryPageHits a| The total number of page cache hits that the query currently executing in this transaction performed. -m| LONG +m| INTEGER // New in 5.0 m| currentQueryPageFaults a| The total number of page cache faults that the query currently executing in this transaction performed. -m| LONG +m| INTEGER m| initializationStackTrace a| The initialization stacktrace for this transaction, or an empty string if unavailable. diff --git a/modules/ROOT/pages/constraints/syntax.adoc b/modules/ROOT/pages/constraints/syntax.adoc index dee270c2a..79c2e9496 100644 --- a/modules/ROOT/pages/constraints/syntax.adoc +++ b/modules/ROOT/pages/constraints/syntax.adoc @@ -331,38 +331,47 @@ This is the default if none is given. The returned columns from the show command is: .Listing constraints output -[options="header", width="100%", cols="4m,6a"] +[options="header", width="100%", cols="4m,6a,2m"] |=== -| Column | Description +| Column | Description | Type | id | The id of the constraint. label:default-output[] +| INTEGER | name | Name of the constraint (explicitly set by the user or automatically assigned). label:default-output[] +| STRING | type | The ConstraintType of this constraint (`UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, or `NODE_KEY`). label:default-output[] // TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints //| The ConstraintType of this constraint (`UNIQUENESS` (node uniqueness), `RELATIONSHIP_UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, `NODE_KEY`, or `RELATIONSHIP_KEY`). label:default-output[] +| STRING | entityType | Type of entities this constraint represents (nodes or relationship). label:default-output[] +| STRING | labelsOrTypes | The labels or relationship types of this constraint. label:default-output[] +| LIST OF STRING | properties | The properties of this constraint. label:default-output[] +| LIST OF STRING | ownedIndex | The name of the index associated with the constraint or `null`, in case no index is associated with it. label:default-output[] +| STRING | options | The options passed to `CREATE` command, for the index associated to the constraint, or `null` if no index is associated with the constraint. +| MAP | createStatement | Statement used to create the constraint. +| STRING |=== diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index f1aa378d5..79bdab6b1 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -131,18 +131,21 @@ There are four different commands for listing databases: These commands return the following columns: .Listing databases output -[options="header", width="100%", cols="4m,6a"] +[options="header", width="100%", cols="4m,6a,2m"] |=== -| Column | Description +| Column | Description | Type | name | The name of the database. label:default-output[] +| STRING | type | The type of the database: `system`, `standard`, or `composite`. label:default-output[] +| STRING | aliases | The names of any aliases the database may have. label:default-output[] +| LIST OF STRING | access | The database access mode, either `read-write` or `read-only`. label:default-output[] @@ -151,32 +154,41 @@ These commands return the following columns: ==== A database may be described as read-only when using `ALTER DATABASE ... SET ACCESS READ ONLY`. ==== +| STRING | databaseID | The database unique ID. +| STRING | serverID | The server instance ID. +| STRING | address | Instance address in a clustered DBMS. The default for a standalone database is `neo4j://localhost:7687`. label:default-output[] +| STRING | role | The current role of the database (`primary`, `secondary`, `unknown`). label:default-output[] +| STRING | writer |`true` for the database node that accepts writes (this node is the leader for this database in a cluster or this is a standalone instance). label:default-output[] +| BOOLEAN | requestedStatus | The expected status of the database. label:default-output[] +| STRING | currentStatus | The actual status of the database. label:default-output[] +| STRING -| error -| An error message explaining why the database is not in the correct state. label:default-output[] +| statusMessage +| A message explaining the status of the database, often explaining why it is not in the correct state. label:default-output[] +| STRING | default | @@ -186,6 +198,7 @@ Show if this is the default database for the DBMS. label:default-output[] ==== Not returned by `SHOW HOME DATABASE` or `SHOW DEFAULT DATABASE`. ==== +| BOOLEAN | home | @@ -195,31 +208,39 @@ Shown if this is the home database for the current user. label:default-output[] ==== Not returned by `SHOW HOME DATABASE` or `SHOW DEFAULT DATABASE`. ==== +| BOOLEAN | `currentPrimariesCount` | Number of primaries for this database reported as running currently. It is the same as the number of rows where `role=primary` and `name=this database`. +| INTEGER | `currentSecondariesCount` | Number of secondaries for this database reported as running currently. It is the same as the number of rows where `role=secondary` and `name=this database`. +| INTEGER | `requestedPrimariesCount` | The requested number of primaries for this database. May be lower than current if the DBMS is currently reducing the number of copies of the database, or higher if it is currently increasing the number of copies. +| INTEGER | `requestedSecondariesCount` | The requested number of secondaries for this database. May be lower than current if the DBMS is currently reducing the number of copies of the database, or higher if it is currently increasing the number of copies. +| INTEGER | creationTime | The date and time at which the database was created. +| DATETIME | lastStartTime | The date and time at which the database was last started. +| DATETIME | lastStopTime | The date and time at which the database was last stopped. +| DATETIME | store a| @@ -231,17 +252,21 @@ The value is a string formatted as: ---- {storage engine}-{store format}-{major version}.{minor version} ---- +| STRING | lastCommittedTxn | The ID of the last transaction received. +| INTEGER | replicationLag | Number of transactions the current database is behind compared to the database on the primary instance. The lag is expressed in negative integers. In standalone environments, the value is always `0`. +| INTEGER |constituents |The names of any constituents the database may have. label:default-output[] +| LIST OF STRING |=== diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index 1d8e18fa5..9a4ec10d7 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -1291,49 +1291,62 @@ There already exists a constraint called 'bookRecommendations'. Listing indexes can be done with `SHOW INDEXES`, which will produce a table with the following columns: .List indexes output -[options="header", cols="4,6"] +[options="header", cols="4,6,2"] |=== -| Column | Description +| Column | Description | Type | `id` | The id of the index. label:default-output[] +| `INTEGER` | `name` | Name of the index (explicitly set by the user or automatically assigned). label:default-output[] +| `STRING` | `state` | Current state of the index. label:default-output[] +| `STRING` | `populationPercent` | % of index population. label:default-output[] +| `FLOAT` | `type` | The IndexType of this index (`FULLTEXT`, `LOOKUP`, `POINT`, `RANGE`, or `TEXT`). label:default-output[] - -// New in 5.0 -| `owningConstraint` -| The name of the constraint the index is associated with or `null`, in case it is not associated with any constraint. label:default-output[] +| `STRING` | `entityType` | Type of entities this index represents (nodes or relationship). label:default-output[] +| `STRING` | `labelsOrTypes` | The labels or relationship types of this index. label:default-output[] +| `LIST OF STRING` | `properties` | The properties of this index. label:default-output[] +| `LIST OF STRING` | `indexProvider` | The index provider for this index. label:default-output[] +| `STRING` + +// New in 5.0 +| `owningConstraint` +| The name of the constraint the index is associated with or `null` if the index is not associated with any constraint. label:default-output[] +| `STRING` | `options` | The options passed to `CREATE` command. +| `MAP` | `failureMessage` | The failure description of a failed index. +| `STRING` | `createStatement` | Statement used to create the index. +| `STRING` |=== From d9f0e5c1ec9ea9044fc09e0d83e649936e9db4da Mon Sep 17 00:00:00 2001 From: Lidia Zuin <102308961+lidiazuin@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:37:31 +0100 Subject: [PATCH 156/383] Adding reference to dbms in the command for set-initial-password (#472) (#474) The original command line includes dbms, whereas here in the text it is not mentioned. Cherry-picked from https://github.com/neo4j/docs-cypher/pull/472 --- modules/ROOT/pages/access-control/manage-users.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/access-control/manage-users.adoc index c7042a895..502fc0cce 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/access-control/manage-users.adoc @@ -495,7 +495,7 @@ SHOW USERS |=== When first starting a Neo4j DBMS, there is always a single default user `neo4j` with administrative privileges. -It is possible to set the initial password using link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/set-initial-password[neo4j-admin set-initial-password], otherwise it is necessary to change the password after the first login. +It is possible to set the initial password using link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/set-initial-password[`neo4j-admin dbms set-initial-password `], otherwise it is necessary to change the password after the first login. .Show user ====== From d9a468c598c2d47e5250b24608859132d7fb8283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Cord=C3=B3n?= Date: Mon, 20 Mar 2023 10:05:27 +0000 Subject: [PATCH 157/383] =?UTF-8?q?Revert=20"Adds=20docs=20for=20status=20?= =?UTF-8?q?report=20and=20error=20behaviour=20in=20CALL=20IN=20TX=E2=80=A6?= =?UTF-8?q?=20(#477)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Why Because dev in the monorepo is still pointing at neo4j 5.6, but these docs refer to 5.7, so they need for dev to be repointed at neo4j dev. --- modules/ROOT/pages/clauses/call-subquery.adoc | 255 +----------------- 1 file changed, 8 insertions(+), 247 deletions(-) diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/clauses/call-subquery.adoc index e95141e26..1df778181 100644 --- a/modules/ROOT/pages/clauses/call-subquery.adoc +++ b/modules/ROOT/pages/clauses/call-subquery.adoc @@ -415,11 +415,6 @@ RETURN p.name, youngerPersonsCount Subqueries can be made to execute in separate, inner transactions, producing intermediate commits. This can come in handy when doing large write operations, like batch updates, imports, and deletes. To execute a subquery in separate transactions, you add the modifier `IN TRANSACTIONS` after the subquery. -An outer transaction is opened to report back the accumulated statistics for the inner transactions -(created and deleted nodes, relationships, etc) and it will succeed or fail depending on the results -of those inner transactions. -For more information, see <>. -Canceling that outer transaction will cancel the inner ones. The following example uses a CSV file and the `LOAD CSV` clause to import more data to the example graph. It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+`: @@ -440,7 +435,7 @@ It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+` LOAD CSV FROM 'file:///friends.csv' AS line CALL { WITH line - CREATE (:Person {name: line[1], age: toInteger(line[2])}) + CREATE (:PERSON {name: line[1], age: toInteger(line[2])}) } IN TRANSACTIONS ---- @@ -606,20 +601,15 @@ The batch size of `2 ROWS` is an example given the small data set used here. For larger data sets, you might want to use larger batch sizes, such as `10000 ROWS`. ==== -=== Error behaviour [[txs_error_behaviour]] -Users can choose one of three different option flags to control the behaviour -in case of an error occurring in any of the inner transactions of `+CALL { ... } IN TRANSACTIONS+`: -* `ON ERROR CONTINUE` to ignore a recoverable error and continue the execution of subsequent inner transactions. -The outer transaction succeeds. -It will cause the expected variables from the failed inner query to be bound as null for that specific transaction. -* `ON ERROR BREAK` to ignore a recoverable error and stop the execution of subsequent inner transactions. The outer transaction succeeds. -It will cause expected variables from the failed inner query to be bound as null for all onward transactions (including the failed one). -* `ON ERROR FAIL` to acknowledge a recoverable error and stop the execution of subsequent inner transactions. The outer transaction fails. This is the default behaviour if no flag is explicitly specified. +=== Errors + +If an error occurs in `+CALL { ... } IN TRANSACTIONS+` the entire query fails and +both the current inner transaction and the outer transaction are rolled back. [IMPORTANT] ==== -On error, any previously committed inner transactions remain committed, and are not rolled back. Any failed inner transactions are rolled back. +On error, any previously committed inner transactions remain committed, and are not rolled back. ==== In the following example, the last subquery execution in the second inner transaction fails @@ -631,7 +621,7 @@ due to division by zero. UNWIND [4, 2, 1, 0] AS i CALL { WITH i - CREATE (:Person {num: 100/i}) + CREATE (:Example {num: 100/i}) } IN TRANSACTIONS OF 2 ROWS RETURN i ---- @@ -647,7 +637,7 @@ When the failure occurred, the first transaction had already been committed, so .Query [source, cypher] ---- -MATCH (e:Person) +MATCH (e:Example) RETURN e.num ---- @@ -660,235 +650,6 @@ RETURN e.num 1+d|Rows: 2 |=== -In the following example, `ON ERROR CONTINUE` is used after a failed inner transaction to execute the remaining inner transactions and not fail the outer transaction: - -.Query -[source, cypher] ----- -UNWIND [1, 0, 2, 4] AS i -CALL { - WITH i - CREATE (n:Person {num: 100/i}) // Note, fails when i = 0 - RETURN n -} IN TRANSACTIONS - OF 1 ROW - ON ERROR CONTINUE -RETURN n.num; ----- - -.Result -[role="queryresult",options="header,footer",cols="1*>. - -After each execution of the inner query finishes (successfully or not), a status value is created that records information about the execution and the transaction that executed it: - -* If the inner execution produces one or more rows as output, then a binding to this status value is added to each row, under the selected variable name. -* If the inner execution fails then a single row is produced containing a binding to this status value under the selected variable, and null bindings for all variables that should have been returned by the inner query (if any). - -The status value is a map value with the following fields: - -* `started`: `true` when the inner transaction was started, `false` otherwise. -* `committed`, true when the inner transaction changes were successfully committed, false otherwise. -* `transactionId`: the inner transaction id, or null if the transaction was not started. -* `errorMessage`, the inner transaction error message, or null in case of no error. - -Example of reporting status with `ON ERROR CONTINUE`: - -.Query -[source, cypher, indent=0] ----- -UNWIND [1, 0, 2, 4] AS i -CALL { - WITH i - CREATE (n:Person {num: 100/i}) // Note, fails when i = 0 - RETURN n -} IN TRANSACTIONS - OF 1 ROW - ON ERROR CONTINUE - REPORT STATUS AS s -RETURN n.num, s; ----- - -.Result -[role="queryresult",options="header,footer",cols="2* Date: Mon, 20 Mar 2023 15:14:32 +0100 Subject: [PATCH 158/383] Fix link on predicate functions page (#478) https://trello.com/c/nZUzxV8v/4797-broken-link-in-the-cypher-manual --- modules/ROOT/pages/functions/predicate.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 8ec6c6768..ebaa29ad1 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -256,7 +256,7 @@ This query returns the `name` property of every `Person` node, along with a bool ==== The *function* `exists()` looks very similar to the *expression* `+EXISTS { ... }+`, but they are not related. -See xref::clauses/where.adoc#existential-subqueries[Using EXISTS subqueries] for more information. +See xref::syntax/expressions.adoc#existential-subqueries[Using EXISTS subqueries] for more information. ==== From 6d5a66ead89935b3acf0026477a39ef2c12ab3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 22 Mar 2023 10:17:55 +0100 Subject: [PATCH 159/383] update Neo4j version (#481) --- antora.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antora.yml b/antora.yml index 67e3512a8..37cd44dac 100644 --- a/antora.yml +++ b/antora.yml @@ -7,5 +7,5 @@ nav: asciidoc: attributes: neo4j-version: '5' - neo4j-version-minor: '5.6' - neo4j-version-exact: '5.6.0' + neo4j-version-minor: '5.7' + neo4j-version-exact: '5.7.0' From bf3ade46eb21da5c005b82722a4672028aa406d7 Mon Sep 17 00:00:00 2001 From: Satia Herfert Date: Wed, 22 Mar 2023 10:58:41 +0100 Subject: [PATCH 160/383] Deprecate `connectComponentsPlanner` (#468) --- ...ions-additions-removals-compatibility.adoc | 28 +++++++++++++++++++ .../pages/query-tuning/query-options.adoc | 8 +++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index f832259e2..a199cf61f 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -10,6 +10,34 @@ New features are added to the language continuously, and occasionally, some feat This section lists all of the features that have been removed, deprecated, added, or extended in different Cypher versions. Replacement syntax for deprecated and removed features are also indicated. +[[cypher-deprecations-additions-removals-5.7]] +== Version 5.7 + +=== Deprecated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:deprecated[] +[source, cypher, role="noheader"] +---- +CYPHER connectComponentsPlanner=greedy MATCH (a), (b) RETURN * +---- + +[source, cypher, role="noheader"] +---- +CYPHER connectComponentsPlanner=idp MATCH (a), (b) RETURN * +---- +a| + +The Cypher query option `connectComponentsPlanner` is deprecated and will be removed without a replacement. +The product's default behavior of using a cost-based IDP search algorithm when combining sub-plans will be kept. +|=== + [[cypher-deprecations-additions-removals-5.6]] == Version 5.6 diff --git a/modules/ROOT/pages/query-tuning/query-options.adoc b/modules/ROOT/pages/query-tuning/query-options.adoc index 4c756d198..e075f4f41 100644 --- a/modules/ROOT/pages/query-tuning/query-options.adoc +++ b/modules/ROOT/pages/query-tuning/query-options.adoc @@ -84,7 +84,7 @@ Using this option can significantly _increase_ the planning time of the query. [[cypher-connect-components-planner]] -== Cypher connect-components planner +== Cypher connect-components planner label:deprecated[] One part of the Cypher planner is responsible for combining sub-plans for separate patterns into larger plans - a task referred to as _connecting components_. @@ -117,6 +117,12 @@ Using this option can significantly _increase_ the planning time of the query bu |=== +[IMPORTANT] +==== +The Cypher query option `connectComponentsPlanner` is deprecated and will be removed without a replacement. +The product's default behavior of using a cost-based IDP search algorithm when combining sub-plans will be kept. +==== + [[cypher-update-strategy]] == Cypher update strategy From 79b3c79046345fdb6dfba7468682807312c575fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 23 Mar 2023 09:50:20 +0100 Subject: [PATCH 161/383] New Cypher Manual introduction (#470) New introduction contains 4 subsections: - Cypher Manual: intended readership, content & structure - Cypher: overview & tutorial - Cypher & Neo4j - Cypher & Aura This PR removes: - current home page - Cypher path matching (language is archaic and out of place in intro - key information will be added to new subchapter "overview" under new chapter "Patterns" (To be written)). This PR relocates: - Neo4j, databases & graphs (restructured and in subchapter "Key terminology" under Cypher & Neo4j) - Querying, administering, and administering (heavily restructured and in subchapter "Cypher tutorial" under Cypher: Overview & tutorial) - Clause Composition (relocated as first chapter of Clauses chapter) - Transactions (as subchapters under Cypher & Neo4j) --------- --- antora.yml | 2 +- modules/ROOT/content-nav.adoc | 13 +- modules/ROOT/images/introduction_example1.svg | 1 + modules/ROOT/images/introduction_example2.svg | 1 + modules/ROOT/images/introduction_schema.svg | 9 + .../clause_composition.adoc | 0 modules/ROOT/pages/clauses/union.adoc | 2 +- .../ROOT/pages/execution-plans/operators.adoc | 2 +- modules/ROOT/pages/index.adoc | 98 --- .../{aura.adoc => cypher_aura.adoc} | 18 +- .../ROOT/pages/introduction/cypher_neo4j.adoc | 169 ++++ .../pages/introduction/cypher_overview.adoc | 76 ++ .../pages/introduction/cypher_tutorial.adoc | 831 ++++++++++++++++++ modules/ROOT/pages/introduction/index.adoc | 146 +-- 14 files changed, 1136 insertions(+), 232 deletions(-) create mode 100644 modules/ROOT/images/introduction_example1.svg create mode 100644 modules/ROOT/images/introduction_example2.svg create mode 100644 modules/ROOT/images/introduction_schema.svg rename modules/ROOT/pages/{introduction => clauses}/clause_composition.adoc (100%) delete mode 100644 modules/ROOT/pages/index.adoc rename modules/ROOT/pages/introduction/{aura.adoc => cypher_aura.adoc} (76%) create mode 100644 modules/ROOT/pages/introduction/cypher_neo4j.adoc create mode 100644 modules/ROOT/pages/introduction/cypher_overview.adoc create mode 100644 modules/ROOT/pages/introduction/cypher_tutorial.adoc diff --git a/antora.yml b/antora.yml index 37cd44dac..47c896cc7 100644 --- a/antora.yml +++ b/antora.yml @@ -1,7 +1,7 @@ name: cypher-manual title: Cypher Manual version: '5' -start_page: ROOT:index.adoc +start_page: ROOT:introduction/index.adoc nav: - modules/ROOT/content-nav.adoc asciidoc: diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index a2063f57c..ca192bcfe 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -1,12 +1,8 @@ -* xref:index.adoc[] - * xref:introduction/index.adoc[] -** xref:introduction/neo4j-databases-graphs.adoc[] -** xref:introduction/querying-updating-administering.adoc[] -** xref:introduction/transactions.adoc[] -** xref:introduction/uniqueness.adoc[] -** xref:introduction/clause_composition.adoc[] -** xref:introduction/aura.adoc[] +** xref:introduction/cypher_overview.adoc[] +** xref:introduction/cypher_tutorial.adoc[] +** xref:introduction/cypher_neo4j.adoc[] +** xref:introduction/cypher_aura.adoc[] * xref:syntax/index.adoc[] ** xref:syntax/values.adoc[] @@ -25,6 +21,7 @@ ** xref:syntax/working-with-null.adoc[] * xref:clauses/index.adoc[] +** xref:clauses/clause_composition.adoc[] ** xref:clauses/match.adoc[] ** xref:clauses/optional-match.adoc[] ** xref:clauses/return.adoc[] diff --git a/modules/ROOT/images/introduction_example1.svg b/modules/ROOT/images/introduction_example1.svg new file mode 100644 index 000000000..82738cb33 --- /dev/null +++ b/modules/ROOT/images/introduction_example1.svg @@ -0,0 +1 @@ +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)ACTED_INACTED_INACTED_INACTED_INDIRECTEDACTED_INACTED_INACTED_INACTED_INACTED_INACTED_INACTED_INACTED_IN Tom Hanks Apollo 13 You've M… A LeagueO… Joe VersusVo… That Thing You Do The Da Vinci Code Cloud Atlas Cast Away The Green Mile Sleepless in Seattl The Polar Express Charlie Wilson's Wa \ No newline at end of file diff --git a/modules/ROOT/images/introduction_example2.svg b/modules/ROOT/images/introduction_example2.svg new file mode 100644 index 000000000..bb0636913 --- /dev/null +++ b/modules/ROOT/images/introduction_example2.svg @@ -0,0 +1 @@ +Neo4j Graph VisualizationCreated using Neo4j (http://www.neo4j.com/)ACTED_INREVIEWEDREVIEWEDACTED_IN Keanu Reeves Tom Hanks The Repla… Jessica Thom… The Da Vinci Code \ No newline at end of file diff --git a/modules/ROOT/images/introduction_schema.svg b/modules/ROOT/images/introduction_schema.svg new file mode 100644 index 000000000..38a117296 --- /dev/null +++ b/modules/ROOT/images/introduction_schema.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/modules/ROOT/pages/introduction/clause_composition.adoc b/modules/ROOT/pages/clauses/clause_composition.adoc similarity index 100% rename from modules/ROOT/pages/introduction/clause_composition.adoc rename to modules/ROOT/pages/clauses/clause_composition.adoc diff --git a/modules/ROOT/pages/clauses/union.adoc b/modules/ROOT/pages/clauses/union.adoc index 0493b752c..b2e94956a 100644 --- a/modules/ROOT/pages/clauses/union.adoc +++ b/modules/ROOT/pages/clauses/union.adoc @@ -22,7 +22,7 @@ If any of the queries in a UNION contain updates, the order of queries in the UN Any clause before the UNION cannot observe writes made by a clause after the UNION. Any clause after UNION can observe all writes made by a clause before the UNION. -For details see xref::introduction/clause_composition.adoc#cypher-clause-composition-union-queries[clause composition in queries with `UNION`] for details. +For details see xref::clauses/clause_composition.adoc#cypher-clause-composition-union-queries[clause composition in queries with `UNION`] for details. ==== image:graph_union_clause.svg[] diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index ad876773f..f0350279d 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -3439,7 +3439,7 @@ This is done to ensure isolation between parts of the query plan that might othe Values from the graph are fetched in a lazy manner; i.e. a pattern matching might not be fully exhausted before updates are applied. To maintain correct semantics, the query planner will insert `Eager` operators into the query plan to prevent updates from influencing pattern matching, or other read operations. This scenario is exemplified by the query below, where the `DELETE` clause would otherwise influence both the `MATCH` clause and the `MERGE` clause. -For more information on how the `Eager` operator can ensure correct semantics, see the section on xref::introduction/clause_composition.adoc[Clause composition]. +For more information on how the `Eager` operator can ensure correct semantics, see the section on xref::clauses/clause_composition.adoc[Clause composition]. The `Eager` operator can cause high memory usage when importing data or migrating graph structures. In such cases, the operations should be split into simpler steps; e.g. importing nodes and relationships separately. diff --git a/modules/ROOT/pages/index.adoc b/modules/ROOT/pages/index.adoc deleted file mode 100644 index 0eb155bf2..000000000 --- a/modules/ROOT/pages/index.adoc +++ /dev/null @@ -1,98 +0,0 @@ -:description: This is the Cypher Query Language documentation for Neo4j, authored by the Neo4j Team. - -[[cypher-manual]] -= The Neo4j Cypher Manual v{neo4j-version} -:neo4j-buildnumber: {neo4j-version-minor} - -Cypher is Neo4j's graph query language that allows users to store and retrieve data from the graph database. -It is a declarative, SQL-inspired language for describing visual patterns in graphs. -The syntax provides a visual and logical way to match patterns of nodes and relationships in the graph. - - -== Documentation updates for Neo4j 5 - -Neo4j {neo4j-version} includes a number of new features and updates. -A highlight of these include: - -* Cypher syntax improvements with Graph Pattern Matching (relationships and labels): -+ -** In `MATCH` clauses, `WHERE` can be placed inside a relationship pattern to filter relationships. -** In `MATCH` clauses, nodes and relationships can be filtered using more sophisticated label (type) expressions. -** Simpler alternative syntax to navigate and traverse graphs using the following operators: -*** `&`: logical `AND` -*** `|`: logical `OR` -*** `!`: logical `NOT` -*** `%`: a "wildcard", meaning "any label" (in Cypher this translates to `size(labels(n)) > 0`). + - -+ -For more information, see the xref:syntax/expressions.adoc#label-expressions[section on Label expressions] and in xref:clauses/where.adoc[the `WHERE` clause]. - -* New `elementID` for graph objects: -+ -New IDs are introduced to uniquely identify graph elements in Neo4j databases. -Node ID will exist with each release of Neo4j {neo4j-version}. -+ -For more information, see xref:functions/scalar.adoc#functions-elementid[`elementId()`]. - -* Composite databases. -+ -Composite databases allow queries that access multiple graphs at once. -You can create, update, and remove configurations without a restart, whether the database is within the same cluster, or hosted remotely. -+ -For more information on composite databases, and how to create composite databases, see link:https://neo4j.com/docs/operations-manual/current/composite-databases/[Operations Manual -> Composite databases], and xref:databases.adoc#administration-databases-create-composite-database[Creating composite databases]. - -* Immutable privileges. -+ -Immutable privileges are useful for restricting the actions of users who themselves are able to administer privileges. + -For more information, see xref:access-control/privileges-immutable.adoc[Immutable privileges]. - -* `Execute` and `ExecuteBoosted` privilege. -+ -The permissions for the execution of admin procedures have been refreshed; these two privileges are now hierarchically related. -+ -For more information, see xref:access-control/dbms-administration.adoc#access-control-execute-procedure[the `EXECUTE PROCEDURE` privilege] and xref:access-control/dbms-administration.adoc#access-control-execute-boosted-procedure[the `EXECUTE BOOSTED PROCEDURE` privilege]. - -* `EXISTS` and `COUNT` are now both expressions. -+ -For more information, see xref:syntax/expressions.adoc#cypher-subquery-expressions[Subquery expressions]. - -* `SHOW` and `TERMINATE TRANSACTIONS` improvements. -+ -You can now combine these two commands in the same query. -The ability to yield and filter the output from `TERMINATE TRANSACTIONS` has been added. -+ -For more information, see xref:deprecations-additions-removals-compatibility.adoc#_updated_features[Updated features list]. - -* `SHOW SETTINGS` clause. -+ -You can now query configuration settings using `SHOW SETTINGS` clause. -+ -For more information, see xref:clauses/listing-settings.adoc[SHOW SETTINGS]. - -* Changes to Neo4j indexes: -+ -** The B-tree index type has been removed. -** New Range and Point index types are available now. -** Faster Text index provider for `ENDS WITH` and `CONTAINS` queries is introduced. -** Full-text indexes can now index lists of strings. - -+ -For more information, see xref:indexes-for-search-performance.adoc[new index types]. - - -ifdef::backend-html5[(C) {copyright}] -ifndef::backend-pdf[] - -Documentation license: link:{common-license-page-uri}[Creative Commons 4.0] -endif::[] -ifdef::backend-pdf[] -(C) {copyright} - -Documentation license: <> -endif::[] - -ifdef::backend-pdf[] -include::license.adoc[leveloffset=+1] -endif::[] - - diff --git a/modules/ROOT/pages/introduction/aura.adoc b/modules/ROOT/pages/introduction/cypher_aura.adoc similarity index 76% rename from modules/ROOT/pages/introduction/aura.adoc rename to modules/ROOT/pages/introduction/cypher_aura.adoc index 449df071e..ad865f2b7 100644 --- a/modules/ROOT/pages/introduction/aura.adoc +++ b/modules/ROOT/pages/introduction/cypher_aura.adoc @@ -1,12 +1,16 @@ -= Aura and Cypher +[[cypher-aura]] += Cypher and Aura +:description: This section provides an introduction to the Cypher query language. -== Introduction +This section gives a brief overview of Aura, and how Cypher differs to users of Aura. + +== What is Aura? Aura is Neo4j's fully managed cloud service. It consists of AuraDB and AuraDS. AuraDB is a graph database service for developers building intelligent applications, and AuraDS is a Graph Data Science (GDS) service for data scientists building predictive models and analytics workflows. -AuraDB is available on the following tiers: +AuraDB is available on the following tiers: * AuraDB Free * AuraDB Pro @@ -14,12 +18,12 @@ AuraDB is available on the following tiers: For more information, see link:{neo4j-docs-base-uri}/aura/auradb[Aura docs - Neo4j AuraDB overview]. -AuraDS is available on the following tiers: +AuraDS is available on the following tiers: * AuraDS Pro * AuraDS Enterprise -For more information, see link:{neo4j-docs-base-uri}/aura/aurads[Aura docs - Neo4j AuraDS overview] +For more information, see link:{neo4j-docs-base-uri}/aura/aurads[Aura docs - Neo4j AuraDS overview]. == Using Cypher on Aura @@ -36,8 +40,8 @@ The Cypher Manual uses two different labels to differentiate this distinction: [options="header,cols=""2a,2a"] |=== | Label | Description -| label:not-on-aura[] | Cypher feature not available on any tier of Aura. -| label:aura-db-enterprise[] | Cypher feature only available on AuraDB Enterprise. +| label:not-on-aura[Not Available on Aura] | Cypher feature not available on any tier of Aura. +| label:aura-db-enterprise[AuraDB Enterprise] | Cypher feature only available on AuraDB Enterprise. |=== //// diff --git a/modules/ROOT/pages/introduction/cypher_neo4j.adoc b/modules/ROOT/pages/introduction/cypher_neo4j.adoc new file mode 100644 index 000000000..01ceb8486 --- /dev/null +++ b/modules/ROOT/pages/introduction/cypher_neo4j.adoc @@ -0,0 +1,169 @@ += Cypher and Neo4j +:description: This section discusses aspects of Neo4j (different editions, key terms & transactions) that are important to consider when using Cypher. + +This section discusses aspects of Neo4j that are important to consider when using Cypher. + +[[cypher-neo4j-editions]] +== Cypher and the different editions of Neo4j + +Neo4j consists of two editions: a commercial Enterprise Edition, and a Community Edition. + +Cypher works almost identically between the two editions, but there are key areas in which they differ: + +[options="header"] +|=== +| Feature | Enterprise Edition | Community Edition + +| xref::databases.adoc[Multi-database] +a| +Any number of user databases. +a| +Only `system` and one user database. + +| Role-based security +a| +User, role, and privilege management for flexible xref::access-control/index.adoc[access control] and xref::access-control/manage-privileges.adoc[sub-graph access control]. +a| +xref::access-control/manage-users.adoc[Multi-user management]. +All users have full access rights. + +| Constraints +a| +All constraints: +xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], +xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], +xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], and +xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints]. +//// +TODO: Switch the part above to the one below when adding back relationship key and uniqueness constraints +All constraints: +xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], +xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], +xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], +xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship property uniqueness constraints], +xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints], and +xref::constraints/examples.adoc#constraints-examples-relationship-key[relationship key constraints]. +//// +a| +Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints]. +// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints +//Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node] and xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship] property uniqueness constraints. + +|=== + +[[neo4j-terminology]] +== Key Neo4j terminology + +Cypher queries are executed against a Neo4j database, but normally apply to specific graphs. It is important to understand the meaning of these terms and exactly when a graph is not a database. + + +*DBMS*:: A Neo4j Database Management System is capable of containing and managing multiple graphs contained in databases. Client applications will connect to the DBMS and open sessions against it. +A client session provides access to any graph in the DBMS. + +*Graph*:: Refers to a data model within a database. +Normally there is only one graph within each database, and many administrative commands that refer to a specific graph do so using the database name. +Cypher queries executed in a session may declare which graph they apply to, or use a default, given by the session. +Composite databases can contain multiple graphs, by means of aliases to other databases. +Queries submitted to composite databases may refer to multiple graphs within the same query. + +For more information, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases/[Operations manual -> Composite databases]. + +Composite databases can contain multiple graphs, by means of aliases to other databases. Queries submitted to composite databases may refer to multiple graphs within the same query. For more information, see Operations manual → Composite databases. + +*Database*:: A database is a storage and retrieval mechanism for collecting data in a defined space on disk and in memory. + +[[built-in-databases]] +== Built-in databases in Neo4j + +All Neo4j servers contain a built-in database called `system`, which behaves differently than all other databases. +The `system` database stores system data and you can not perform graph queries against it. + +A fresh installation of Neo4j includes two databases: + +* `system` - the system database described above, containing meta-data on the DBMS and security configuration. +* `neo4j` - the default database, named using the config option `dbms.default_database=neo4j`. + +For more information about the _system_ database, see the sections on xref::databases.adoc[Database management] and xref::access-control/index.adoc[Access control]. + +=== Query considerations + +Most of the time Cypher queries are reading or updating queries, which are run against a graph. +There are also administrative commands that apply to a database, or to the entire DBMS. +Administrative commands cannot be run in a session connected to a normal user database, but instead need to be run within a session connected to the `system` database. +Administrative commands execute on the `system` database. +If an administrative command is submitted to a user database, it is rerouted to the system database. + +[[cypher-neo4j-transactions]] +== Cypher and Neo4j transactions + +All Cypher queries run within transactions. +Modifications done by updating queries are held in memory by the transaction until it is committed, at which point the changes are persisted to disk and become visible to other transactions. +If an error occurs - either during query evaluation, such as division by zero, or during commit, such as constraint violations - the transaction is automatically rolled back, and no changes are persisted in the graph. + +In short, an updating query always either fully succeeds or does not succeed at all. + +[NOTE] +==== +A query that makes a large number of updates consequently uses large amounts of memory since the transaction holds changes in memory. +For memory configuration in Neo4j, see the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/performance/memory-configuration[Neo4j Operations Manual -> Memory configuration]. +==== + +=== Explicit and implicit transactions + +Transactions in Neo4j can be either explicit or implicit. + +[options="header"] +|=== +| Explicit | Implicit + +| Opened by the user. +| Opened automatically. + +| Can execute multiple Cypher queries in sequence. +| Can execute a single Cypher query. + +| Committed, or rolled back, by the user. +| Committed automatically when a transactions finishes successfully. +|=== + +Queries that start separate transactions themselves, such as queries using xref::clauses/call-subquery.adoc#subquery-call-in-transactions[`+CALL { ... } IN TRANSACTIONS+`], are only allowed in _implicit_ mode. +Explicit transactions cannot be managed directly from queries, they must be managed via APIs or tools. + +For examples of the API, or the commands used to start and commit transactions, refer to the API or tool-specific documentation: + +* For information on using transactions with a Neo4j driver, see _The session API_ in the link:{docs-base-uri}[Neo4j Driver manuals]. +* For information on using transactions over the HTTP API, see the link:{neo4j-docs-base-uri}/http-api/{page-version}/actions#http-api-actions[HTTP API documentation -> Using the HTTP API]. +* For information on using transactions within the embedded Core API, see the link:{neo4j-docs-base-uri}/java-reference/{page-version}/java-embedded/cypher-java#cypher-java[Java Reference -> Executing Cypher queries from Java]. +* For information on using transactions within the Neo4j Browser or Cypher-shell, see the link:{neo4j-docs-base-uri}/browser-manual/current/reference-commands/[Neo4j Browser documentation] or the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tools/cypher-shell/#cypher-shell-commands[Cypher-shell documentation]. + +When writing procedures or using Neo4j embedded, remember that all iterators returned from an execution result should be either fully exhausted or closed. +This ensures that the resources bound to them are properly released. + +=== DBMS transactions + +Beginning a transaction while connected to a DBMS will start a DBMS-level transaction. +A DBMS-level transaction is a container for database transactions. + +A database transaction is started when the first query to a specific database is issued. +Database transactions opened inside a DBMS-level transaction are committed or rolled back when the DBMS-level transaction is committed or rolled back. + +DBMS transactions have the following limitations: + +* Only one database can be written to in a DBMS transaction. +* Cypher operations fall into the following main categories: + +** Operations on graphs. +** Schema commands. +** Administration commands. + +It is not possible to combine any of these workloads in a single DBMS transaction. + +=== ACID compliance + +Neo4j is fully ACID compliant. +This means that: + +* Atomicity - If a part of a transaction fails, the database state is left unchanged. +* Consistency — Every transaction leaves the database in a consistent state. +* Isolation — During a transaction, modified data cannot be accessed by other operations. +* Durability — The DBMS can always recover the results of a committed transaction. diff --git a/modules/ROOT/pages/introduction/cypher_overview.adoc b/modules/ROOT/pages/introduction/cypher_overview.adoc new file mode 100644 index 000000000..af6b4b82f --- /dev/null +++ b/modules/ROOT/pages/introduction/cypher_overview.adoc @@ -0,0 +1,76 @@ +[[cypher-overview]] += Cypher: overview +:description: This section provides an overview of Cypher and its key differences compared to SQL. + +This section provides an overview of Cypher and a brief discussion of how Cypher differs to SQL. + +== What is Cypher? + +Cypher is Neo4j’s declarative graph query language. +It was created in 2011 by Neo4j engineers as an SQL-equivalent language for graph databases. +Similar to SQL, Cypher lets users focus on _what_ to retrieve from graph, rather than _how_ to retrieve it. +As such, Cypher enables users to realize the full potential of their property graph databases by allowing for efficient and expressive queries that reveal previously unknown data connections and clusters. + +Cypher provides a visual way of matching patterns and relationships. +It relies on the following ascii-art type of syntax: `(nodes)-[:CONNECT_TO]->(otherNodes)`. +Rounded brackets are used for circular nodes, and `-[:ARROWS]->` for relationships. +Writing a query is effectively like drawing a pattern through the data in the graph. +In other words, entities such as nodes and their relationships are visually built into queries. +This makes Cypher a highly intuitive language to both read and write. + +== Cypher and SQL: key differences + +Cypher and SQL are similar in many ways. +For example, they share many of the same keywords, such as `WHERE` and `ORDER BY`. +However, there are some important differences between the two: + +*Cypher is schema-flexible*:: + +While it is both possible and advised to enforce partial schemas using xref:constraints/index.adoc[indexes and constraints], Cypher and Neo4j offers a greater degree of schema-flexibility than SQL and a relational database. +More specifically, nodes and relationships in a Neo4j database do not have to have a specific property set to them because other nodes or relationships in the same graph have that property (unless there is an xref:constraints/examples.adoc#constraints-examples-node-property-existence[existence constraint created on that specific property]). +This means that users are not required to use a fixed schema to represent data and that they can add new attributes and relationships as their graphs evolve. + +*Query order*:: + +SQL queries begin with what a user wants to return, whereas Cypher queries end with the return clause. For example, consider the following two queries (both searching a database for titles of movies with a rating of greater than 7), the first written with SQL and the second with Cypher: ++ +[source, SQL] +---- +SELECT movie.name +FROM movie +WHERE movie.rating > 7 +---- ++ +[source, cypher, role=test-skip] +---- +MATCH (movie:Movie) +WHERE movie.rating > 7 +RETURN movie.name +---- + +*Cypher queries are more concise*:: + +Due to its intuitive, whiteboard-like method of constructing clauses, Cypher queries are often more concise than their equivalent SQL queries. +For example, consider the following two queries (both searching a database for the names of the actors in the movie The Matrix), the first written with SQL and the second with Cypher: ++ +[source, SQL] +---- +SELECT actors.name +FROM actors + LEFT JOIN acted_in ON acted_in.actor_id = actors.id + LEFT JOIN movies ON movies.id = acted_in.movie_id +WHERE movies.title = "The Matrix" +---- ++ +[source, cypher, role=test-skip] +---- +MATCH (actor:Actor)-[:ACTED_IN]->(movie:Movie {title: 'The Matrix'}) +RETURN actor.name +---- + +== Cypher and APOC + +Neo4j supports the APOC (Awesome Procedures on Cypher) Core library. +The APOC Core library provides access to user-defined procedures and functions which extend the use of the Cypher query language into areas such as data integration, graph algorithms, and data conversion. + +For more details, visit the link:{neo4j-docs-base-uri}/apoc/{page-version}[APOC Core page]. diff --git a/modules/ROOT/pages/introduction/cypher_tutorial.adoc b/modules/ROOT/pages/introduction/cypher_tutorial.adoc new file mode 100644 index 000000000..ac8132693 --- /dev/null +++ b/modules/ROOT/pages/introduction/cypher_tutorial.adoc @@ -0,0 +1,831 @@ +[[cypher-tutorial]] += Cypher: tutorial +:description: This section provides an overview of Cypher, and goes through a short Cypher tutorial based on the Neo4j movie database. + +In this short tutorial, users will learn how to create, query, and delete a property graph database using Cypher. +The tutorial uses the xref:https://github.com/neo4j-graph-examples/movies/tree/main/documentation[Neo4j movie database]. + +== Creating a data model + +Before creating a property graph database, it is important to develop an appropriate data model. +This will provide structure to the data, and allow users of the graph to efficiently retrieve the information they are looking for. + +The following data model is used for the Neo4j data model: + +image::introduction_schema.svg[width="800",role="middle"] + +It includes two types of node labels: + +* `Person` nodes, which have the following properties: `name` (string) and `born` (integer). +* `Movie` nodes, which have the following properties: `title` (string), `released` (integer), and `tagline` (string). + +The data model also contains five different relationship types between the `Person` and `Movie` nodes: `ACTED_IN`, `DIRECTED`, `PRODUCED`, `WROTE`, and `REVIEWED`. Two of the relationship types have properties: + +* The `ACTED_IN` relationship type, which has the `roles` property (string). +* The `REVIEWED` relationship type, which has a `summary` property (string) and a `rating` property (float). + +_To learn more about data modelling for graph databases, enroll in the free +https://graphacademy.neo4j.com/courses/modeling-fundamentals/[Graph Data Modelling Fundamentals] course offered by GraphAcademy._ + +== Creating a property graph database + +The complete Cypher query to create the Neo4j movie database, can be found https://github.com/neo4j-graph-examples/movies/blob/main/scripts/movies.cypher[here]. +To create the full graph, run the full query against an empty Neo4j database. + +//// +[source, cypher, role=test-setup] +---- +CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'}) +CREATE (Keanu:Person {name:'Keanu Reeves', born:1964}) +CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967}) +CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961}) +CREATE (Hugo:Person {name:'Hugo Weaving', born:1960}) +CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967}) +CREATE (LanaW:Person {name:'Lana Wachowski', born:1965}) +CREATE (JoelS:Person {name:'Joel Silver', born:1952}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix), +(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix), +(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix), +(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix), +(LillyW)-[:DIRECTED]->(TheMatrix), +(LanaW)-[:DIRECTED]->(TheMatrix), +(JoelS)-[:PRODUCED]->(TheMatrix) + +CREATE (Emil:Person {name:"Emil Eifrem", born:1978}) +CREATE (Emil)-[:ACTED_IN {roles:["Emil"]}]->(TheMatrix) + +CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded), +(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded), +(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded), +(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded), +(LillyW)-[:DIRECTED]->(TheMatrixReloaded), +(LanaW)-[:DIRECTED]->(TheMatrixReloaded), +(JoelS)-[:PRODUCED]->(TheMatrixReloaded) + +CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions), +(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions), +(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions), +(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions), +(LillyW)-[:DIRECTED]->(TheMatrixRevolutions), +(LanaW)-[:DIRECTED]->(TheMatrixRevolutions), +(JoelS)-[:PRODUCED]->(TheMatrixRevolutions) + +CREATE (TheDevilsAdvocate:Movie {title:"The Devil's Advocate", released:1997, tagline:'Evil has its winning ways'}) +CREATE (Charlize:Person {name:'Charlize Theron', born:1975}) +CREATE (Al:Person {name:'Al Pacino', born:1940}) +CREATE (Taylor:Person {name:'Taylor Hackford', born:1944}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Kevin Lomax']}]->(TheDevilsAdvocate), +(Charlize)-[:ACTED_IN {roles:['Mary Ann Lomax']}]->(TheDevilsAdvocate), +(Al)-[:ACTED_IN {roles:['John Milton']}]->(TheDevilsAdvocate), +(Taylor)-[:DIRECTED]->(TheDevilsAdvocate) + +CREATE (AFewGoodMen:Movie {title:"A Few Good Men", released:1992, tagline:"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth."}) +CREATE (TomC:Person {name:'Tom Cruise', born:1962}) +CREATE (JackN:Person {name:'Jack Nicholson', born:1937}) +CREATE (DemiM:Person {name:'Demi Moore', born:1962}) +CREATE (KevinB:Person {name:'Kevin Bacon', born:1958}) +CREATE (KieferS:Person {name:'Kiefer Sutherland', born:1966}) +CREATE (NoahW:Person {name:'Noah Wyle', born:1971}) +CREATE (CubaG:Person {name:'Cuba Gooding Jr.', born:1968}) +CREATE (KevinP:Person {name:'Kevin Pollak', born:1957}) +CREATE (JTW:Person {name:'J.T. Walsh', born:1943}) +CREATE (JamesM:Person {name:'James Marshall', born:1967}) +CREATE (ChristopherG:Person {name:'Christopher Guest', born:1948}) +CREATE (RobR:Person {name:'Rob Reiner', born:1947}) +CREATE (AaronS:Person {name:'Aaron Sorkin', born:1961}) +CREATE +(TomC)-[:ACTED_IN {roles:['Lt. Daniel Kaffee']}]->(AFewGoodMen), +(JackN)-[:ACTED_IN {roles:['Col. Nathan R. Jessup']}]->(AFewGoodMen), +(DemiM)-[:ACTED_IN {roles:['Lt. Cdr. JoAnne Galloway']}]->(AFewGoodMen), +(KevinB)-[:ACTED_IN {roles:['Capt. Jack Ross']}]->(AFewGoodMen), +(KieferS)-[:ACTED_IN {roles:['Lt. Jonathan Kendrick']}]->(AFewGoodMen), +(NoahW)-[:ACTED_IN {roles:['Cpl. Jeffrey Barnes']}]->(AFewGoodMen), +(CubaG)-[:ACTED_IN {roles:['Cpl. Carl Hammaker']}]->(AFewGoodMen), +(KevinP)-[:ACTED_IN {roles:['Lt. Sam Weinberg']}]->(AFewGoodMen), +(JTW)-[:ACTED_IN {roles:['Lt. Col. Matthew Andrew Markinson']}]->(AFewGoodMen), +(JamesM)-[:ACTED_IN {roles:['Pfc. Louden Downey']}]->(AFewGoodMen), +(ChristopherG)-[:ACTED_IN {roles:['Dr. Stone']}]->(AFewGoodMen), +(AaronS)-[:ACTED_IN {roles:['Man in Bar']}]->(AFewGoodMen), +(RobR)-[:DIRECTED]->(AFewGoodMen), +(AaronS)-[:WROTE]->(AFewGoodMen) + +CREATE (TopGun:Movie {title:"Top Gun", released:1986, tagline:'I feel the need, the need for speed.'}) +CREATE (KellyM:Person {name:'Kelly McGillis', born:1957}) +CREATE (ValK:Person {name:'Val Kilmer', born:1959}) +CREATE (AnthonyE:Person {name:'Anthony Edwards', born:1962}) +CREATE (TomS:Person {name:'Tom Skerritt', born:1933}) +CREATE (MegR:Person {name:'Meg Ryan', born:1961}) +CREATE (TonyS:Person {name:'Tony Scott', born:1944}) +CREATE (JimC:Person {name:'Jim Cash', born:1941}) +CREATE +(TomC)-[:ACTED_IN {roles:['Maverick']}]->(TopGun), +(KellyM)-[:ACTED_IN {roles:['Charlie']}]->(TopGun), +(ValK)-[:ACTED_IN {roles:['Iceman']}]->(TopGun), +(AnthonyE)-[:ACTED_IN {roles:['Goose']}]->(TopGun), +(TomS)-[:ACTED_IN {roles:['Viper']}]->(TopGun), +(MegR)-[:ACTED_IN {roles:['Carole']}]->(TopGun), +(TonyS)-[:DIRECTED]->(TopGun), +(JimC)-[:WROTE]->(TopGun) + +CREATE (JerryMaguire:Movie {title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.'}) +CREATE (ReneeZ:Person {name:'Renee Zellweger', born:1969}) +CREATE (KellyP:Person {name:'Kelly Preston', born:1962}) +CREATE (JerryO:Person {name:"Jerry O'Connell", born:1974}) +CREATE (JayM:Person {name:'Jay Mohr', born:1970}) +CREATE (BonnieH:Person {name:'Bonnie Hunt', born:1961}) +CREATE (ReginaK:Person {name:'Regina King', born:1971}) +CREATE (JonathanL:Person {name:'Jonathan Lipnicki', born:1996}) +CREATE (CameronC:Person {name:'Cameron Crowe', born:1957}) +CREATE +(TomC)-[:ACTED_IN {roles:['Jerry Maguire']}]->(JerryMaguire), +(CubaG)-[:ACTED_IN {roles:['Rod Tidwell']}]->(JerryMaguire), +(ReneeZ)-[:ACTED_IN {roles:['Dorothy Boyd']}]->(JerryMaguire), +(KellyP)-[:ACTED_IN {roles:['Avery Bishop']}]->(JerryMaguire), +(JerryO)-[:ACTED_IN {roles:['Frank Cushman']}]->(JerryMaguire), +(JayM)-[:ACTED_IN {roles:['Bob Sugar']}]->(JerryMaguire), +(BonnieH)-[:ACTED_IN {roles:['Laurel Boyd']}]->(JerryMaguire), +(ReginaK)-[:ACTED_IN {roles:['Marcee Tidwell']}]->(JerryMaguire), +(JonathanL)-[:ACTED_IN {roles:['Ray Boyd']}]->(JerryMaguire), +(CameronC)-[:DIRECTED]->(JerryMaguire), +(CameronC)-[:PRODUCED]->(JerryMaguire), +(CameronC)-[:WROTE]->(JerryMaguire) + +CREATE (StandByMe:Movie {title:"Stand By Me", released:1986, tagline:"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of."}) +CREATE (RiverP:Person {name:'River Phoenix', born:1970}) +CREATE (CoreyF:Person {name:'Corey Feldman', born:1971}) +CREATE (WilW:Person {name:'Wil Wheaton', born:1972}) +CREATE (JohnC:Person {name:'John Cusack', born:1966}) +CREATE (MarshallB:Person {name:'Marshall Bell', born:1942}) +CREATE +(WilW)-[:ACTED_IN {roles:['Gordie Lachance']}]->(StandByMe), +(RiverP)-[:ACTED_IN {roles:['Chris Chambers']}]->(StandByMe), +(JerryO)-[:ACTED_IN {roles:['Vern Tessio']}]->(StandByMe), +(CoreyF)-[:ACTED_IN {roles:['Teddy Duchamp']}]->(StandByMe), +(JohnC)-[:ACTED_IN {roles:['Denny Lachance']}]->(StandByMe), +(KieferS)-[:ACTED_IN {roles:['Ace Merrill']}]->(StandByMe), +(MarshallB)-[:ACTED_IN {roles:['Mr. Lachance']}]->(StandByMe), +(RobR)-[:DIRECTED]->(StandByMe) + +CREATE (AsGoodAsItGets:Movie {title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.'}) +CREATE (HelenH:Person {name:'Helen Hunt', born:1963}) +CREATE (GregK:Person {name:'Greg Kinnear', born:1963}) +CREATE (JamesB:Person {name:'James L. Brooks', born:1940}) +CREATE +(JackN)-[:ACTED_IN {roles:['Melvin Udall']}]->(AsGoodAsItGets), +(HelenH)-[:ACTED_IN {roles:['Carol Connelly']}]->(AsGoodAsItGets), +(GregK)-[:ACTED_IN {roles:['Simon Bishop']}]->(AsGoodAsItGets), +(CubaG)-[:ACTED_IN {roles:['Frank Sachs']}]->(AsGoodAsItGets), +(JamesB)-[:DIRECTED]->(AsGoodAsItGets) + +CREATE (WhatDreamsMayCome:Movie {title:'What Dreams May Come', released:1998, tagline:'After life there is more. The end is just the beginning.'}) +CREATE (AnnabellaS:Person {name:'Annabella Sciorra', born:1960}) +CREATE (MaxS:Person {name:'Max von Sydow', born:1929}) +CREATE (WernerH:Person {name:'Werner Herzog', born:1942}) +CREATE (Robin:Person {name:'Robin Williams', born:1951}) +CREATE (VincentW:Person {name:'Vincent Ward', born:1956}) +CREATE +(Robin)-[:ACTED_IN {roles:['Chris Nielsen']}]->(WhatDreamsMayCome), +(CubaG)-[:ACTED_IN {roles:['Albert Lewis']}]->(WhatDreamsMayCome), +(AnnabellaS)-[:ACTED_IN {roles:['Annie Collins-Nielsen']}]->(WhatDreamsMayCome), +(MaxS)-[:ACTED_IN {roles:['The Tracker']}]->(WhatDreamsMayCome), +(WernerH)-[:ACTED_IN {roles:['The Face']}]->(WhatDreamsMayCome), +(VincentW)-[:DIRECTED]->(WhatDreamsMayCome) + +CREATE (SnowFallingonCedars:Movie {title:'Snow Falling on Cedars', released:1999, tagline:'First loves last. Forever.'}) +CREATE (EthanH:Person {name:'Ethan Hawke', born:1970}) +CREATE (RickY:Person {name:'Rick Yune', born:1971}) +CREATE (JamesC:Person {name:'James Cromwell', born:1940}) +CREATE (ScottH:Person {name:'Scott Hicks', born:1953}) +CREATE +(EthanH)-[:ACTED_IN {roles:['Ishmael Chambers']}]->(SnowFallingonCedars), +(RickY)-[:ACTED_IN {roles:['Kazuo Miyamoto']}]->(SnowFallingonCedars), +(MaxS)-[:ACTED_IN {roles:['Nels Gudmundsson']}]->(SnowFallingonCedars), +(JamesC)-[:ACTED_IN {roles:['Judge Fielding']}]->(SnowFallingonCedars), +(ScottH)-[:DIRECTED]->(SnowFallingonCedars) + +CREATE (YouveGotMail:Movie {title:"You've Got Mail", released:1998, tagline:'At odds in life... in love on-line.'}) +CREATE (ParkerP:Person {name:'Parker Posey', born:1968}) +CREATE (DaveC:Person {name:'Dave Chappelle', born:1973}) +CREATE (SteveZ:Person {name:'Steve Zahn', born:1967}) +CREATE (TomH:Person {name:'Tom Hanks', born:1956}) +CREATE (NoraE:Person {name:'Nora Ephron', born:1941}) +CREATE +(TomH)-[:ACTED_IN {roles:['Joe Fox']}]->(YouveGotMail), +(MegR)-[:ACTED_IN {roles:['Kathleen Kelly']}]->(YouveGotMail), +(GregK)-[:ACTED_IN {roles:['Frank Navasky']}]->(YouveGotMail), +(ParkerP)-[:ACTED_IN {roles:['Patricia Eden']}]->(YouveGotMail), +(DaveC)-[:ACTED_IN {roles:['Kevin Jackson']}]->(YouveGotMail), +(SteveZ)-[:ACTED_IN {roles:['George Pappas']}]->(YouveGotMail), +(NoraE)-[:DIRECTED]->(YouveGotMail) + +CREATE (SleeplessInSeattle:Movie {title:'Sleepless in Seattle', released:1993, tagline:'What if someone you never met, someone you never saw, someone you never knew was the only someone for you?'}) +CREATE (RitaW:Person {name:'Rita Wilson', born:1956}) +CREATE (BillPull:Person {name:'Bill Pullman', born:1953}) +CREATE (VictorG:Person {name:'Victor Garber', born:1949}) +CREATE (RosieO:Person {name:"Rosie O'Donnell", born:1962}) +CREATE +(TomH)-[:ACTED_IN {roles:['Sam Baldwin']}]->(SleeplessInSeattle), +(MegR)-[:ACTED_IN {roles:['Annie Reed']}]->(SleeplessInSeattle), +(RitaW)-[:ACTED_IN {roles:['Suzy']}]->(SleeplessInSeattle), +(BillPull)-[:ACTED_IN {roles:['Walter']}]->(SleeplessInSeattle), +(VictorG)-[:ACTED_IN {roles:['Greg']}]->(SleeplessInSeattle), +(RosieO)-[:ACTED_IN {roles:['Becky']}]->(SleeplessInSeattle), +(NoraE)-[:DIRECTED]->(SleeplessInSeattle) + +CREATE (JoeVersustheVolcano:Movie {title:'Joe Versus the Volcano', released:1990, tagline:'A story of love, lava and burning desire.'}) +CREATE (JohnS:Person {name:'John Patrick Stanley', born:1950}) +CREATE (Nathan:Person {name:'Nathan Lane', born:1956}) +CREATE +(TomH)-[:ACTED_IN {roles:['Joe Banks']}]->(JoeVersustheVolcano), +(MegR)-[:ACTED_IN {roles:['DeDe', 'Angelica Graynamore', 'Patricia Graynamore']}]->(JoeVersustheVolcano), +(Nathan)-[:ACTED_IN {roles:['Baw']}]->(JoeVersustheVolcano), +(JohnS)-[:DIRECTED]->(JoeVersustheVolcano) + +CREATE (WhenHarryMetSally:Movie {title:'When Harry Met Sally', released:1998, tagline:'Can two friends sleep together and still love each other in the morning?'}) +CREATE (BillyC:Person {name:'Billy Crystal', born:1948}) +CREATE (CarrieF:Person {name:'Carrie Fisher', born:1956}) +CREATE (BrunoK:Person {name:'Bruno Kirby', born:1949}) +CREATE +(BillyC)-[:ACTED_IN {roles:['Harry Burns']}]->(WhenHarryMetSally), +(MegR)-[:ACTED_IN {roles:['Sally Albright']}]->(WhenHarryMetSally), +(CarrieF)-[:ACTED_IN {roles:['Marie']}]->(WhenHarryMetSally), +(BrunoK)-[:ACTED_IN {roles:['Jess']}]->(WhenHarryMetSally), +(RobR)-[:DIRECTED]->(WhenHarryMetSally), +(RobR)-[:PRODUCED]->(WhenHarryMetSally), +(NoraE)-[:PRODUCED]->(WhenHarryMetSally), +(NoraE)-[:WROTE]->(WhenHarryMetSally) + +CREATE (ThatThingYouDo:Movie {title:'That Thing You Do', released:1996, tagline:'In every life there comes a time when that thing you dream becomes that thing you do'}) +CREATE (LivT:Person {name:'Liv Tyler', born:1977}) +CREATE +(TomH)-[:ACTED_IN {roles:['Mr. White']}]->(ThatThingYouDo), +(LivT)-[:ACTED_IN {roles:['Faye Dolan']}]->(ThatThingYouDo), +(Charlize)-[:ACTED_IN {roles:['Tina']}]->(ThatThingYouDo), +(TomH)-[:DIRECTED]->(ThatThingYouDo) + +CREATE (TheReplacements:Movie {title:'The Replacements', released:2000, tagline:'Pain heals, Chicks dig scars... Glory lasts forever'}) +CREATE (Brooke:Person {name:'Brooke Langton', born:1970}) +CREATE (Gene:Person {name:'Gene Hackman', born:1930}) +CREATE (Orlando:Person {name:'Orlando Jones', born:1968}) +CREATE (Howard:Person {name:'Howard Deutch', born:1950}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Shane Falco']}]->(TheReplacements), +(Brooke)-[:ACTED_IN {roles:['Annabelle Farrell']}]->(TheReplacements), +(Gene)-[:ACTED_IN {roles:['Jimmy McGinty']}]->(TheReplacements), +(Orlando)-[:ACTED_IN {roles:['Clifford Franklin']}]->(TheReplacements), +(Howard)-[:DIRECTED]->(TheReplacements) + +CREATE (RescueDawn:Movie {title:'RescueDawn', released:2006, tagline:"Based on the extraordinary true story of one man's fight for freedom"}) +CREATE (ChristianB:Person {name:'Christian Bale', born:1974}) +CREATE (ZachG:Person {name:'Zach Grenier', born:1954}) +CREATE +(MarshallB)-[:ACTED_IN {roles:['Admiral']}]->(RescueDawn), +(ChristianB)-[:ACTED_IN {roles:['Dieter Dengler']}]->(RescueDawn), +(ZachG)-[:ACTED_IN {roles:['Squad Leader']}]->(RescueDawn), +(SteveZ)-[:ACTED_IN {roles:['Duane']}]->(RescueDawn), +(WernerH)-[:DIRECTED]->(RescueDawn) + +CREATE (TheBirdcage:Movie {title:'The Birdcage', released:1996, tagline:'Come as you are'}) +CREATE (MikeN:Person {name:'Mike Nichols', born:1931}) +CREATE +(Robin)-[:ACTED_IN {roles:['Armand Goldman']}]->(TheBirdcage), +(Nathan)-[:ACTED_IN {roles:['Albert Goldman']}]->(TheBirdcage), +(Gene)-[:ACTED_IN {roles:['Sen. Kevin Keeley']}]->(TheBirdcage), +(MikeN)-[:DIRECTED]->(TheBirdcage) + +CREATE (Unforgiven:Movie {title:'Unforgiven', released:1992, tagline:"It's a hell of a thing, killing a man"}) +CREATE (RichardH:Person {name:'Richard Harris', born:1930}) +CREATE (ClintE:Person {name:'Clint Eastwood', born:1930}) +CREATE +(RichardH)-[:ACTED_IN {roles:['English Bob']}]->(Unforgiven), +(ClintE)-[:ACTED_IN {roles:['Bill Munny']}]->(Unforgiven), +(Gene)-[:ACTED_IN {roles:['Little Bill Daggett']}]->(Unforgiven), +(ClintE)-[:DIRECTED]->(Unforgiven) + +CREATE (JohnnyMnemonic:Movie {title:'Johnny Mnemonic', released:1995, tagline:'The hottest data on earth. In the coolest head in town'}) +CREATE (Takeshi:Person {name:'Takeshi Kitano', born:1947}) +CREATE (Dina:Person {name:'Dina Meyer', born:1968}) +CREATE (IceT:Person {name:'Ice-T', born:1958}) +CREATE (RobertL:Person {name:'Robert Longo', born:1953}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Johnny Mnemonic']}]->(JohnnyMnemonic), +(Takeshi)-[:ACTED_IN {roles:['Takahashi']}]->(JohnnyMnemonic), +(Dina)-[:ACTED_IN {roles:['Jane']}]->(JohnnyMnemonic), +(IceT)-[:ACTED_IN {roles:['J-Bone']}]->(JohnnyMnemonic), +(RobertL)-[:DIRECTED]->(JohnnyMnemonic) + +CREATE (CloudAtlas:Movie {title:'Cloud Atlas', released:2012, tagline:'Everything is connected'}) +CREATE (HalleB:Person {name:'Halle Berry', born:1966}) +CREATE (JimB:Person {name:'Jim Broadbent', born:1949}) +CREATE (TomT:Person {name:'Tom Tykwer', born:1965}) +CREATE (DavidMitchell:Person {name:'David Mitchell', born:1969}) +CREATE (StefanArndt:Person {name:'Stefan Arndt', born:1961}) +CREATE +(TomH)-[:ACTED_IN {roles:['Zachry', 'Dr. Henry Goose', 'Isaac Sachs', 'Dermot Hoggins']}]->(CloudAtlas), +(Hugo)-[:ACTED_IN {roles:['Bill Smoke', 'Haskell Moore', 'Tadeusz Kesselring', 'Nurse Noakes', 'Boardman Mephi', 'Old Georgie']}]->(CloudAtlas), +(HalleB)-[:ACTED_IN {roles:['Luisa Rey', 'Jocasta Ayrs', 'Ovid', 'Meronym']}]->(CloudAtlas), +(JimB)-[:ACTED_IN {roles:['Vyvyan Ayrs', 'Captain Molyneux', 'Timothy Cavendish']}]->(CloudAtlas), +(TomT)-[:DIRECTED]->(CloudAtlas), +(LillyW)-[:DIRECTED]->(CloudAtlas), +(LanaW)-[:DIRECTED]->(CloudAtlas), +(DavidMitchell)-[:WROTE]->(CloudAtlas), +(StefanArndt)-[:PRODUCED]->(CloudAtlas) + +CREATE (TheDaVinciCode:Movie {title:'The Da Vinci Code', released:2006, tagline:'Break The Codes'}) +CREATE (IanM:Person {name:'Ian McKellen', born:1939}) +CREATE (AudreyT:Person {name:'Audrey Tautou', born:1976}) +CREATE (PaulB:Person {name:'Paul Bettany', born:1971}) +CREATE (RonH:Person {name:'Ron Howard', born:1954}) +CREATE +(TomH)-[:ACTED_IN {roles:['Dr. Robert Langdon']}]->(TheDaVinciCode), +(IanM)-[:ACTED_IN {roles:['Sir Leight Teabing']}]->(TheDaVinciCode), +(AudreyT)-[:ACTED_IN {roles:['Sophie Neveu']}]->(TheDaVinciCode), +(PaulB)-[:ACTED_IN {roles:['Silas']}]->(TheDaVinciCode), +(RonH)-[:DIRECTED]->(TheDaVinciCode) + +CREATE (VforVendetta:Movie {title:'V for Vendetta', released:2006, tagline:'Freedom! Forever!'}) +CREATE (NatalieP:Person {name:'Natalie Portman', born:1981}) +CREATE (StephenR:Person {name:'Stephen Rea', born:1946}) +CREATE (JohnH:Person {name:'John Hurt', born:1940}) +CREATE (BenM:Person {name: 'Ben Miles', born:1967}) +CREATE +(Hugo)-[:ACTED_IN {roles:['V']}]->(VforVendetta), +(NatalieP)-[:ACTED_IN {roles:['Evey Hammond']}]->(VforVendetta), +(StephenR)-[:ACTED_IN {roles:['Eric Finch']}]->(VforVendetta), +(JohnH)-[:ACTED_IN {roles:['High Chancellor Adam Sutler']}]->(VforVendetta), +(BenM)-[:ACTED_IN {roles:['Dascomb']}]->(VforVendetta), +(JamesM)-[:DIRECTED]->(VforVendetta), +(LillyW)-[:PRODUCED]->(VforVendetta), +(LanaW)-[:PRODUCED]->(VforVendetta), +(JoelS)-[:PRODUCED]->(VforVendetta), +(LillyW)-[:WROTE]->(VforVendetta), +(LanaW)-[:WROTE]->(VforVendetta) + +CREATE (SpeedRacer:Movie {title:'Speed Racer', released:2008, tagline:'Speed has no limits'}) +CREATE (EmileH:Person {name:'Emile Hirsch', born:1985}) +CREATE (JohnG:Person {name:'John Goodman', born:1960}) +CREATE (SusanS:Person {name:'Susan Sarandon', born:1946}) +CREATE (MatthewF:Person {name:'Matthew Fox', born:1966}) +CREATE (ChristinaR:Person {name:'Christina Ricci', born:1980}) +CREATE (Rain:Person {name:'Rain', born:1982}) +CREATE +(EmileH)-[:ACTED_IN {roles:['Speed Racer']}]->(SpeedRacer), +(JohnG)-[:ACTED_IN {roles:['Pops']}]->(SpeedRacer), +(SusanS)-[:ACTED_IN {roles:['Mom']}]->(SpeedRacer), +(MatthewF)-[:ACTED_IN {roles:['Racer X']}]->(SpeedRacer), +(ChristinaR)-[:ACTED_IN {roles:['Trixie']}]->(SpeedRacer), +(Rain)-[:ACTED_IN {roles:['Taejo Togokahn']}]->(SpeedRacer), +(BenM)-[:ACTED_IN {roles:['Cass Jones']}]->(SpeedRacer), +(LillyW)-[:DIRECTED]->(SpeedRacer), +(LanaW)-[:DIRECTED]->(SpeedRacer), +(LillyW)-[:WROTE]->(SpeedRacer), +(LanaW)-[:WROTE]->(SpeedRacer), +(JoelS)-[:PRODUCED]->(SpeedRacer) + +CREATE (NinjaAssassin:Movie {title:'Ninja Assassin', released:2009, tagline:'Prepare to enter a secret world of assassins'}) +CREATE (NaomieH:Person {name:'Naomie Harris'}) +CREATE +(Rain)-[:ACTED_IN {roles:['Raizo']}]->(NinjaAssassin), +(NaomieH)-[:ACTED_IN {roles:['Mika Coretti']}]->(NinjaAssassin), +(RickY)-[:ACTED_IN {roles:['Takeshi']}]->(NinjaAssassin), +(BenM)-[:ACTED_IN {roles:['Ryan Maslow']}]->(NinjaAssassin), +(JamesM)-[:DIRECTED]->(NinjaAssassin), +(LillyW)-[:PRODUCED]->(NinjaAssassin), +(LanaW)-[:PRODUCED]->(NinjaAssassin), +(JoelS)-[:PRODUCED]->(NinjaAssassin) + +CREATE (TheGreenMile:Movie {title:'The Green Mile', released:1999, tagline:"Walk a mile you'll never forget."}) +CREATE (MichaelD:Person {name:'Michael Clarke Duncan', born:1957}) +CREATE (DavidM:Person {name:'David Morse', born:1953}) +CREATE (SamR:Person {name:'Sam Rockwell', born:1968}) +CREATE (GaryS:Person {name:'Gary Sinise', born:1955}) +CREATE (PatriciaC:Person {name:'Patricia Clarkson', born:1959}) +CREATE (FrankD:Person {name:'Frank Darabont', born:1959}) +CREATE +(TomH)-[:ACTED_IN {roles:['Paul Edgecomb']}]->(TheGreenMile), +(MichaelD)-[:ACTED_IN {roles:['John Coffey']}]->(TheGreenMile), +(DavidM)-[:ACTED_IN {roles:['Brutus "Brutal" Howell']}]->(TheGreenMile), +(BonnieH)-[:ACTED_IN {roles:['Jan Edgecomb']}]->(TheGreenMile), +(JamesC)-[:ACTED_IN {roles:['Warden Hal Moores']}]->(TheGreenMile), +(SamR)-[:ACTED_IN {roles:['"Wild Bill" Wharton']}]->(TheGreenMile), +(GaryS)-[:ACTED_IN {roles:['Burt Hammersmith']}]->(TheGreenMile), +(PatriciaC)-[:ACTED_IN {roles:['Melinda Moores']}]->(TheGreenMile), +(FrankD)-[:DIRECTED]->(TheGreenMile) + +CREATE (FrostNixon:Movie {title:'Frost/Nixon', released:2008, tagline:'400 million people were waiting for the truth.'}) +CREATE (FrankL:Person {name:'Frank Langella', born:1938}) +CREATE (MichaelS:Person {name:'Michael Sheen', born:1969}) +CREATE (OliverP:Person {name:'Oliver Platt', born:1960}) +CREATE +(FrankL)-[:ACTED_IN {roles:['Richard Nixon']}]->(FrostNixon), +(MichaelS)-[:ACTED_IN {roles:['David Frost']}]->(FrostNixon), +(KevinB)-[:ACTED_IN {roles:['Jack Brennan']}]->(FrostNixon), +(OliverP)-[:ACTED_IN {roles:['Bob Zelnick']}]->(FrostNixon), +(SamR)-[:ACTED_IN {roles:['James Reston, Jr.']}]->(FrostNixon), +(RonH)-[:DIRECTED]->(FrostNixon) + +CREATE (Hoffa:Movie {title:'Hoffa', released:1992, tagline:"He didn't want law. He wanted justice."}) +CREATE (DannyD:Person {name:'Danny DeVito', born:1944}) +CREATE (JohnR:Person {name:'John C. Reilly', born:1965}) +CREATE +(JackN)-[:ACTED_IN {roles:['Hoffa']}]->(Hoffa), +(DannyD)-[:ACTED_IN {roles:['Robert "Bobby" Ciaro']}]->(Hoffa), +(JTW)-[:ACTED_IN {roles:['Frank Fitzsimmons']}]->(Hoffa), +(JohnR)-[:ACTED_IN {roles:['Peter "Pete" Connelly']}]->(Hoffa), +(DannyD)-[:DIRECTED]->(Hoffa) + +CREATE (Apollo13:Movie {title:'Apollo 13', released:1995, tagline:'Houston, we have a problem.'}) +CREATE (EdH:Person {name:'Ed Harris', born:1950}) +CREATE (BillPax:Person {name:'Bill Paxton', born:1955}) +CREATE +(TomH)-[:ACTED_IN {roles:['Jim Lovell']}]->(Apollo13), +(KevinB)-[:ACTED_IN {roles:['Jack Swigert']}]->(Apollo13), +(EdH)-[:ACTED_IN {roles:['Gene Kranz']}]->(Apollo13), +(BillPax)-[:ACTED_IN {roles:['Fred Haise']}]->(Apollo13), +(GaryS)-[:ACTED_IN {roles:['Ken Mattingly']}]->(Apollo13), +(RonH)-[:DIRECTED]->(Apollo13) + +CREATE (Twister:Movie {title:'Twister', released:1996, tagline:"Don't Breathe. Don't Look Back."}) +CREATE (PhilipH:Person {name:'Philip Seymour Hoffman', born:1967}) +CREATE (JanB:Person {name:'Jan de Bont', born:1943}) +CREATE +(BillPax)-[:ACTED_IN {roles:['Bill Harding']}]->(Twister), +(HelenH)-[:ACTED_IN {roles:['Dr. Jo Harding']}]->(Twister), +(ZachG)-[:ACTED_IN {roles:['Eddie']}]->(Twister), +(PhilipH)-[:ACTED_IN {roles:['Dustin "Dusty" Davis']}]->(Twister), +(JanB)-[:DIRECTED]->(Twister) + +CREATE (CastAway:Movie {title:'Cast Away', released:2000, tagline:'At the edge of the world, his journey begins.'}) +CREATE (RobertZ:Person {name:'Robert Zemeckis', born:1951}) +CREATE +(TomH)-[:ACTED_IN {roles:['Chuck Noland']}]->(CastAway), +(HelenH)-[:ACTED_IN {roles:['Kelly Frears']}]->(CastAway), +(RobertZ)-[:DIRECTED]->(CastAway) + +CREATE (OneFlewOvertheCuckoosNest:Movie {title:"One Flew Over the Cuckoo's Nest", released:1975, tagline:"If he's crazy, what does that make you?"}) +CREATE (MilosF:Person {name:'Milos Forman', born:1932}) +CREATE +(JackN)-[:ACTED_IN {roles:['Randle McMurphy']}]->(OneFlewOvertheCuckoosNest), +(DannyD)-[:ACTED_IN {roles:['Martini']}]->(OneFlewOvertheCuckoosNest), +(MilosF)-[:DIRECTED]->(OneFlewOvertheCuckoosNest) + +CREATE (SomethingsGottaGive:Movie {title:"Something's Gotta Give", released:2003}) +CREATE (DianeK:Person {name:'Diane Keaton', born:1946}) +CREATE (NancyM:Person {name:'Nancy Meyers', born:1949}) +CREATE +(JackN)-[:ACTED_IN {roles:['Harry Sanborn']}]->(SomethingsGottaGive), +(DianeK)-[:ACTED_IN {roles:['Erica Barry']}]->(SomethingsGottaGive), +(Keanu)-[:ACTED_IN {roles:['Julian Mercer']}]->(SomethingsGottaGive), +(NancyM)-[:DIRECTED]->(SomethingsGottaGive), +(NancyM)-[:PRODUCED]->(SomethingsGottaGive), +(NancyM)-[:WROTE]->(SomethingsGottaGive) + +CREATE (BicentennialMan:Movie {title:'Bicentennial Man', released:1999, tagline:"One robot's 200 year journey to become an ordinary man."}) +CREATE (ChrisC:Person {name:'Chris Columbus', born:1958}) +CREATE +(Robin)-[:ACTED_IN {roles:['Andrew Marin']}]->(BicentennialMan), +(OliverP)-[:ACTED_IN {roles:['Rupert Burns']}]->(BicentennialMan), +(ChrisC)-[:DIRECTED]->(BicentennialMan) + +CREATE (CharlieWilsonsWar:Movie {title:"Charlie Wilson's War", released:2007, tagline:"A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire."}) +CREATE (JuliaR:Person {name:'Julia Roberts', born:1967}) +CREATE +(TomH)-[:ACTED_IN {roles:['Rep. Charlie Wilson']}]->(CharlieWilsonsWar), +(JuliaR)-[:ACTED_IN {roles:['Joanne Herring']}]->(CharlieWilsonsWar), +(PhilipH)-[:ACTED_IN {roles:['Gust Avrakotos']}]->(CharlieWilsonsWar), +(MikeN)-[:DIRECTED]->(CharlieWilsonsWar) + +CREATE (ThePolarExpress:Movie {title:'The Polar Express', released:2004, tagline:'This Holiday Season... Believe'}) +CREATE +(TomH)-[:ACTED_IN {roles:['Hero Boy', 'Father', 'Conductor', 'Hobo', 'Scrooge', 'Santa Claus']}]->(ThePolarExpress), +(RobertZ)-[:DIRECTED]->(ThePolarExpress) + +CREATE (ALeagueofTheirOwn:Movie {title:'A League of Their Own', released:1992, tagline:'Once in a lifetime you get a chance to do something different.'}) +CREATE (Madonna:Person {name:'Madonna', born:1954}) +CREATE (GeenaD:Person {name:'Geena Davis', born:1956}) +CREATE (LoriP:Person {name:'Lori Petty', born:1963}) +CREATE (PennyM:Person {name:'Penny Marshall', born:1943}) +CREATE +(TomH)-[:ACTED_IN {roles:['Jimmy Dugan']}]->(ALeagueofTheirOwn), +(GeenaD)-[:ACTED_IN {roles:['Dottie Hinson']}]->(ALeagueofTheirOwn), +(LoriP)-[:ACTED_IN {roles:['Kit Keller']}]->(ALeagueofTheirOwn), +(RosieO)-[:ACTED_IN {roles:['Doris Murphy']}]->(ALeagueofTheirOwn), +(Madonna)-[:ACTED_IN {roles:['"All the Way" Mae Mordabito']}]->(ALeagueofTheirOwn), +(BillPax)-[:ACTED_IN {roles:['Bob Hinson']}]->(ALeagueofTheirOwn), +(PennyM)-[:DIRECTED]->(ALeagueofTheirOwn) + +CREATE (PaulBlythe:Person {name:'Paul Blythe'}) +CREATE (AngelaScope:Person {name:'Angela Scope'}) +CREATE (JessicaThompson:Person {name:'Jessica Thompson'}) +CREATE (JamesThompson:Person {name:'James Thompson'}) + +CREATE +(JessicaThompson)-[:REVIEWED {summary:'An amazing journey', rating:95}]->(CloudAtlas), +(JessicaThompson)-[:REVIEWED {summary:'Silly, but fun', rating:65}]->(TheReplacements), +(JamesThompson)-[:REVIEWED {summary:'The coolest football movie ever', rating:100}]->(TheReplacements), +(AngelaScope)-[:REVIEWED {summary:'Pretty funny at times', rating:62}]->(TheReplacements), +(JessicaThompson)-[:REVIEWED {summary:'Dark, but compelling', rating:85}]->(Unforgiven), +(JessicaThompson)-[:REVIEWED {summary:"Slapstick redeemed only by the Robin Williams and Gene Hackman's stellar performances", rating:45}]->(TheBirdcage), +(JessicaThompson)-[:REVIEWED {summary:'A solid romp', rating:68}]->(TheDaVinciCode), +(JamesThompson)-[:REVIEWED {summary:'Fun, but a little far fetched', rating:65}]->(TheDaVinciCode), +(JessicaThompson)-[:REVIEWED {summary:'You had me at Jerry', rating:92}]->(JerryMaguire) +---- +//// + +== Finding nodes + +The `MATCH` clause is used to find a specific pattern in the graph, such as a specific node. +The `RETURN` clause specifies what of the found graph pattern to return. + +For example, this query will find the nodes with `Person` label and the name `Keanu Reeves`, and return the `name` and `born` properties of the found nodes: + +.Query +[source, cypher] +---- +MATCH (keanu:Person {name:'Keanu Reeves'}) +RETURN keanu.name, keanu.born +---- + +.Result +[role="queryresult",options="header,footer",cols="2*= 1980 AND bornInEighties.born < 1990 +RETURN bornInEighties.name as name, bornInEighties.born as born ORDER BY born DESC +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(m:Movie) +Return tom, type(r), m.title +---- + +The graph returned shows that he has 13 outgoing relationships connected to 12 different `Movie` nodes (12 have the `ACTED_IN` type and one has the `DIRECTED` type). + +image::introduction_example1.svg[width="500",role="middle"] + +It is possible to further modify Cypher queries by adding label expressions to the clauses. +For example, the below query uses a `NOT` expression (`!`) to return all relationships that does not contain the specified type, `ACTED_IN`. + +.Query +[source, cypher] +---- +MATCH (tom:Person {name:'Tom Hanks'})-[r:!ACTED_IN]->(m:Movie) +Return tom.name, type(r), m.title +---- + +.Result +[role="queryresult",options="header,footer",cols="3*(colleagues:Person) +RETURN DISTINCT colleagues.name as colleagues LIMIT 5 +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(m:Movie)<-[:ACTED_IN]-(coActors:Person), + (coActors)-[:ACTED_IN]->(m2:Movie)<-[:ACTED_IN]-(tom:Person {name:'Tom Hanks'}) +RETURN DISTINCT coActors.name +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(joe)-[:FRIEND]->(steve) -CREATE (john)-[:FRIEND]->(sara)-[:FRIEND]->(maria) ----- - -image::graph1.svg[] - -For example, here is a query which finds a user called *'John'* and *'John's'* friends (though not his direct friends) before returning both *'John'* and any friends-of-friends that are found. - -[source,cypher, indent=0] ----- -MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof) -RETURN john.name, fof.name ----- - -Resulting in: - -[queryresult] ----- -Rows: 2 +Welcome to the Neo4j Cypher Manual. -+----------------------+ -| john.name | fof.name | -+----------------------+ -| 'John' | 'Maria' | -| 'John' | 'Steve' | -+----------------------+ ----- +Cypher is Neo4j’s declarative query language, allowing users to unlock the full potential of property graph databases. +For a reference of all available Cypher features, see the link:{neo4j-docs-base-uri}/cypher-cheat-sheet/{page-version}/[Cypher Cheat Sheet]. -Next up we will add filtering to set more parts in motion: +This introduction will cover the following topics: -We take a list of user names and find all nodes with names from this list, match their friends and return only those followed users who have a *'name'* property starting with *'S'*. +* xref:introduction/cypher_overview.adoc[] +* xref:introduction/cypher_tutorial.adoc[] +* xref:introduction/cypher_neo4j.adoc[] +* xref:introduction/cypher_aura.adoc[] +[[intended-readership]] +== Cypher Manual: intended readership -[source,cypher, indent=0] ----- -MATCH (user)-[:FRIEND]->(follower) -WHERE user.name IN ['Joe', 'John', 'Sara', 'Maria', 'Steve'] AND follower.name =~ 'S.*' -RETURN user.name, follower.name ----- +The Cypher Manual aims to be as instructive as possible to readers from a variety of backgrounds and professions, such as developers, administrators, and academic researchers. +In so doing, it assumes a degree of prior knowledge of Cypher. -Resulting in: +The Cypher Manual is, therefore, not primarily intended for beginners (in other words, readers who have never used Cypher before). +Beginners are instead encouraged to visit the link:{neo4j-docs-base-uri}/getting-started/{page-version}/cypher-intro/[Cypher Getting Started Guide], and enroll in the free Cypher courses offered by Neo4j’s https://graphacademy.neo4j.com/[Neo4j GraphAcademy]. -[queryresult] ----- -Rows: 2 +//// +TODO: add when new structure of the Manual has been published -+---------------------------+ -| user.name | follower.name | -+---------------------------+ -| 'John' | 'Sara' | -| 'Joe' | 'Steve' | -+---------------------------+ ----- +[[structure-content]] +== Cypher Manual: content and structure -====== +Chapter 1: Clauses +Chapter 2: Patterns +Chapter 3: Syntax +Chapter 4: Values and Types +Chapter 5: Expressions +Chapter 6: Functions +Chapter 7: Procedures +Chapter 8: Indexes and Constraints +Chapter 9: Query Profiling +Chapter 10: Administration +//// From 34c84b170bd32947f547ffdfeac9e707b466e93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 23 Mar 2023 13:00:16 +0100 Subject: [PATCH 162/383] Fix clause composition section in new intro (#484) --- modules/ROOT/pages/introduction/cypher_tutorial.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/introduction/cypher_tutorial.adoc b/modules/ROOT/pages/introduction/cypher_tutorial.adoc index ac8132693..5b4b1517a 100644 --- a/modules/ROOT/pages/introduction/cypher_tutorial.adoc +++ b/modules/ROOT/pages/introduction/cypher_tutorial.adoc @@ -596,7 +596,7 @@ The first clause takes as input the state of the graph before the query and an e The output of a clause is a new state of the graph and a new table of intermediate results, serving as input to the next clause. The output of the last clause is the result of the query. -Note that if one of the clauses returns `null`, there is nothing to pass on to subsequent clauses, thus ending the query. +Note that if one of the clauses returns an empty table of intermediate results, there is nothing to pass on to subsequent clauses, thus ending the query. (There are ways to circumvent this behaviour. For example, by replacing a `MATCH` clause with xref:clauses/optional-match.adoc[OPTIONAL MATCH].) @@ -730,7 +730,7 @@ The xref:syntax/operators.adoc#syntax-using-the-distinct-operator[DISTINCT] oper .Query [source, cypher, role=test-result-skip] ---- -MATCH (tom:Person {name:'Tom Hanks'})-[*1..3]->(colleagues:Person) +MATCH (tom:Person {name:'Tom Hanks'})-[*1..3]-(colleagues:Person) RETURN DISTINCT colleagues.name as colleagues LIMIT 5 ---- From 9e77f236b95f613c9dd236e20156f2d3f951a66d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 23 Mar 2023 14:08:37 +0100 Subject: [PATCH 163/383] update headers and typo fix (#487) --- modules/ROOT/pages/introduction/cypher_overview.adoc | 2 +- modules/ROOT/pages/introduction/cypher_tutorial.adoc | 2 +- modules/ROOT/pages/introduction/index.adoc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ROOT/pages/introduction/cypher_overview.adoc b/modules/ROOT/pages/introduction/cypher_overview.adoc index af6b4b82f..f5a94daee 100644 --- a/modules/ROOT/pages/introduction/cypher_overview.adoc +++ b/modules/ROOT/pages/introduction/cypher_overview.adoc @@ -1,5 +1,5 @@ [[cypher-overview]] -= Cypher: overview += Overview :description: This section provides an overview of Cypher and its key differences compared to SQL. This section provides an overview of Cypher and a brief discussion of how Cypher differs to SQL. diff --git a/modules/ROOT/pages/introduction/cypher_tutorial.adoc b/modules/ROOT/pages/introduction/cypher_tutorial.adoc index 5b4b1517a..18c6b9281 100644 --- a/modules/ROOT/pages/introduction/cypher_tutorial.adoc +++ b/modules/ROOT/pages/introduction/cypher_tutorial.adoc @@ -1,5 +1,5 @@ [[cypher-tutorial]] -= Cypher: tutorial += Tutorial :description: This section provides an overview of Cypher, and goes through a short Cypher tutorial based on the Neo4j movie database. In this short tutorial, users will learn how to create, query, and delete a property graph database using Cypher. diff --git a/modules/ROOT/pages/introduction/index.adoc b/modules/ROOT/pages/introduction/index.adoc index 03de76d61..7798ac12a 100644 --- a/modules/ROOT/pages/introduction/index.adoc +++ b/modules/ROOT/pages/introduction/index.adoc @@ -22,7 +22,7 @@ The Cypher Manual aims to be as instructive as possible to readers from a variet In so doing, it assumes a degree of prior knowledge of Cypher. The Cypher Manual is, therefore, not primarily intended for beginners (in other words, readers who have never used Cypher before). -Beginners are instead encouraged to visit the link:{neo4j-docs-base-uri}/getting-started/{page-version}/cypher-intro/[Cypher Getting Started Guide], and enroll in the free Cypher courses offered by Neo4j’s https://graphacademy.neo4j.com/[Neo4j GraphAcademy]. +Beginners are instead encouraged to visit the link:{neo4j-docs-base-uri}/getting-started/{page-version}/cypher-intro/[Cypher Getting Started Guide], and enroll in the free Cypher courses offered by https://graphacademy.neo4j.com/[Neo4j GraphAcademy]. //// TODO: add when new structure of the Manual has been published From 7dc163d4d40c11a21e6226f7a6c589f9eaf3c8ab Mon Sep 17 00:00:00 2001 From: Hannes Sandberg Date: Fri, 24 Mar 2023 14:47:32 +0100 Subject: [PATCH 164/383] add WAIT to alter database (#482) --- modules/ROOT/pages/databases.adoc | 3 ++- ...ions-additions-removals-compatibility.adoc | 20 +++++++++++++++++++ modules/ROOT/pages/keyword-glossary.adoc | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 79bdab6b1..24c3e90c8 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -93,6 +93,7 @@ ALTER DATABASE name [IF EXISTS] SET ACCESS {READ ONLY \| READ WRITE} \| SET TOPOLOGY n PRIMAR{Y\|IES} [m SECONDAR{Y\|IES}] } +[WAIT [n [SEC[OND[S]]]]\|NOWAIT] ---- | STOP DATABASE @@ -1115,7 +1116,7 @@ To ensure the database to be dropped is standard and not composite, the user fir [[administration-wait-nowait]] == Wait options -Aside from `SHOW DATABASES` and `ALTER DATABASE`, all database management commands accept an optional `WAIT`/`NOWAIT` clause. +Aside from `SHOW DATABASES`, all database management commands accept an optional `WAIT`/`NOWAIT` clause. The `WAIT`/`NOWAIT` clause allows you to specify a time limit in which the command must complete and return. The options are: diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index a199cf61f..43671a6cb 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -38,6 +38,26 @@ The Cypher query option `connectComponentsPlanner` is deprecated and will be rem The product's default behavior of using a cost-based IDP search algorithm when combining sub-plans will be kept. |=== + +=== Updated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +ALTER DATABASE ... [WAIT [n [SEC[OND[S]]]]\|NOWAIT] +---- +a| +New sub-clause `WAIT` for `ALTER DATABASE`. +This enables adding a waiting clause to specify a time limit in which the command must be completed and returned. +|=== + [[cypher-deprecations-additions-removals-5.6]] == Version 5.6 diff --git a/modules/ROOT/pages/keyword-glossary.adoc b/modules/ROOT/pages/keyword-glossary.adoc index e5dd0f0a9..16e3bfecf 100644 --- a/modules/ROOT/pages/keyword-glossary.adoc +++ b/modules/ROOT/pages/keyword-glossary.adoc @@ -1084,7 +1084,7 @@ The following commands are only executable against the `system` database: | User and role | Change the password of the user that is currently logged in. -| xref:databases.adoc#administration-databases-alter-database[ALTER DATABASE ... [IF EXISTS\] [SET ACCESS {READ ONLY \| READ WRITE}\] [SET TOPOLOGY n PRIMAR{Y\|IES} [m SECONDAR{Y\|IES}\]\]] +| xref:databases.adoc#administration-databases-alter-database[ALTER DATABASE ... [IF EXISTS\] [SET ACCESS {READ ONLY \| READ WRITE}\] [SET TOPOLOGY n PRIMAR{Y\|IES} [m SECONDAR{Y\|IES}\]\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] | Database | Modifies the database access mode and / or topology. From 26a164fcc9790a7943408a2c1557c1b3ad45a9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Cord=C3=B3n?= Date: Fri, 24 Mar 2023 14:00:33 +0000 Subject: [PATCH 165/383] Adds docs for status report and error behaviour in CALL IN TXs (#483) This was added initially and approved in #312 and reverted in #477 due to the branch dev from docs not having been repointed at neo4j 4.7 yet. This PR reverts the revert. --------- --- modules/ROOT/pages/clauses/call-subquery.adoc | 255 +++++++++++++++++- 1 file changed, 247 insertions(+), 8 deletions(-) diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/clauses/call-subquery.adoc index 1df778181..4dbbd751b 100644 --- a/modules/ROOT/pages/clauses/call-subquery.adoc +++ b/modules/ROOT/pages/clauses/call-subquery.adoc @@ -415,6 +415,11 @@ RETURN p.name, youngerPersonsCount Subqueries can be made to execute in separate, inner transactions, producing intermediate commits. This can come in handy when doing large write operations, like batch updates, imports, and deletes. To execute a subquery in separate transactions, you add the modifier `IN TRANSACTIONS` after the subquery. +An outer transaction is opened to report back the accumulated statistics for the inner transactions +(created and deleted nodes, relationships, etc) and it will succeed or fail depending on the results +of those inner transactions. +For more information, see <>. +Canceling that outer transaction will cancel the inner ones. The following example uses a CSV file and the `LOAD CSV` clause to import more data to the example graph. It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+`: @@ -435,7 +440,7 @@ It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+` LOAD CSV FROM 'file:///friends.csv' AS line CALL { WITH line - CREATE (:PERSON {name: line[1], age: toInteger(line[2])}) + CREATE (:Person {name: line[1], age: toInteger(line[2])}) } IN TRANSACTIONS ---- @@ -601,15 +606,20 @@ The batch size of `2 ROWS` is an example given the small data set used here. For larger data sets, you might want to use larger batch sizes, such as `10000 ROWS`. ==== +=== Error behaviour [[txs_error_behaviour]] +Users can choose one of three different option flags to control the behaviour +in case of an error occurring in any of the inner transactions of `+CALL { ... } IN TRANSACTIONS+`: -=== Errors - -If an error occurs in `+CALL { ... } IN TRANSACTIONS+` the entire query fails and -both the current inner transaction and the outer transaction are rolled back. +* `ON ERROR CONTINUE` to ignore a recoverable error and continue the execution of subsequent inner transactions. +The outer transaction succeeds. +It will cause the expected variables from the failed inner query to be bound as null for that specific transaction. +* `ON ERROR BREAK` to ignore a recoverable error and stop the execution of subsequent inner transactions. The outer transaction succeeds. +It will cause expected variables from the failed inner query to be bound as null for all onward transactions (including the failed one). +* `ON ERROR FAIL` to acknowledge a recoverable error and stop the execution of subsequent inner transactions. The outer transaction fails. This is the default behaviour if no flag is explicitly specified. [IMPORTANT] ==== -On error, any previously committed inner transactions remain committed, and are not rolled back. +On error, any previously committed inner transactions remain committed, and are not rolled back. Any failed inner transactions are rolled back. ==== In the following example, the last subquery execution in the second inner transaction fails @@ -621,7 +631,7 @@ due to division by zero. UNWIND [4, 2, 1, 0] AS i CALL { WITH i - CREATE (:Example {num: 100/i}) + CREATE (:Person {num: 100/i}) } IN TRANSACTIONS OF 2 ROWS RETURN i ---- @@ -637,7 +647,7 @@ When the failure occurred, the first transaction had already been committed, so .Query [source, cypher] ---- -MATCH (e:Example) +MATCH (e:Person) RETURN e.num ---- @@ -650,6 +660,235 @@ RETURN e.num 1+d|Rows: 2 |=== +In the following example, `ON ERROR CONTINUE` is used after a failed inner transaction to execute the remaining inner transactions and not fail the outer transaction: + +.Query +[source, cypher] +---- +UNWIND [1, 0, 2, 4] AS i +CALL { + WITH i + CREATE (n:Person {num: 100/i}) // Note, fails when i = 0 + RETURN n +} IN TRANSACTIONS + OF 1 ROW + ON ERROR CONTINUE +RETURN n.num; +---- + +.Result +[role="queryresult",options="header,footer",cols="1*>. + +After each execution of the inner query finishes (successfully or not), a status value is created that records information about the execution and the transaction that executed it: + +* If the inner execution produces one or more rows as output, then a binding to this status value is added to each row, under the selected variable name. +* If the inner execution fails then a single row is produced containing a binding to this status value under the selected variable, and null bindings for all variables that should have been returned by the inner query (if any). + +The status value is a map value with the following fields: + +* `started`: `true` when the inner transaction was started, `false` otherwise. +* `committed`, true when the inner transaction changes were successfully committed, false otherwise. +* `transactionId`: the inner transaction id, or null if the transaction was not started. +* `errorMessage`, the inner transaction error message, or null in case of no error. + +Example of reporting status with `ON ERROR CONTINUE`: + +.Query +[source, cypher, indent=0, role=test-result-skip] +---- +UNWIND [1, 0, 2, 4] AS i +CALL { + WITH i + CREATE (n:Person {num: 100/i}) // Note, fails when i = 0 + RETURN n +} IN TRANSACTIONS + OF 1 ROW + ON ERROR CONTINUE + REPORT STATUS AS s +RETURN n.num, s; +---- + +.Result +[role="queryresult",options="header,footer",cols="2* Date: Thu, 30 Mar 2023 11:48:32 +0100 Subject: [PATCH 166/383] Add a PDF-specific title (#491) Adds a title for the PDF. --- modules/ROOT/pages/introduction/index.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/ROOT/pages/introduction/index.adoc b/modules/ROOT/pages/introduction/index.adoc index 7798ac12a..316a1460c 100644 --- a/modules/ROOT/pages/introduction/index.adoc +++ b/modules/ROOT/pages/introduction/index.adoc @@ -1,6 +1,11 @@ [[cypher-intro]] +ifdef::backend-pdf[] += Neo4j {neo4j-version} Cypher Manual +endif::[] +ifndef::backend-pdf[] = Introduction :description: This section provides an introduction to the Cypher query language. +endif::[] Welcome to the Neo4j Cypher Manual. From 772339c02cd9440eb50ede627870248b23ade766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 31 Mar 2023 09:06:10 +0200 Subject: [PATCH 167/383] Update textual representation of path results (#492) In 5.6 Browser updated the formatting of the textual representation of returned paths to include the relationship types connecting nodes and to more generally represent the ascii-art style used by Cypher. This PR updates the Cypher Manual accordingly. --- modules/ROOT/pages/clauses/create.adoc | 2 +- modules/ROOT/pages/clauses/match.adoc | 14 +++++++------- modules/ROOT/pages/clauses/return.adoc | 6 +++--- modules/ROOT/pages/functions/list.adoc | 2 +- modules/ROOT/pages/functions/predicate.adoc | 8 ++++---- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/ROOT/pages/clauses/create.adoc b/modules/ROOT/pages/clauses/create.adoc index 746ac2cf4..04f79f64a 100644 --- a/modules/ROOT/pages/clauses/create.adoc +++ b/modules/ROOT/pages/clauses/create.adoc @@ -242,7 +242,7 @@ This query creates three nodes and two relationships in one go, assigns it to a [role="queryresult",options="header,footer",cols="1*(:Company {name: "Neo4j"})<-[:WORKS_AT]-(:Person {name: "Michael"}) 1+d|Rows: 1 + Nodes created: 3 + Relationships created: 2 + diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index f9fc71819..b27db7bb1 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -540,7 +540,7 @@ image::MATCH_properties_on_variable_length_path.svg[width="400",role="middle"] [role="queryresult",options="header,footer",cols="1*(:Movie {title: "No Code of Conduct"})<-[:ACTED_IN {role: "Bill Peterson",lead: true}]-(:Person {name: "Martin Sheen"})+ 1+d|Rows: 1 |=== @@ -595,8 +595,8 @@ image::MATCH_named_path_example.svg[width="500",role="middle"] [role="queryresult",options="header,footer",cols="1*(:Movie {title: "The American President"})+ +| +(:Person {name: "Michael Douglas"})-[:ACTED_IN {role: "Gordon Gekko"}]->(:Movie {title: "Wall Street"})+ 1+d|Rows: 2 |=== @@ -632,7 +632,7 @@ image::MATCH_shortestpath_example.svg[width="400",role="middle"] [role="queryresult",options="header,footer",cols="1*(:Movie {title: "Wall Street"})<-[:DIRECTED]-(:Person {name: "Oliver Stone"})+ 1+d|Rows: 1 |=== @@ -662,7 +662,7 @@ image::MATCH_shortestpath_with_predicates_example.svg[width="400",role="middle"] [role="queryresult",options="header,footer",cols="1*(:Person {name: "Martin Sheen"})-[:ACTED_IN {role: "A.J. MacInerney"}]->(:Movie {title: "The American President"})+│ 1+d|Rows: 1 |=== @@ -691,8 +691,8 @@ image::MATCH_allshortestpaths_example.svg[width="400",role="middle"] [role="queryresult",options="header,footer",cols="1*(:Movie {title: "Wall Street"})<-[:ACTED_IN {role: "Gordon Gekko"}]-(:Person {name: "Michael Douglas"})+ +| +(:Person {name: "Martin Sheen"})-[:ACTED_IN {role: "A.J. MacInerney"}]->(:Movie {title: "The American President"})<-[:ACTED_IN {role: "President Andrew Shepherd"}]-(:Person {name: "Michael Douglas"})+ 1+d|Rows: 2 |=== diff --git a/modules/ROOT/pages/clauses/return.adoc b/modules/ROOT/pages/clauses/return.adoc index 076391179..12d27de2e 100644 --- a/modules/ROOT/pages/clauses/return.adoc +++ b/modules/ROOT/pages/clauses/return.adoc @@ -113,8 +113,8 @@ This returns the two nodes, and the two possible paths between them. [role="queryresult",options="header,footer",cols="4*(:Movie {title: "Man of Tai Chi",released: 2013})+ | +{:ACTED_IN}+ +| +{"bornIn":"Beirut","nationality":"Canadian","name":"Keanu Reeves"}+ | +{"title":"Man of Tai Chi","released":2013}+ | +(:Person {bornIn: "Beirut",nationality: "Canadian",name: "Keanu Reeves"})-[:DIRECTED]->(:Movie {title: "Man of Tai Chi",released: 2013})+ | +{:DIRECTED}+ 4+d|Rows: 1 |=== @@ -209,7 +209,7 @@ Returns a predicate, a literal and function call with a pattern expression param [role="queryresult",options="header,footer",cols="3*(:Person {nationality: "American",name: "Carrie Anne Moss",age: 55})-[:KNOWS]->(:Person {nationality: "Australian",name: "Guy Pearce",age: 55})+ 1+d|Rows: 1 |=== @@ -493,8 +493,8 @@ image::predicate_function_example.svg[width="300",role="middle"] |=== | +p+ -| +[{"nationality":"Canadian","name":"Keanu Reeves","age":58},{},{"nationality":"American","name":"Carrie Anne Moss","age":55}]+ -| +[{"nationality":"Canadian","name":"Keanu Reeves","age":58},{},{"nationality":"American","name":"Carrie Anne Moss","age":55},{"nationality":"American","name":"Carrie Anne Moss","age":55},{},{"nationality":"Australian","name":"Guy Pearce","age":55}]+ +| +(:Person {nationality: "Canadian",name: "Keanu Reeves",age: 58})-[:KNOWS]->(:Person {nationality: "American",name: "Carrie Anne Moss",age: 55})+ +| +(:Person {nationality: "Canadian",name: "Keanu Reeves",age: 58})-[:KNOWS]->(:Person {nationality: "American",name: "Carrie Anne Moss",age: 55})-[:KNOWS]->(:Person {nationality: "Australian",name: "Guy Pearce",age: 55})+ 1+d|Rows: 2 |=== @@ -560,7 +560,7 @@ In every returned path there is exactly one node which the `nationality` propert |=== | +p+ -| +[{"nationality":"Canadian","name":"Keanu Reeves","age":58},{},{"nationality":"Northern Irish","name":"Liam Neeson","age":70}] + +| +(:Person {nationality: "Canadian",name: "Keanu Reeves",age: 58})-[:KNOWS]->(:Person {nationality: "Northern Irish",name: "Liam Neeson",age: 70})+ 1+d|Rows: 1 |=== From e688d3cc3c7173bf8b9018ef6a4067632e4cb1c4 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Tue, 4 Apr 2023 07:39:41 +0200 Subject: [PATCH 168/383] [Testing] Fix inaccurate `SHOW DATABASES` results (#496) --- modules/ROOT/pages/databases.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 24c3e90c8..bec460963 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -289,8 +289,8 @@ SHOW DATABASES | +name+ | +type+ | +aliases+ | +access+ | +address+ | +role+ | +writer+ | +requestedStatus+ | +currentStatus+ | +statusMessage+ | +default+ | +home+ | +constituents+ | +"movies"+ | +standard+ | +["films","motion pictures"]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +false+ | +false+ | +[]+ -| +"neo4j"+ | +"standard"+ | +[]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +false+ | +false+ | +[]+ -| +"system"+ | +"standard"+ | +[]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +false+ | +false+ | +[]+ +| +"neo4j"+ | +"standard"+ | +[]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +true+ | +true+ | +[]+ +| +"system"+ | +"system"+ | +[]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +false+ | +false+ | +[]+ 13+d|Rows: 3 |=== @@ -629,6 +629,7 @@ SHOW DATABASES YIELD name, type, access, role, writer, constituents | +"library"+ | +"composite"+ | +"read-only"+ | ++ | +false+ | +["library.sci-fi","library.romance"]+ | +"movies"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ | +"neo4j"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ +| +"romance"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ | +"sci-fi-books"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ | +"system"+ | +"system"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ | +"topology-example"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ From 6740b281f9bdc94c45e53add80aeed87db664787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:52:05 +0200 Subject: [PATCH 169/383] New Administration chapter (#494) This PR collects the various administrative sections currently in Cypher Manual and places them under a new chapter heading. This includes: - database management - database alias management - server management - access control Also included in PR: - xref redirects Corresponding PRs: Operations Manual: https://github.com/neo4j/docs-operations/pull/635 Cheat sheet: https://github.com/neo4j/docs-cheat-sheet/pull/71 --------- Co-authored-by: JPryce-Aklundh --- modules/ROOT/content-nav.adoc | 32 +-- .../access-control/built-in-roles.adoc | 28 +-- .../database-administration.adoc | 8 +- .../access-control/dbms-administration.adoc | 56 ++--- .../administration/access-control/index.adoc | 37 +++ .../access-control/limitations.adoc | 4 +- .../access-control/manage-privileges.adoc | 24 +- .../access-control/manage-roles.adoc | 42 ++-- .../access-control/manage-users.adoc | 42 ++-- .../access-control/privileges-immutable.adoc | 6 +- .../access-control/privileges-reads.adoc | 8 +- .../access-control/privileges-writes.adoc | 34 +-- .../pages/{ => administration}/aliases.adoc | 22 +- .../pages/{ => administration}/databases.adoc | 4 +- .../index.adoc | 31 +-- .../servers.adoc} | 19 +- modules/ROOT/pages/clauses/index.adoc | 6 +- .../ROOT/pages/clauses/listing-functions.adoc | 10 +- .../pages/clauses/listing-procedures.adoc | 12 +- .../ROOT/pages/clauses/listing-settings.adoc | 2 +- .../pages/clauses/transaction-clauses.adoc | 8 +- modules/ROOT/pages/constraints/syntax.adoc | 8 +- ...ions-additions-removals-compatibility.adoc | 20 +- modules/ROOT/pages/functions/scalar.adoc | 2 +- .../pages/indexes-for-full-text-search.adoc | 2 +- .../pages/indexes-for-search-performance.adoc | 8 +- .../ROOT/pages/introduction/cypher_neo4j.adoc | 8 +- .../introduction/neo4j-databases-graphs.adoc | 8 +- modules/ROOT/pages/keyword-glossary.adoc | 232 +++++++++--------- 29 files changed, 372 insertions(+), 351 deletions(-) rename modules/ROOT/pages/{ => administration}/access-control/built-in-roles.adoc (85%) rename modules/ROOT/pages/{ => administration}/access-control/database-administration.adoc (97%) rename modules/ROOT/pages/{ => administration}/access-control/dbms-administration.adoc (94%) create mode 100644 modules/ROOT/pages/administration/access-control/index.adoc rename modules/ROOT/pages/{ => administration}/access-control/limitations.adoc (97%) rename modules/ROOT/pages/{ => administration}/access-control/manage-privileges.adoc (94%) rename modules/ROOT/pages/{ => administration}/access-control/manage-roles.adoc (81%) rename modules/ROOT/pages/{ => administration}/access-control/manage-users.adoc (87%) rename modules/ROOT/pages/{ => administration}/access-control/privileges-immutable.adoc (69%) rename modules/ROOT/pages/{ => administration}/access-control/privileges-reads.adoc (90%) rename modules/ROOT/pages/{ => administration}/access-control/privileges-writes.adoc (80%) rename modules/ROOT/pages/{ => administration}/aliases.adoc (96%) rename modules/ROOT/pages/{ => administration}/databases.adoc (99%) rename modules/ROOT/pages/{access-control => administration}/index.adoc (60%) rename modules/ROOT/pages/{access-control/manage-servers.adoc => administration/servers.adoc} (90%) diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index ca192bcfe..5aa713e0a 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -71,22 +71,6 @@ ** xref:constraints/syntax.adoc[] ** xref:constraints/examples.adoc[] -* xref:databases.adoc[] -* xref:aliases.adoc[] - -* xref:access-control/index.adoc[] -** xref:access-control/manage-users.adoc[] -** xref:access-control/manage-roles.adoc[] -** xref:access-control/manage-privileges.adoc[] -** xref:access-control/manage-servers.adoc[] -** xref:access-control/built-in-roles.adoc[] -** xref:access-control/privileges-reads.adoc[] -** xref:access-control/privileges-writes.adoc[] -** xref:access-control/database-administration.adoc[] -** xref:access-control/dbms-administration.adoc[] -** xref:access-control/limitations.adoc[] -** xref:access-control/privileges-immutable.adoc[] - * xref:query-tuning/index.adoc[] ** xref:query-tuning/query-options.adoc[] ** xref:query-tuning/query-profile.adoc[] @@ -101,6 +85,22 @@ ** xref:execution-plans/operators.adoc[] ** xref:execution-plans/shortestpath-planning.adoc[] +* xref:administration/index.adoc[] +** xref:administration/databases.adoc[] +** xref:administration/aliases.adoc[] +** xref:administration/servers.adoc[] +** xref:administration/access-control/index.adoc[] +*** xref:administration/access-control/manage-users.adoc[] +*** xref:administration/access-control/manage-roles.adoc[] +*** xref:administration/access-control/manage-privileges.adoc[] +*** xref:administration/access-control/built-in-roles.adoc[] +*** xref:administration/access-control/privileges-reads.adoc[] +*** xref:administration/access-control/privileges-writes.adoc[] +*** xref:administration/access-control/database-administration.adoc[] +*** xref:administration/access-control/dbms-administration.adoc[] +*** xref:administration/access-control/limitations.adoc[] +*** xref:administration/access-control/privileges-immutable.adoc[] + * xref:deprecations-additions-removals-compatibility.adoc[] * xref:keyword-glossary.adoc[] diff --git a/modules/ROOT/pages/access-control/built-in-roles.adoc b/modules/ROOT/pages/administration/access-control/built-in-roles.adoc similarity index 85% rename from modules/ROOT/pages/access-control/built-in-roles.adoc rename to modules/ROOT/pages/administration/access-control/built-in-roles.adoc index 9f009b9f3..cd29e7da2 100644 --- a/modules/ROOT/pages/access-control/built-in-roles.adoc +++ b/modules/ROOT/pages/administration/access-control/built-in-roles.adoc @@ -11,12 +11,12 @@ This section explains the default privileges of the built-in roles in Neo4j and All of the commands described in this chapter require that the user executing the commands has the rights to do so. The privileges listed in the following sections are the default set of privileges for each built-in role: -* xref::access-control/built-in-roles.adoc#access-control-built-in-roles-public[The `PUBLIC` role] -* xref::access-control/built-in-roles.adoc#access-control-built-in-roles-reader[The `reader` role] -* xref::access-control/built-in-roles.adoc#access-control-built-in-roles-editor[The `editor` role] -* xref::access-control/built-in-roles.adoc#access-control-built-in-roles-publisher[The `publisher` role] -* xref::access-control/built-in-roles.adoc#access-control-built-in-roles-architect[The `architect` role] -* xref::access-control/built-in-roles.adoc#access-control-built-in-roles-admin[The `admin` role] +* xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-public[The `PUBLIC` role] +* xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-reader[The `reader` role] +* xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-editor[The `editor` role] +* xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-publisher[The `publisher` role] +* xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-architect[The `architect` role] +* xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-admin[The `admin` role] [[access-control-built-in-roles-public]] == The `PUBLIC` role @@ -222,7 +222,7 @@ The resulting `editor` role now has the same privileges as the original built-in [[access-control-built-in-roles-publisher]] == The `publisher` role -The `publisher` role can do the same as xref::access-control/built-in-roles.adoc#access-control-built-in-roles-editor[`editor`], as well as create new labels, property keys and relationship types. +The `publisher` role can do the same as xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-editor[`editor`], as well as create new labels, property keys and relationship types. [[access-control-built-in-roles-publisher-list]] @@ -303,7 +303,7 @@ The resulting `publisher` role now has the same privileges as the original built [[access-control-built-in-roles-architect]] == The `architect` role -The `architect` role can do the same as the xref::access-control/built-in-roles.adoc#access-control-built-in-roles-publisher[`publisher`], as well as create and manage indexes and constraints. +The `architect` role can do the same as the xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-publisher[`publisher`], as well as create and manage indexes and constraints. [[access-control-built-in-roles-architect-list]] @@ -396,24 +396,24 @@ The resulting `architect` role now has the same privileges as the original built [[access-control-built-in-roles-admin]] == The `admin` role -The `admin` role can do the same as the xref::access-control/built-in-roles.adoc#access-control-built-in-roles-architect[`architect`], as well as manage databases, aliases, users, roles and privileges. +The `admin` role can do the same as the xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-architect[`architect`], as well as manage databases, aliases, users, roles and privileges. The `admin` role has the ability to perform administrative tasks. These include the rights to perform the following classes of tasks: -* Manage xref::access-control/database-administration.adoc[database security] to control the rights to perform actions on specific databases: +* Manage xref::administration/access-control/database-administration.adoc[database security] to control the rights to perform actions on specific databases: ** Manage access to a database and the right to start and stop a database. ** Manage xref::indexes-for-search-performance.adoc[indexes] and xref::constraints/index.adoc[constraints]. ** Allow the creation of labels, relationship types or property names. ** Manage transactions -* Manage xref::access-control/dbms-administration.adoc[DBMS security] to control the rights to perform actions on the entire system: -** Manage xref::databases.adoc[multiple databases]. -** Manage xref::access-control/manage-users.adoc[users] and xref::access-control/manage-roles.adoc[roles]. +* Manage xref::administration/access-control/dbms-administration.adoc[DBMS security] to control the rights to perform actions on the entire system: +** Manage xref::administration/databases.adoc[multiple databases]. +** Manage xref::administration/access-control/manage-users.adoc[users] and xref::administration/access-control/manage-roles.adoc[roles]. ** Change configuration parameters. ** Manage sub-graph privileges. ** Manage procedure security. -These rights are conferred using privileges that can be managed through the xref::access-control/manage-privileges.adoc#access-control-graph-privileges[`GRANT`, `DENY` and `REVOKE` commands]. +These rights are conferred using privileges that can be managed through the xref::administration/access-control/manage-privileges.adoc#access-control-graph-privileges[`GRANT`, `DENY` and `REVOKE` commands]. [[access-control-built-in-roles-admin-list]] diff --git a/modules/ROOT/pages/access-control/database-administration.adoc b/modules/ROOT/pages/administration/access-control/database-administration.adoc similarity index 97% rename from modules/ROOT/pages/access-control/database-administration.adoc rename to modules/ROOT/pages/administration/access-control/database-administration.adoc index a1a21df88..874047c73 100644 --- a/modules/ROOT/pages/access-control/database-administration.adoc +++ b/modules/ROOT/pages/administration/access-control/database-administration.adoc @@ -31,7 +31,7 @@ The components of the database privilege commands are: * _mutability_: ** `IMMUTABLE` - When used in conjunction with `GRANT` or `DENY`, specifies that a privilege cannot subsequently be removed unless auth is disabled. Contrastingly, when `IMMUTABLE` is specified in conjunction with a `REVOKE` command, it will act as a filter and only remove matching _immutable_ privileges. -See also xref:access-control/index.adoc#access-control-privileges-immutable[immutable privileges]. +See also xref:administration/access-control/index.adoc#access-control-privileges-immutable[immutable privileges]. * _database-privilege_ ** `ACCESS` - allows access to a specific database or remote database alias. @@ -73,7 +73,7 @@ This can be quite powerful as it allows permissions to be switched from one data [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .General grant +ON DATABASE+ privilege syntax @@ -625,7 +625,7 @@ a|Rows: 6 [NOTE] ==== -Note that `START` and `STOP` privileges are not included in the xref::access-control/database-administration.adoc#access-control-database-administration-all[`ALL DATABASE PRIVILEGES`]. +Note that `START` and `STOP` privileges are not included in the xref::administration/access-control/database-administration.adoc#access-control-database-administration-all[`ALL DATABASE PRIVILEGES`]. ==== @@ -970,7 +970,7 @@ GRANT [IMMUTABLE] TRANSACTION [MANAGEMENT] [( { * \| user[, ...] } )] [NOTE] ==== -Note that the `TRANSACTION MANAGEMENT` privileges are not included in the xref::access-control/database-administration.adoc#access-control-database-administration-all[`ALL DATABASE PRIVILEGES`]. +Note that the `TRANSACTION MANAGEMENT` privileges are not included in the xref::administration/access-control/database-administration.adoc#access-control-database-administration-all[`ALL DATABASE PRIVILEGES`]. ==== For example, to grant the role `regularUsers` the ability to list transactions for user `jake` on the database `neo4j`, use: diff --git a/modules/ROOT/pages/access-control/dbms-administration.adoc b/modules/ROOT/pages/administration/access-control/dbms-administration.adoc similarity index 94% rename from modules/ROOT/pages/access-control/dbms-administration.adoc rename to modules/ROOT/pages/administration/access-control/dbms-administration.adoc index 65f15513d..900c30032 100644 --- a/modules/ROOT/pages/access-control/dbms-administration.adoc +++ b/modules/ROOT/pages/administration/access-control/dbms-administration.adoc @@ -71,13 +71,13 @@ This section explains how to use Cypher to manage Neo4j DBMS administrative priv All DBMS privileges are relevant system-wide. Like user management, they do not belong to one specific database or graph. -For more details on the differences between graphs, databases and the DBMS, refer to xref::introduction/neo4j-databases-graphs.adoc[]. +For more details on the differences between graphs, databases and the DBMS, refer to xref::introduction/cypher_neo4j.adoc[]. image::privileges_grant_and_deny_syntax_dbms_privileges.svg[title="Syntax of GRANT and DENY DBMS Privileges"] image::privileges_hierarchy_dbms.svg[title="DBMS privileges hierarchy"] -The xref::access-control/built-in-roles.adoc#access-control-built-in-roles-admin[`admin` role] has a number of built-in privileges. +The xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-admin[`admin` role] has a number of built-in privileges. These include: * Create, delete, and modify databases and aliases. @@ -91,14 +91,14 @@ To enable a user to perform these tasks, you can grant them the `admin` role, bu All privileges are also assignable using Cypher commands. For more details, see the following sections: -* xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[Role management] -* xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[User management] -* xref::access-control/dbms-administration.adoc#access-control-dbms-administration-impersonation[Impersonation privileges management] -* xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[Database management] -* xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[Alias management] -* xref::access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[Privilege management] -* xref::access-control/database-administration.adoc#access-control-database-administration-transaction[Transaction management] -* xref::access-control/dbms-administration.adoc#access-control-dbms-administration-execute[Procedure and user-defined function security] +* xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[Role management] +* xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[User management] +* xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-impersonation[Impersonation privileges management] +* xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[Database management] +* xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[Alias management] +* xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[Privilege management] +* xref::administration/access-control/database-administration.adoc#access-control-database-administration-transaction[Transaction management] +* xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-execute[Procedure and user-defined function security] [[access-control-dbms-administration-custom]] == Using a custom role to manage DBMS privileges @@ -203,7 +203,7 @@ They can be granted, denied and revoked like other privileges. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Role management privileges command syntax @@ -437,7 +437,7 @@ They can be granted, denied and revoked like other privileges. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .User management privileges command syntax @@ -769,7 +769,7 @@ The ability to impersonate users can be granted via the `IMPERSONATE` privilege. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Impersonation privileges command syntax @@ -842,7 +842,7 @@ They can be granted, denied and revoked like other privileges. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Database management privileges command syntax @@ -1098,11 +1098,11 @@ a|Rows: 1 The DBMS privileges for alias management can be assigned by using Cypher administrative commands and can be applied to both local and remote aliases. They can be granted, denied and revoked like other privileges. -It is also possible to manage aliases with xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[database management commands]. +It is also possible to manage aliases with xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[database management commands]. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Alias management privileges command syntax @@ -1270,7 +1270,7 @@ They can be granted, denied, and revoked like other privileges. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Server management privileges command syntax @@ -1300,7 +1300,7 @@ They can be granted, denied and revoked like other privileges. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Privilege management privileges command syntax @@ -1452,7 +1452,7 @@ They can be granted, denied and revoked like other privileges. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Execute privileges command syntax @@ -1501,7 +1501,7 @@ These cannot be revoked, but will be updated on each restart with the current co === The `EXECUTE PROCEDURE` privilege The ability to execute a procedure can be granted via the `EXECUTE PROCEDURE` privilege. -A role with this privilege is allowed to execute the procedures matched by the xref::access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing]. +A role with this privilege is allowed to execute the procedures matched by the xref::administration/access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing]. The following query shows an example of how to grant this privilege: [source, cypher, role=noplay] @@ -1565,7 +1565,7 @@ Both the `dbms.killTransaction` and the `dbms.killTransactions` procedures are b === The `EXECUTE BOOSTED PROCEDURE` privilege The ability to execute a procedure with elevated privileges can be granted via the `EXECUTE BOOSTED PROCEDURE` privilege. -A user with this privilege is allowed to execute the procedures matched by the xref::access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing] without the execution being restricted to their other privileges. +A user with this privilege is allowed to execute the procedures matched by the xref::administration/access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing] without the execution being restricted to their other privileges. There is no need to grant an individual `EXECUTE PROCEDURE` privilege for the procedures either, as granting the `EXECUTE BOOSTED PROCEDURE` includes an implicit `EXECUTE PROCEDURE` grant for them. A denied `EXECUTE PROCEDURE` still denies executing the procedure. @@ -1764,7 +1764,7 @@ For comparison, when only `EXECUTE BOOSTED PROCEDURE myProc` is granted, the `my === The `EXECUTE ADMIN PROCEDURE` privilege The ability to execute admin procedures (annotated with `@Admin`) can be granted via the `EXECUTE ADMIN PROCEDURES` privilege. -This privilege is equivalent to granting the xref::access-control/dbms-administration.adoc#access-control-execute-boosted-procedure[`EXECUTE BOOSTED PROCEDURE` privilege] on each of the admin procedures. +This privilege is equivalent to granting the xref::administration/access-control/dbms-administration.adoc#access-control-execute-boosted-procedure[`EXECUTE BOOSTED PROCEDURE` privilege] on each of the admin procedures. Any newly added `admin` procedure is automatically included in this privilege. The following query shows an example of how to grant this privilege: @@ -1806,7 +1806,7 @@ In this case, it does not matter whether `EXECUTE PROCEDURE`, `EXECUTE BOOSTED P //EXECUTE [USER [DEFINED]] FUNCTION[S] The ability to execute a user-defined function (UDF) can be granted via the `EXECUTE USER DEFINED FUNCTION` privilege. -A role with this privilege is allowed to execute the UDFs matched by the xref::access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing]. +A role with this privilege is allowed to execute the UDFs matched by the xref::administration/access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing]. [IMPORTANT] ==== @@ -1902,7 +1902,7 @@ The `apoc.any.property` and `apoc.any.properties` are blocked, as well as any ot //EXECUTE BOOSTED [USER [DEFINED]] FUNCTION[S] The ability to execute a user-defined function (UDF) with elevated privileges can be granted via the `EXECUTE BOOSTED USER DEFINED FUNCTION` privilege. -A user with this privilege is allowed to execute the UDFs matched by the xref::access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing] without the execution being restricted to their other privileges. +A user with this privilege is allowed to execute the UDFs matched by the xref::administration/access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing] without the execution being restricted to their other privileges. There is no need to grant an individual `EXECUTE USER DEFINED FUNCTION` privilege for the functions, as granting `EXECUTE BOOSTED USER DEFINED FUNCTION` includes an implicit `EXECUTE USER DEFINED FUNCTION` grant. However, a denied `EXECUTE USER DEFINED FUNCTION` still prevents the function to be executed. @@ -1915,7 +1915,7 @@ The `EXECUTE BOOSTED USER DEFINED FUNCTION` privilege does not apply to built-in Granting `EXECUTE BOOSTED USER DEFINED FUNCTION` on its own allows the UDF to be both executed (because of the implicit `EXECUTE USER DEFINED FUNCTION` grant) and gives it elevated privileges during the execution. A denied `EXECUTE BOOSTED USER DEFINED FUNCTION` on its own behaves slightly differently: it only denies the elevation and not the execution of the UDF. However, a role with only a granted `EXECUTE BOOSTED USER DEFINED FUNCTION` and a denied `EXECUTE BOOSTED USER DEFINED FUNCTION` prevents the execution to be performed as well. -This is the same behavior as for the xref::access-control/dbms-administration.adoc#access-control-execute-boosted-procedure[`EXECUTE BOOSTED PROCEDURE` privilege]. +This is the same behavior as for the xref::administration/access-control/dbms-administration.adoc#access-control-execute-boosted-procedure[`EXECUTE BOOSTED PROCEDURE` privilege]. .Execute boosted user-defined function ====== @@ -1960,12 +1960,12 @@ a|Rows: 2 == The DBMS `SETTING` privileges The ability to show configuration settings can be granted via the `SHOW SETTING` privilege. -A role with this privilege is allowed to query the configuration settings matched by the xref::access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing]. +A role with this privilege is allowed to query the configuration settings matched by the xref::administration/access-control/dbms-administration.adoc#access-control-name-globbing[name-globbing]. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Setting privileges command syntax @@ -2055,7 +2055,7 @@ The right to perform the following privileges can be achieved with a single comm [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== [source, syntax, role=noheader] diff --git a/modules/ROOT/pages/administration/access-control/index.adoc b/modules/ROOT/pages/administration/access-control/index.adoc new file mode 100644 index 000000000..5506d1f1c --- /dev/null +++ b/modules/ROOT/pages/administration/access-control/index.adoc @@ -0,0 +1,37 @@ +:description: Neo4j role-based access control and fine-grained security. + +[[access-control]] += Access control + +== Overview + +Neo4j has a complex security model stored in the system graph, which is maintained on a special database called the `system` database. +All administrative commands need to be executed against the `system` database. +When connected to the DBMS over link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/connectors[Bolt], administrative commands are automatically routed to the `system` database. +For more information on how to manage multiple databases, refer to the section on xref::administration/databases.adoc[administering databases]. + +_Role-based access control_ was introduced in Neo4j 3.1. +Since then, it has been possible to create users and assign them to roles to control whether users can read, write and administer the database. +In Neo4j 4.0 this model was enhanced significantly with the addition of _privileges_, which are the underlying access-control rules by which the users rights are defined. + +The original built-in roles still exist with almost the exact same access rights, but they are no-longer statically defined (see xref::administration/access-control/built-in-roles.adoc[Built-in roles]). +Instead, they are defined in terms of their underlying _privileges_, and they can be modified by adding or removing these access rights. + +In addition, any newly created roles can be assigned to any combination of _privileges_, so that you may set specific access controls for them. +Another new major capability is the _sub-graph_ access control, through which read access to the graph can be limited to specific combinations of labels, relationship types, and properties. + +== Categories of access control + +More details about specific categories of access control can be found in the following sections: + +* xref:administration/access-control/manage-users.adoc[] +* xref:administration/access-control/manage-roles.adoc[] +* xref:administration/access-control/manage-privileges.adoc[] +* xref:administration/access-control/built-in-roles.adoc[] +* xref:administration/access-control/privileges-reads.adoc[] +* xref:administration/access-control/privileges-writes.adoc[] +* xref:administration/access-control/database-administration.adoc[] +* xref:administration/access-control/dbms-administration.adoc[] +* xref:administration/access-control/limitations.adoc[] +* xref:administration/access-control/privileges-immutable.adoc[] + diff --git a/modules/ROOT/pages/access-control/limitations.adoc b/modules/ROOT/pages/administration/access-control/limitations.adoc similarity index 97% rename from modules/ROOT/pages/access-control/limitations.adoc rename to modules/ROOT/pages/administration/access-control/limitations.adoc index 084ca2bd3..25d563b31 100644 --- a/modules/ROOT/pages/access-control/limitations.adoc +++ b/modules/ROOT/pages/administration/access-control/limitations.adoc @@ -26,7 +26,7 @@ As described in xref::indexes-for-search-performance.adoc[Indexes for search per Note that the Neo4j security model impacts the results of queries, regardless if the indexes are used or not. When using non full-text Neo4j indexes, a Cypher query will always return the same results it would have if no index existed. -This means that, if the security model causes fewer results to be returned due to restricted read access in xref::access-control/manage-privileges.adoc[Graph and sub-graph access control], +This means that, if the security model causes fewer results to be returned due to restricted read access in xref::administration/access-control/manage-privileges.adoc[Graph and sub-graph access control], the index will also return the same fewer results. However, this rule is not fully obeyed by xref::indexes-for-full-text-search.adoc[Indexes for full-text search]. @@ -189,7 +189,7 @@ In this case, the query will return zero results rather than simply returning th === Traversing the graph with multi-labeled nodes -The general influence of access control privileges on graph traversal is described in detail in xref::access-control/manage-privileges.adoc[Graph and sub-graph access control]. +The general influence of access control privileges on graph traversal is described in detail in xref::administration/access-control/manage-privileges.adoc[Graph and sub-graph access control]. The following section will only focus on nodes due to their ability to have multiple labels. Relationships can only have one type of label and thus they do not exhibit the behavior this section aims to clarify. While this section will not mention relationships further, the general function of the traverse privilege also applies to them. diff --git a/modules/ROOT/pages/access-control/manage-privileges.adoc b/modules/ROOT/pages/administration/access-control/manage-privileges.adoc similarity index 94% rename from modules/ROOT/pages/access-control/manage-privileges.adoc rename to modules/ROOT/pages/administration/access-control/manage-privileges.adoc index 1b867d74d..238954cc4 100644 --- a/modules/ROOT/pages/access-control/manage-privileges.adoc +++ b/modules/ROOT/pages/administration/access-control/manage-privileges.adoc @@ -13,28 +13,28 @@ Privileges control the access rights to graph elements using a combined allowlis It is possible to grant or deny access, or use a combination of the two. The user will be able to access the resource if they have a `GRANT` (allowlist) and do not have a `DENY` (denylist) relevant to that resource. All other combinations of `GRANT` and `DENY` will result in the matching path being inaccessible. -What this means in practice depends on whether we are talking about a xref::access-control/privileges-reads.adoc[read privilege] or a xref::access-control/privileges-writes.adoc[write privilege]: +What this means in practice depends on whether we are talking about a xref::administration/access-control/privileges-reads.adoc[read privilege] or a xref::administration/access-control/privileges-writes.adoc[write privilege]: -* If an entity is not accessible due to xref::access-control/privileges-reads.adoc[read privileges], the data will become invisible. +* If an entity is not accessible due to xref::administration/access-control/privileges-reads.adoc[read privileges], the data will become invisible. It will appear to the user as if they had a smaller database (smaller graph). -* If an entity is not accessible due to xref::access-control/privileges-writes.adoc[write privileges], an error will occur on any attempt to write that data. +* If an entity is not accessible due to xref::administration/access-control/privileges-writes.adoc[write privileges], an error will occur on any attempt to write that data. [NOTE] ==== In this document we will often use the terms _'allows'_ and _'enables'_ in seemingly identical ways. However, there is a subtle difference. -We will use _'enables'_ to refer to the consequences of xref::access-control/privileges-reads.adoc[read privileges] where a restriction will not cause an error, only a reduction in the apparent graph size. -We will use _'allows'_ to refer to the consequence of xref::access-control/privileges-writes.adoc[write privileges] where a restriction can result in an error. +We will use _'enables'_ to refer to the consequences of xref::administration/access-control/privileges-reads.adoc[read privileges] where a restriction will not cause an error, only a reduction in the apparent graph size. +We will use _'allows'_ to refer to the consequence of xref::administration/access-control/privileges-writes.adoc[write privileges] where a restriction can result in an error. ==== [NOTE] ==== If a user was not also provided with the database `ACCESS` privilege, then access to the entire database will be denied. -Information about the database access privilege can be found in xref::access-control/database-administration.adoc#access-control-database-administration-access[The ACCESS privilege]. +Information about the database access privilege can be found in xref::administration/access-control/database-administration.adoc#access-control-database-administration-access[The ACCESS privilege]. ==== [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== [[access-control-graph-privileges]] @@ -49,10 +49,10 @@ The components of the graph privilege commands are: ** `REVOKE` – removes granted or denied privileges from roles. * _mutability_: -** `IMMUTABLE` can optionally be specified when performing a `GRANT` or `DENY` to indicate that the privilege cannot be subsequently removed unless auth is disabled. Auth must also be disabled in order to `GRANT` or `DENY` an immutable privilege. Contrastingly, when `IMMUTABLE` is specified in conjunction with a `REVOKE` command, it will act as a filter and only remove matching _immutable_ privileges. See also xref:access-control/index.adoc#access-control-privileges-immutable[immutable privileges]. +** `IMMUTABLE` can optionally be specified when performing a `GRANT` or `DENY` to indicate that the privilege cannot be subsequently removed unless auth is disabled. Auth must also be disabled in order to `GRANT` or `DENY` an immutable privilege. Contrastingly, when `IMMUTABLE` is specified in conjunction with a `REVOKE` command, it will act as a filter and only remove matching _immutable_ privileges. See also xref:administration/access-control/index.adoc#access-control-privileges-immutable[immutable privileges]. * _graph-privilege_: -** Can be either a xref::access-control/privileges-reads.adoc[read privilege] or xref::access-control/privileges-writes.adoc[write privilege]. +** Can be either a xref::administration/access-control/privileges-reads.adoc[read privilege] or xref::administration/access-control/privileges-writes.adoc[write privilege]. * _name_: ** The graph or graphs to associate the privilege with. @@ -80,7 +80,7 @@ This can be quite powerful as it allows permissions to be switched from one grap ** Multiple labels or types can be specified, comma-separated. ** Defaults to `ELEMENTS` `+*+` if omitted. ** Some of the commands for write privileges do not allow an _entity_ part. -See xref::access-control/privileges-writes.adoc[Write privileges] for details. +See xref::administration/access-control/privileges-writes.adoc[Write privileges] for details. * _role[, ...]_ ** The role or roles to associate the privilege with, comma-separated. @@ -1038,7 +1038,7 @@ SHOW PRIVILEGES AS REVOKE COMMANDS a|Rows: 35 |=== -For more info about revoking privileges, please see xref::access-control/manage-privileges.adoc#access-control-revoke-privileges[The REVOKE command]. +For more info about revoking privileges, please see xref::administration/access-control/manage-privileges.adoc#access-control-revoke-privileges[The REVOKE command]. [[access-control-list-privileges-role]] === Examples for listing privileges for specific roles @@ -1164,7 +1164,7 @@ SHOW ROLE architect PRIVILEGES AS COMMANDS WHERE command CONTAINS 'MATCH' |=== Again, it is possible to get the privileges listed as revoking commands instead of granting or denying. -For more info about revoking privileges, please see xref::access-control/manage-privileges.adoc#access-control-revoke-privileges[The REVOKE command]. +For more info about revoking privileges, please see xref::administration/access-control/manage-privileges.adoc#access-control-revoke-privileges[The REVOKE command]. [source, cypher, role=noplay] ---- diff --git a/modules/ROOT/pages/access-control/manage-roles.adoc b/modules/ROOT/pages/administration/access-control/manage-roles.adoc similarity index 81% rename from modules/ROOT/pages/access-control/manage-roles.adoc rename to modules/ROOT/pages/administration/access-control/manage-roles.adoc index e12aa550a..16b1afd87 100644 --- a/modules/ROOT/pages/access-control/manage-roles.adoc +++ b/modules/ROOT/pages/administration/access-control/manage-roles.adoc @@ -32,7 +32,7 @@ When connected to the DBMS over `bolt`, administration commands are automaticall [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== [cols="<15s,<85"] @@ -57,7 +57,7 @@ Lists roles. When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. -For more information, see xref::access-control/manage-roles.adoc#access-control-list-roles[Listing roles]. +For more information, see xref::administration/access-control/manage-roles.adoc#access-control-list-roles[Listing roles]. | Required privilege a| @@ -67,7 +67,7 @@ GRANT SHOW ROLE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]). +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]). |=== @@ -93,7 +93,7 @@ Lists roles and users assigned to them. When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. -For more information, see xref::access-control/manage-roles.adoc#access-control-list-roles[Listing roles]. +For more information, see xref::administration/access-control/manage-roles.adoc#access-control-list-roles[Listing roles]. | Required privilege a| @@ -102,7 +102,7 @@ a| GRANT SHOW ROLE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) [source, privilege, role="noheader"] @@ -111,7 +111,7 @@ GRANT SHOW USER ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) |=== @@ -138,7 +138,7 @@ Lists the privileges granted to the specified roles. When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. -For more information, see xref::access-control/manage-privileges.adoc#access-control-list-privileges[Listing privileges]. +For more information, see xref::administration/access-control/manage-privileges.adoc#access-control-list-privileges[Listing privileges]. | Required privilege a| @@ -147,7 +147,7 @@ a| GRANT SHOW PRIVILEGE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[DBMS PRIVILEGE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[DBMS PRIVILEGE MANAGEMENT privileges]) |=== @@ -170,7 +170,7 @@ CREATE ROLE name [IF NOT EXISTS] [AS COPY OF otherName] a| Creates a new role. -For more information, see xref::access-control/manage-roles.adoc#access-control-create-roles[Creating roles]. +For more information, see xref::administration/access-control/manage-roles.adoc#access-control-create-roles[Creating roles]. | Required privilege a| @@ -179,7 +179,7 @@ a| GRANT CREATE ROLE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) |=== @@ -199,7 +199,7 @@ CREATE OR REPLACE ROLE name [AS COPY OF otherName] a| Creates a new role, or if a role with the same name exists, replace it. -For more information, see xref::access-control/manage-roles.adoc#access-control-create-roles[Creating roles]. +For more information, see xref::administration/access-control/manage-roles.adoc#access-control-create-roles[Creating roles]. | Required privilege a| @@ -213,7 +213,7 @@ GRANT CREATE ROLE GRANT DROP ROLE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) |=== @@ -235,7 +235,7 @@ RENAME ROLE name [IF EXISTS] TO otherName a| Changes the name of a role. -For more information, see xref::access-control/manage-roles.adoc#access-control-rename-roles[Renaming roles]. +For more information, see xref::administration/access-control/manage-roles.adoc#access-control-rename-roles[Renaming roles]. | Required privilege a| @@ -244,7 +244,7 @@ a| GRANT RENAME ROLE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) |=== @@ -266,7 +266,7 @@ DROP ROLE name [IF EXISTS] a| Removes a role. -For more information, see xref::access-control/manage-roles.adoc#access-control-drop-roles[Deleting roles]. +For more information, see xref::administration/access-control/manage-roles.adoc#access-control-drop-roles[Deleting roles]. | Required privilege [source, privilege, role="noheader"] @@ -274,7 +274,7 @@ For more information, see xref::access-control/manage-roles.adoc#access-control- GRANT DROP ROLE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) |=== @@ -296,7 +296,7 @@ GRANT ROLE[S] name[, ...] TO user[, ...] a| Assigns roles to users. -For more information, see xref::access-control/manage-roles.adoc#access-control-assign-roles[Assigning roles to users]. +For more information, see xref::administration/access-control/manage-roles.adoc#access-control-assign-roles[Assigning roles to users]. | Required privilege a| @@ -305,7 +305,7 @@ a| GRANT ASSIGN ROLE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) |=== @@ -327,7 +327,7 @@ REVOKE ROLE[S] name[, ...] FROM user[, ...] a| Removes roles from users. -For more information, see xref::access-control/manage-roles.adoc#access-control-revoke-roles[Revoking roles from users]. +For more information, see xref::administration/access-control/manage-roles.adoc#access-control-revoke-roles[Revoking roles from users]. | Required privilege a| @@ -336,7 +336,7 @@ a| GRANT REMOVE ROLE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) |=== @@ -452,7 +452,7 @@ It is also possible to use `SKIP` and `LIMIT` to paginate the results. [NOTE] ==== -The `SHOW ROLE name PRIVILEGES` command is found in xref::access-control/manage-privileges.adoc#access-control-list-privileges[Listing privileges]. +The `SHOW ROLE name PRIVILEGES` command is found in xref::administration/access-control/manage-privileges.adoc#access-control-list-privileges[Listing privileges]. ==== diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/administration/access-control/manage-users.adoc similarity index 87% rename from modules/ROOT/pages/access-control/manage-users.adoc rename to modules/ROOT/pages/administration/access-control/manage-users.adoc index 502fc0cce..3e1d7bba8 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/administration/access-control/manage-users.adoc @@ -17,7 +17,7 @@ When connected to the DBMS over `bolt`, administration commands are automaticall [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== [cols="<15s,<85"] @@ -42,7 +42,7 @@ Lists the current user. When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. -For more information, see xref::access-control/manage-users.adoc#access-control-current-users[Listing current user]. +For more information, see xref::administration/access-control/manage-users.adoc#access-control-current-users[Listing current user]. | Required privilege a| None @@ -72,7 +72,7 @@ Lists all users. When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. -For more information, see xref::access-control/manage-users.adoc#access-control-list-users[Listing users]. +For more information, see xref::administration/access-control/manage-users.adoc#access-control-list-users[Listing users]. | Required privilege a| @@ -81,7 +81,7 @@ a| GRANT SHOW USER ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) |=== @@ -107,7 +107,7 @@ Lists the privileges granted to the specified users or the current user if no us When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. -For more information, see xref::access-control/manage-privileges.adoc#access-control-list-privileges[Listing privileges]. +For more information, see xref::administration/access-control/manage-privileges.adoc#access-control-list-privileges[Listing privileges]. | Required privilege a| @@ -116,14 +116,14 @@ a| GRANT SHOW PRIVILEGE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[DBMS PRIVILEGE MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[DBMS PRIVILEGE MANAGEMENT privileges]) [source, privilege, role="noheader"] ---- GRANT SHOW USER ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) |=== @@ -147,7 +147,7 @@ CREATE USER name [IF NOT EXISTS] a| Creates a new user. -For more information, see xref::access-control/manage-users.adoc#access-control-create-users[Creating users]. +For more information, see xref::administration/access-control/manage-users.adoc#access-control-create-users[Creating users]. | Required privilege a| @@ -156,7 +156,7 @@ a| GRANT CREATE USER ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) |=== @@ -180,7 +180,7 @@ CREATE OR REPLACE USER name a| Creates a new user, or if a user with the same name exists, replace it. -For more information, see xref::access-control/manage-users.adoc#access-control-create-users[Creating users]. +For more information, see xref::administration/access-control/manage-users.adoc#access-control-create-users[Creating users]. | Required privilege a| @@ -189,7 +189,7 @@ a| GRANT CREATE USER ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) [source, privilege, role="noheader"] @@ -197,7 +197,7 @@ GRANT CREATE USER GRANT DROP USER ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) |=== @@ -217,7 +217,7 @@ RENAME USER name [IF EXISTS] TO otherName a| Changes the name of a user. -For more information, see xref::access-control/manage-users.adoc#access-control-rename-users[Renaming users]. +For more information, see xref::administration/access-control/manage-users.adoc#access-control-rename-users[Renaming users]. | Required privilege a| @@ -226,7 +226,7 @@ a| GRANT RENAME USER ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) |=== @@ -253,7 +253,7 @@ Modifies the settings for an existing user. At least one `SET` or `REMOVE` clause is required. `SET` and `REMOVE` clauses cannot be combined in the same command. -For more information, see xref::access-control/manage-users.adoc#access-control-alter-users[Modifying users]. +For more information, see xref::administration/access-control/manage-users.adoc#access-control-alter-users[Modifying users]. | Required privilege a| @@ -272,7 +272,7 @@ GRANT SET USER STATUS GRANT SET USER HOME DATABASE ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) |=== @@ -294,7 +294,7 @@ ALTER CURRENT USER SET PASSWORD FROM 'oldPassword' TO 'newPassword' a| Changes the current user's password. -For more information, see xref::access-control/manage-users.adoc#access-control-alter-password[Changing the current user's password]. +For more information, see xref::administration/access-control/manage-users.adoc#access-control-alter-password[Changing the current user's password]. | Required privilege a| None @@ -319,7 +319,7 @@ DROP USER name [IF EXISTS] a| Removes an existing user. -For more information, see xref::access-control/manage-users.adoc#access-control-drop-users[Delete users]. +For more information, see xref::administration/access-control/manage-users.adoc#access-control-drop-users[Delete users]. | Required privilege a| @@ -328,7 +328,7 @@ a| GRANT DROP USER ---- -(see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) +(see xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DBMS USER MANAGEMENT privileges]) |=== @@ -526,7 +526,7 @@ RETURN user AS adminUser [NOTE] ==== -The `SHOW USER name PRIVILEGES` command is described in xref::access-control/manage-privileges.adoc#access-control-list-privileges[Listing privileges]. +The `SHOW USER name PRIVILEGES` command is described in xref::administration/access-control/manage-privileges.adoc#access-control-list-privileges[Listing privileges]. ==== @@ -582,7 +582,7 @@ User names are case sensitive. The created user will appear on the list provided by `SHOW USERS`. * In Neo4j Community Edition there are no roles, but all users have implied administrator privileges. -* In Neo4j Enterprise Edition all users are automatically assigned the xref::access-control/built-in-roles.adoc#access-control-built-in-roles-public[`PUBLIC` role], giving them a base set of privileges. +* In Neo4j Enterprise Edition all users are automatically assigned the xref::administration/access-control/built-in-roles.adoc#access-control-built-in-roles-public[`PUBLIC` role], giving them a base set of privileges. ==== diff --git a/modules/ROOT/pages/access-control/privileges-immutable.adoc b/modules/ROOT/pages/administration/access-control/privileges-immutable.adoc similarity index 69% rename from modules/ROOT/pages/access-control/privileges-immutable.adoc rename to modules/ROOT/pages/administration/access-control/privileges-immutable.adoc index f201a96db..6ab3965af 100644 --- a/modules/ROOT/pages/access-control/privileges-immutable.adoc +++ b/modules/ROOT/pages/administration/access-control/privileges-immutable.adoc @@ -3,12 +3,12 @@ = Immutable privileges :description: This section explains how to use Cypher to manage immutable privileges. -Unlike regular privileges, having xref:access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[privilege management] privileges is not sufficient to enable immutable privileges to be added or removed. They can only be administered when auth is disabled -- that is, when the configuration setting <> is set to `false`. +Unlike regular privileges, having xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[privilege management] privileges is not sufficient to enable immutable privileges to be added or removed. They can only be administered when auth is disabled -- that is, when the configuration setting <> is set to `false`. [[access-control-privileges-immutable-usecase]] == When to use immutable privileges -Immutable privileges are useful for restricting the actions of users who themselves are able to xref:access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[administer privileges]. +Immutable privileges are useful for restricting the actions of users who themselves are able to xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[administer privileges]. For example, you may want to prevent all users from performing Database Management, even the `admin` user (who are themselves able to add or remove privileges). To do so, you could run: @@ -43,4 +43,4 @@ Under these conditions, immutable privileges can be added and removed in a simil See the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tutorial/tutorial-immutable-privileges[Immutable privileges tutorial] for examples of how to administer immutable privileges. -See xref:access-control/manage-privileges.adoc[Managing Privileges] for more detail on syntax. +See xref:administration/access-control/manage-privileges.adoc[Managing Privileges] for more detail on syntax. diff --git a/modules/ROOT/pages/access-control/privileges-reads.adoc b/modules/ROOT/pages/administration/access-control/privileges-reads.adoc similarity index 90% rename from modules/ROOT/pages/access-control/privileges-reads.adoc rename to modules/ROOT/pages/administration/access-control/privileges-reads.adoc index a457dd5c3..dc76d6960 100644 --- a/modules/ROOT/pages/access-control/privileges-reads.adoc +++ b/modules/ROOT/pages/administration/access-control/privileges-reads.adoc @@ -18,13 +18,13 @@ This section explains how to use Cypher to manage read privileges on graphs. There are three separate read privileges: -* xref::access-control/privileges-reads.adoc#access-control-privileges-reads-traverse[`TRAVERSE`] - enables the specified entities to be found. -* xref::access-control/privileges-reads.adoc#access-control-privileges-reads-read[`READ`] - enables the specified properties of the found entities to be read. -* xref::access-control/privileges-reads.adoc#access-control-privileges-reads-match[`MATCH`] - combines both `TRAVERSE` and `READ`, enabling an entity to be found and its properties read. +* xref::administration/access-control/privileges-reads.adoc#access-control-privileges-reads-traverse[`TRAVERSE`] - enables the specified entities to be found. +* xref::administration/access-control/privileges-reads.adoc#access-control-privileges-reads-read[`READ`] - enables the specified properties of the found entities to be read. +* xref::administration/access-control/privileges-reads.adoc#access-control-privileges-reads-match[`MATCH`] - combines both `TRAVERSE` and `READ`, enabling an entity to be found and its properties read. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== [[access-control-privileges-reads-traverse]] diff --git a/modules/ROOT/pages/access-control/privileges-writes.adoc b/modules/ROOT/pages/administration/access-control/privileges-writes.adoc similarity index 80% rename from modules/ROOT/pages/access-control/privileges-writes.adoc rename to modules/ROOT/pages/administration/access-control/privileges-writes.adoc index a2fbbb984..71f6e2069 100644 --- a/modules/ROOT/pages/access-control/privileges-writes.adoc +++ b/modules/ROOT/pages/administration/access-control/privileges-writes.adoc @@ -18,21 +18,21 @@ This section explains how to use Cypher to manage write privileges on graphs. Write privileges are defined for different parts of the graph: -* xref::access-control/privileges-writes.adoc#access-control-privileges-writes-create[`CREATE`] - allows creating nodes and relationships. -* xref::access-control/privileges-writes.adoc#access-control-privileges-writes-delete[`DELETE`] - allows deleting nodes and relationships. -* xref::access-control/privileges-writes.adoc#access-control-privileges-writes-set-label[`SET LABEL`] - allows setting the specified node labels using the `SET` clause. -* xref::access-control/privileges-writes.adoc#access-control-privileges-writes-remove-label[`REMOVE LABEL`] - allows removing the specified node labels using the `REMOVE` clause. -* xref::access-control/privileges-writes.adoc#access-control-privileges-writes-set-property[`SET PROPERTY`] - allows setting properties on nodes and relationships. +* xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-create[`CREATE`] - allows creating nodes and relationships. +* xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-delete[`DELETE`] - allows deleting nodes and relationships. +* xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-set-label[`SET LABEL`] - allows setting the specified node labels using the `SET` clause. +* xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-remove-label[`REMOVE LABEL`] - allows removing the specified node labels using the `REMOVE` clause. +* xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-set-property[`SET PROPERTY`] - allows setting properties on nodes and relationships. There are also compound privileges which combine the above specific privileges: -* xref::access-control/privileges-writes.adoc#access-control-privileges-writes-merge[`MERGE`] - allows `MATCH`, `CREATE` and `SET PROPERTY` to apply the `MERGE` command. -* xref::access-control/privileges-writes.adoc#access-control-privileges-writes-write[`WRITE`] - allows all `WRITE` operations on an entire graph. -* xref::access-control/privileges-writes.adoc#access-control-privileges-writes-all[`ALL GRAPH PRIVILEGES`] - allows all `READ` and `WRITE` operations on an entire graph. +* xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-merge[`MERGE`] - allows `MATCH`, `CREATE` and `SET PROPERTY` to apply the `MERGE` command. +* xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-write[`WRITE`] - allows all `WRITE` operations on an entire graph. +* xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-all[`ALL GRAPH PRIVILEGES`] - allows all `READ` and `WRITE` operations on an entire graph. [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== [[access-control-privileges-writes-create]] @@ -83,8 +83,8 @@ DENY CREATE ON GRAPH * NODES foo TO regularUsers [NOTE] ==== -If the user attempts to create nodes with a label that does not already exist on the database, then the user must also possess the xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW LABEL] privilege. -The same applies to new relationships: the xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW RELATIONSHIP TYPE] privilege is required. +If the user attempts to create nodes with a label that does not already exist on the database, then the user must also possess the xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW LABEL] privilege. +The same applies to new relationships: the xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW RELATIONSHIP TYPE] privilege is required. ==== @@ -183,7 +183,7 @@ DENY SET LABEL foo ON GRAPH * TO regularUsers [NOTE] ==== -If no instances of this label exist on the database, then the xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW LABEL] privilege is also required. +If no instances of this label exist on the database, then the xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW LABEL] privilege is also required. ==== @@ -275,7 +275,7 @@ DENY SET PROPERTY { foo } ON GRAPH * NODES bar TO regularUsers [NOTE] ==== -If the user attempts to set a property with a property name that does not already exist on the database, the user must also possess the xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW PROPERTY NAME] privilege. +If the user attempts to set a property with a property name that does not already exist on the database, the user must also possess the xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW PROPERTY NAME] privilege. ==== @@ -305,15 +305,15 @@ GRANT MERGE {*} ON GRAPH neo4j ELEMENTS * TO regularUsers ---- It is not possible to deny the `MERGE` privilege. -If you wish to prevent a user from creating elements and setting properties: use xref::access-control/privileges-writes.adoc#access-control-privileges-writes-create[DENY CREATE] or xref::access-control/privileges-writes.adoc#access-control-privileges-writes-set-property[DENY SET PROPERTY]. +If you wish to prevent a user from creating elements and setting properties: use xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-create[DENY CREATE] or xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-set-property[DENY SET PROPERTY]. [NOTE] ==== If the user attempts to create nodes with a label that does not already exist on the database, the user must also possess the -xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW LABEL] privilege. +xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW LABEL] privilege. The same applies to new relationships and properties - the -xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW RELATIONSHIP TYPE] or -xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW PROPERTY NAME] privileges are required. +xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW RELATIONSHIP TYPE] or +xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW PROPERTY NAME] privileges are required. ==== diff --git a/modules/ROOT/pages/aliases.adoc b/modules/ROOT/pages/administration/aliases.adoc similarity index 96% rename from modules/ROOT/pages/aliases.adoc rename to modules/ROOT/pages/administration/aliases.adoc index aa9bffb76..4104f5431 100644 --- a/modules/ROOT/pages/aliases.adoc +++ b/modules/ROOT/pages/administration/aliases.adoc @@ -13,7 +13,7 @@ A local database alias can only target a database within the same DBMS. A remote database alias may target a database from another Neo4j DBMS. When a query is run against a database alias, it will be redirected to the target database. The home database for users can be set to an alias, which will be resolved to the target database on use. -Both local and remote database aliases can be created as part of a xref::databases.adoc#administration-databases-create-composite-database[composite database]. +Both local and remote database aliases can be created as part of a xref::administration/databases.adoc#administration-databases-create-composite-database[composite database]. A local database alias can be used in all other Cypher commands in place of the target database. Please note that the local database alias will be resolved while executing the command. @@ -24,14 +24,14 @@ Remote database aliases require configuration to safely connect to the remote ta It is not possible to impersonate a user on the remote database or to execute an administration command on the remote database via a remote database alias. Database aliases can be created and managed using a set of Cypher administration commands executed against the `system` database. -The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. +The required privileges are described xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. When connected to the DBMS over Bolt, administration commands are automatically routed to the `system` database. The syntax of the database alias management commands is as follows: [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Alias management command syntax @@ -269,7 +269,7 @@ DRIVER { //// Available database aliases can be seen using `SHOW ALIASES FOR DATABASE`. -The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. +The required privileges are described xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. `SHOW ALIASES FOR DATABASE` will produce a table of database aliases with the following columns: @@ -300,7 +300,7 @@ The required privileges are described xref::access-control/dbms-administration.a | driver | The driver options for connection to the remote database or `null` if the target database is local or if no driver settings are added. -List of xref::aliases.adoc#remote-alias-driver-settings[driver settings] allowed for remote database aliases. +List of xref::administration/aliases.adoc#remote-alias-driver-settings[driver settings] allowed for remote database aliases. | MAP | properties @@ -472,7 +472,7 @@ It is also possible to use `SKIP` and `LIMIT` to paginate the results. Database aliases can be created using `CREATE ALIAS`. -The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. +The required privileges are described xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. .Create alias command syntax [options="header", width="100%", cols="5a,2"] @@ -511,7 +511,7 @@ The `IF NOT EXISTS` and `OR REPLACE` parts of this command cannot be used togeth [NOTE] ==== -Database alias names are subject to the rules specified in the xref:alias-management-escaping[Alias names and escaping] section. +Database alias names are subject to the rules specified in the xref:administration/alias-management-escaping[Alias names and escaping] section. ==== [[database-management-create-local-database-alias]] @@ -718,7 +718,7 @@ FOR DATABASE .+Creating remote database aliases with driver settings+ ====== It is possible to override the default driver settings per database alias, which are used for connecting to the remote database. -The full list of supported driver settings can be seen xref::aliases.adoc#remote-alias-driver-settings[here]. +The full list of supported driver settings can be seen xref::administration/aliases.adoc#remote-alias-driver-settings[here]. .Query [source, cypher] @@ -800,7 +800,7 @@ SHOW ALIAS `remote-northwind-2021` FOR DATABASE YIELD name, properties [[alias-management-create-composite-database-alias]] === Create database aliases in composite databases -Both local and remote database aliases can be part of a xref::databases.adoc#administration-databases-create-composite-database[composite database]. +Both local and remote database aliases can be part of a xref::administration/databases.adoc#administration-databases-create-composite-database[composite database]. Create a database alias in a composite database by giving the name of the composite database as namespace for the alias. @@ -905,7 +905,7 @@ CREATE ALIAS garden.trees FOR DATABASE trees AT 'neo4j+s://location:7687' USER a //// Database aliases can be altered using `ALTER ALIAS` to change its database target, properties, url, user credentials, or driver settings. -The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. +The required privileges are described xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. Only the clauses used will be altered. [NOTE] @@ -1193,7 +1193,7 @@ PROPERTIES { treeVersion: 2 } Both local and remote database aliases can be deleted using the `DROP ALIAS` command. -The required privileges are described xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. +The required privileges are described xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[here]. .+Deleting local database aliases+ diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/administration/databases.adoc similarity index 99% rename from modules/ROOT/pages/databases.adoc rename to modules/ROOT/pages/administration/databases.adoc index bec460963..d2ffa03e1 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/administration/databases.adoc @@ -26,7 +26,7 @@ The syntax of the database management commands is as follows: [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Database management command syntax @@ -637,7 +637,7 @@ SHOW DATABASES YIELD name, type, access, role, writer, constituents |=== In order to create database aliases in the composite database, give the composite database as namespace for the alias. -For information about creating aliases in composite databases, see xref:aliases.adoc#alias-management-create-composite-database-alias[here]. +For information about creating aliases in composite databases, see xref:administration/aliases.adoc#alias-management-create-composite-database-alias[here]. [role=enterprise-edition not-on-aura] diff --git a/modules/ROOT/pages/access-control/index.adoc b/modules/ROOT/pages/administration/index.adoc similarity index 60% rename from modules/ROOT/pages/access-control/index.adoc rename to modules/ROOT/pages/administration/index.adoc index bb2657163..c6713ecd0 100644 --- a/modules/ROOT/pages/access-control/index.adoc +++ b/modules/ROOT/pages/administration/index.adoc @@ -1,30 +1,15 @@ -:description: Neo4j role-based access control and fine-grained security. += Administration -[[access-control]] -= Access control +Cypher contains a number of administrative commands which allows for efficient and sophisticated database management, alias management, server management, and role-based access-control. -[abstract] --- -This section explains how to manage Neo4j role-based access control and fine-grained security. --- +More information about each of these topics can be found in the following sections: -Neo4j has a complex security model stored in the system graph, which is maintained on a special database called the `system` database. -All administrative commands need to be executed against the `system` database. -When connected to the DBMS over `bolt`, administrative commands are automatically routed to the `system` database. -For more information on how to manage multiple databases, refer to the section on xref::databases.adoc[administering databases]. +* xref:administration/databases.adoc[] +* xref:administration/aliases.adoc[] +* xref:administration/servers.adoc[] +* xref:administration/access-control/index.adoc[] -The concept of _role-based access control_ was introduced in Neo4j 3.1. -Since then, it has been possible to create users and assign them to roles to control whether users can read, write and administer the database. -In Neo4j 4.0 this model was enhanced significantly with the addition of _privileges_, which are the underlying access-control rules by which the users rights are defined. - -The original built-in roles still exist with almost the exact same access rights, but they are no-longer statically defined (see xref::access-control/built-in-roles.adoc[Built-in roles]). -Instead, they are defined in terms of their underlying _privileges_, and they can be modified by adding or removing these access rights. - -In addition, any newly created roles can be assigned to any combination of _privileges_, so that you may set specific access controls for them. -Another new major capability is the _sub-graph_ access control, through which read access to the graph can be limited to specific combinations of labels, relationship types, and properties. - - -[[access-control-syntax]] +[[administration-syntax]] == Syntax summaries Almost all administration commands have variations. diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/administration/servers.adoc similarity index 90% rename from modules/ROOT/pages/access-control/manage-servers.adoc rename to modules/ROOT/pages/administration/servers.adoc index c3f5a5242..b12574073 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/administration/servers.adoc @@ -1,8 +1,7 @@ :description: This section explains how to use Cypher to manage servers in Neo4j. [role=enterprise-edition] [[server-management]] -= Managing servers - += Server management Servers can be added and managed using a set of Cypher administration commands executed against the `system` database. @@ -14,7 +13,7 @@ When connected to the DBMS over `bolt`, administration commands are automaticall [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== [cols="<15s,<85"] @@ -36,7 +35,7 @@ For more information see <>. | Required privilege a| `GRANT SERVER MANAGEMENT` -(see xref:access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) +(see xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) |=== [cols="<15s,<85"] @@ -58,7 +57,7 @@ For more information see <>. | Required privilege a| `GRANT SERVER MANAGEMENT` -(see xref:access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) +(see xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) |=== [cols="<15s,<85"] @@ -80,7 +79,7 @@ For more information see <>. | Required privilege a| `GRANT SERVER MANAGEMENT` -(see xref:access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) +(see xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) |=== [cols="<15s,<85"] @@ -102,7 +101,7 @@ For more information see <>. | Required privilege a| `GRANT SERVER MANAGEMENT` -(see xref:access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) +(see xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) |=== [cols="<15s,<85"] @@ -124,7 +123,7 @@ For more information see <>. | Required privilege a| `GRANT SERVER MANAGEMENT` -(see xref:access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) +(see xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) |=== [cols="<15s,<85"] @@ -146,7 +145,7 @@ For more information see <>. | Required privilege a| `GRANT SERVER MANAGEMENT` -(see xref:access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) +(see xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) |=== [cols="<15s,<85"] @@ -171,7 +170,7 @@ For more information see <>. | Required privilege a| `GRANT SHOW SERVERS` -(see xref:access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) +(see xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT privileges]) |=== [[server-management-show-servers]] diff --git a/modules/ROOT/pages/clauses/index.adoc b/modules/ROOT/pages/clauses/index.adoc index 2bcb03ad5..c5e139ea3 100644 --- a/modules/ROOT/pages/clauses/index.adoc +++ b/modules/ROOT/pages/clauses/index.adoc @@ -12,13 +12,13 @@ This section contains information on all the clauses in the Cypher query languag [[administration-clauses]] == Administration clauses -These comprise clauses used to manage databases, schema and security; further details can found in xref::databases.adoc[Database management] and xref::access-control/index.adoc[Access control]. +These comprise clauses used to manage databases, schema and security; further details can found in xref::administration/databases.adoc[Database management] and xref::administration/access-control/index.adoc[Access control]. [options="header"] |=== | Clause | Description -m| xref::databases.adoc[CREATE \| DROP \| START \| STOP DATABASE] +m| xref::administration/databases.adoc[CREATE \| DROP \| START \| STOP DATABASE] | Create, drop, start or stop a database. m| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE \| DROP INDEX] @@ -27,7 +27,7 @@ m| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREAT m| xref::constraints/syntax.adoc[CREATE \| DROP CONSTRAINT] | Create or drop a constraint pertaining to either a node label or relationship type, and a property. -| xref::access-control/index.adoc[Access control] +| xref::administration/access-control/index.adoc[Access control] | Manage users, roles, and privileges for database, graph and sub-graph access control. |=== diff --git a/modules/ROOT/pages/clauses/listing-functions.adoc b/modules/ROOT/pages/clauses/listing-functions.adoc index e0c4d5ea6..9c2324b76 100644 --- a/modules/ROOT/pages/clauses/listing-functions.adoc +++ b/modules/ROOT/pages/clauses/listing-functions.adoc @@ -60,13 +60,13 @@ m| BOOLEAN m| rolesExecution a| List of roles permitted to execute this function. -Is `null` without the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Is `null` without the xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. m| LIST OF STRING m| rolesBoostedExecution a| List of roles permitted to use boosted mode when executing this function. -Is `null` without the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Is `null` without the xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. m| LIST OF STRING |=== @@ -76,7 +76,7 @@ m| LIST OF STRING [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== List functions, either all or only built-in or user-defined:: @@ -119,7 +119,7 @@ SHOW [ALL|BUILT IN|USER DEFINED] FUNCTION[S] EXECUTABLE BY username [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- -Required privilege xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[`SHOW USER`]. +Required privilege xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[`SHOW USER`]. This command cannot be used for LDAP users. [NOTE] @@ -358,7 +358,7 @@ SHOW FUNCTIONS EXECUTABLE BY CURRENT USER YIELD * 6+d|Rows: 10 |=== -Notice that the two `roles` columns are empty due to missing the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Notice that the two `roles` columns are empty due to missing the xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. The second option, is to filter for a specific user: diff --git a/modules/ROOT/pages/clauses/listing-procedures.adoc b/modules/ROOT/pages/clauses/listing-procedures.adoc index f11b03808..9798ad1ce 100644 --- a/modules/ROOT/pages/clauses/listing-procedures.adoc +++ b/modules/ROOT/pages/clauses/listing-procedures.adoc @@ -58,13 +58,13 @@ m| BOOLEAN m| rolesExecution a| List of roles permitted to execute this procedure. -Is `null` without the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Is `null` without the xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. m| LIST OF STRING m| rolesBoostedExecution a| List of roles permitted to use boosted mode when executing this procedure. -Is `null` without the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Is `null` without the xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. m| LIST OF STRING m| option @@ -78,7 +78,7 @@ m| MAP [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== List all procedures:: @@ -121,7 +121,7 @@ SHOW PROCEDURE[S] EXECUTABLE BY username [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- -Requires the privilege xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[`SHOW USER`]. +Requires the privilege xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[`SHOW USER`]. This command cannot be used for LDAP users. [NOTE] @@ -365,12 +365,12 @@ SHOW PROCEDURES EXECUTABLE BY CURRENT USER YIELD * |=== The above table only displays the first 15 results of the query. -Note that the two `roles` columns are empty due to missing the xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. +Note that the two `roles` columns are empty due to missing the xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[`SHOW ROLE`] privilege. Also note that the following columns are not present in the table: `mode`, `worksOnSystem`, `signature`, `argumentDescription`, `returnDescription`, `admin`, and `options`. The second option for using the `EXECUTABLE` clause is to filter the list to only contain procedures executable by a specific user. The below example shows the procedures available to the user `jake`, who has been granted the `EXECUTE PROCEDURE dbms.*` privilege by the `admin` of the database. -(More information about `DBMS EXECUTE` privilege administration can be found xref::access-control/dbms-administration.adoc#access-control-dbms-administration-execute[here]). +(More information about `DBMS EXECUTE` privilege administration can be found xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-execute[here]). .Query [source, cypher, role=test-result-skip] diff --git a/modules/ROOT/pages/clauses/listing-settings.adoc b/modules/ROOT/pages/clauses/listing-settings.adoc index 39b8d8e1f..c3cab006f 100644 --- a/modules/ROOT/pages/clauses/listing-settings.adoc +++ b/modules/ROOT/pages/clauses/listing-settings.adoc @@ -73,7 +73,7 @@ m| STRING [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== List settings:: diff --git a/modules/ROOT/pages/clauses/transaction-clauses.adoc b/modules/ROOT/pages/clauses/transaction-clauses.adoc index 5053bb3a7..13c9a9adc 100644 --- a/modules/ROOT/pages/clauses/transaction-clauses.adoc +++ b/modules/ROOT/pages/clauses/transaction-clauses.adoc @@ -211,7 +211,7 @@ The `SHOW TRANSACTIONS` command can be combined with multiple `SHOW TRANSACTIONS [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== List transactions on the current server:: @@ -232,7 +232,7 @@ Transaction IDs must be supplied as one or more comma-separated quoted strings o When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. ==== -A user with the xref::access-control/database-administration.adoc#access-control-database-administration-transaction[`SHOW TRANSACTION`] privilege can view the currently executing transactions in accordance with the privilege grants. +A user with the xref::administration/access-control/database-administration.adoc#access-control-database-administration-transaction[`SHOW TRANSACTION`] privilege can view the currently executing transactions in accordance with the privilege grants. All users may view all of their own currently executing transactions. @@ -373,7 +373,7 @@ The `TERMINATE TRANSACTIONS` command can be combined with multiple `SHOW TRANSAC [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== Terminate transactions by ID on the current server:: @@ -398,7 +398,7 @@ Transaction IDs must be supplied as one or more comma-separated quoted strings o When using the `WHERE` or `RETURN` clauses, the `YIELD` clause is mandatory and must not be omitted. ==== -A user with the xref::access-control/database-administration.adoc#access-control-database-administration-transaction[`TERMINATE TRANSACTION`] privilege can terminate transactions in accordance with the privilege grants. +A user with the xref::administration/access-control/database-administration.adoc#access-control-database-administration-transaction[`TERMINATE TRANSACTION`] privilege can terminate transactions in accordance with the privilege grants. All users may terminate their own currently executing transactions. diff --git a/modules/ROOT/pages/constraints/syntax.adoc b/modules/ROOT/pages/constraints/syntax.adoc index 79c2e9496..76909c2fe 100644 --- a/modules/ROOT/pages/constraints/syntax.adoc +++ b/modules/ROOT/pages/constraints/syntax.adoc @@ -6,7 +6,7 @@ [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== [[constraints-syntax-create]] @@ -28,7 +28,7 @@ There is no supported index configuration for range indexes. [NOTE] ==== -Creating a constraint requires the xref::access-control/database-administration.adoc#access-control-database-administration-constraints[`CREATE CONSTRAINT` privilege]. +Creating a constraint requires the xref::administration/access-control/database-administration.adoc#access-control-database-administration-constraints[`CREATE CONSTRAINT` privilege]. ==== [[constraints-syntax-create-node-unique]] @@ -192,7 +192,7 @@ With the `IF EXISTS` flag, no error is thrown and nothing happens should the con [NOTE] ==== -Dropping a constraint requires the xref::access-control/database-administration.adoc#access-control-database-administration-constraints[`DROP CONSTRAINT` privilege]. +Dropping a constraint requires the xref::administration/access-control/database-administration.adoc#access-control-database-administration-constraints[`DROP CONSTRAINT` privilege]. ==== @@ -203,7 +203,7 @@ List constraints in the database, either all or filtered on constraint type. [NOTE] ==== -Listing constraints requires the xref::access-control/database-administration.adoc#access-control-database-administration-constraints[`SHOW CONSTRAINTS` privilege]. +Listing constraints requires the xref::administration/access-control/database-administration.adoc#access-control-database-administration-constraints[`SHOW CONSTRAINTS` privilege]. ==== The simple version of the command allows for a `WHERE` clause and will give back the default set of output columns: diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 43671a6cb..7c9df9285 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -2667,7 +2667,7 @@ label:new[] DEFAULT GRAPH ---- a| -New optional part of the Cypher commands for xref:access-control/database-administration.adoc[database privileges]. +New optional part of the Cypher commands for xref:administration/access-control/database-administration.adoc[database privileges]. a| @@ -2701,7 +2701,7 @@ EXECUTE ---- a| New Cypher commands for administering privileges for executing procedures and user defined functions. -See xref:access-control/dbms-administration.adoc#access-control-dbms-administration-execute[The DBMS `EXECUTE` privileges]. +See xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-execute[The DBMS `EXECUTE` privileges]. a| @@ -2969,21 +2969,21 @@ New support for `YIELD` and `WHERE` clauses to allow filtering results. a| label:functionality[] label:new[] + -xref:access-control/database-administration.adoc#access-control-database-administration-transaction[TRANSACTION MANAGEMENT] privileges +xref:administration/access-control/database-administration.adoc#access-control-database-administration-transaction[TRANSACTION MANAGEMENT] privileges a| New Cypher commands for administering transaction management. a| label:functionality[] label:new[] + -DBMS xref:access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[USER MANAGEMENT] privileges +DBMS xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[USER MANAGEMENT] privileges a| New Cypher commands for administering user management. a| label:functionality[] label:new[] + -DBMS xref:access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[DATABASE MANAGEMENT] privileges +DBMS xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[DATABASE MANAGEMENT] privileges a| New Cypher commands for administering database management. @@ -2991,7 +2991,7 @@ New Cypher commands for administering database management. a| label:functionality[] label:new[] + -DBMS xref:access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[PRIVILEGE MANAGEMENT] privileges +DBMS xref:administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[PRIVILEGE MANAGEMENT] privileges a| New Cypher commands for administering privilege management. @@ -3031,7 +3031,7 @@ label:new[] ON DEFAULT DATABASE ---- a| -New optional part of the Cypher commands for xref:access-control/database-administration.adoc[database privileges]. +New optional part of the Cypher commands for xref:administration/access-control/database-administration.adoc[database privileges]. |=== [[cypher-deprecations-additions-removals-4.0]] @@ -3351,21 +3351,21 @@ This Neo4j Enterprise Edition only feature involves a new runtime that has many a| label:functionality[] label:new[] + -xref:databases.adoc[Multi-database administration] +xref:administration/databases.adoc[Multi-database administration] a| New Cypher commands for administering multiple databases. a| label:functionality[] label:new[] + -xref:access-control/index.adoc[Access control] +xref:administration/access-control/index.adoc[Access control] a| New Cypher commands for administering role-based access control. a| label:functionality[] label:new[] + -xref:access-control/manage-privileges.adoc[Fine-grained security] +xref:administration/access-control/manage-privileges.adoc[Fine-grained security] a| New Cypher commands for administering dbms, database, graph and sub-graph access control. diff --git a/modules/ROOT/pages/functions/scalar.adoc b/modules/ROOT/pages/functions/scalar.adoc index 1d804fac5..c74ef39cf 100644 --- a/modules/ROOT/pages/functions/scalar.adoc +++ b/modules/ROOT/pages/functions/scalar.adoc @@ -353,7 +353,7 @@ The identifier for a relationship is guaranteed to be unique among other relatio [NOTE] ==== -On a xref:databases.adoc#administration-databases-create-composite-database[composite database], the `id()` function should be used with caution. +On a xref:administration/databases.adoc#administration-databases-create-composite-database[composite database], the `id()` function should be used with caution. It is recommended to use xref:functions/scalar.adoc#functions-elementid[`elementId()`] instead. When called in database-specific subqueries, the resulting id value for a node or relationship is local to that database. diff --git a/modules/ROOT/pages/indexes-for-full-text-search.adoc b/modules/ROOT/pages/indexes-for-full-text-search.adoc index cd321827a..9547f0b6d 100644 --- a/modules/ROOT/pages/indexes-for-full-text-search.adoc +++ b/modules/ROOT/pages/indexes-for-full-text-search.adoc @@ -103,7 +103,7 @@ A full-text index applies to a list of labels or a list of relationship types, f [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== .Syntax for creating full-text indexes diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index 9a4ec10d7..a3a10aaa6 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -80,7 +80,7 @@ It may still throw an error if conflicting constraints exist, such as constraint [NOTE] ==== -The syntax descriptions use xref:access-control/index.adoc#access-control-syntax[the style] from access control. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== @@ -318,9 +318,9 @@ SHOW [ALL \| FULLTEXT \| LOOKUP \| POINT \| RANGE \| TEXT] INDEX[ES] |=== -Creating an index requires xref::access-control/database-administration.adoc#access-control-database-administration-index[the `CREATE INDEX` privilege], -while dropping an index requires xref::access-control/database-administration.adoc#access-control-database-administration-index[the `DROP INDEX` privilege] and -listing indexes require xref::access-control/database-administration.adoc#access-control-database-administration-index[the `SHOW INDEX` privilege]. +Creating an index requires xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `CREATE INDEX` privilege], +while dropping an index requires xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `DROP INDEX` privilege] and +listing indexes require xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `SHOW INDEX` privilege]. xref::query-tuning/using.adoc[Planner hints and the USING keyword] describes how to make the Cypher planner use specific indexes (especially in cases where the planner would not necessarily have used them). diff --git a/modules/ROOT/pages/introduction/cypher_neo4j.adoc b/modules/ROOT/pages/introduction/cypher_neo4j.adoc index 01ceb8486..02a0ba62a 100644 --- a/modules/ROOT/pages/introduction/cypher_neo4j.adoc +++ b/modules/ROOT/pages/introduction/cypher_neo4j.adoc @@ -14,7 +14,7 @@ Cypher works almost identically between the two editions, but there are key area |=== | Feature | Enterprise Edition | Community Edition -| xref::databases.adoc[Multi-database] +| xref::administration/databases.adoc[Multi-database] a| Any number of user databases. a| @@ -22,9 +22,9 @@ Only `system` and one user database. | Role-based security a| -User, role, and privilege management for flexible xref::access-control/index.adoc[access control] and xref::access-control/manage-privileges.adoc[sub-graph access control]. +User, role, and privilege management for flexible xref::administration/access-control/index.adoc[access control] and xref::administration/access-control/manage-privileges.adoc[sub-graph access control]. a| -xref::access-control/manage-users.adoc[Multi-user management]. +xref::administration/access-control/manage-users.adoc[Multi-user management]. All users have full access rights. | Constraints @@ -83,7 +83,7 @@ A fresh installation of Neo4j includes two databases: * `system` - the system database described above, containing meta-data on the DBMS and security configuration. * `neo4j` - the default database, named using the config option `dbms.default_database=neo4j`. -For more information about the _system_ database, see the sections on xref::databases.adoc[Database management] and xref::access-control/index.adoc[Access control]. +For more information about the _system_ database, see the sections on xref::administration/databases.adoc[Database management] and xref::administration/access-control/index.adoc[Access control]. === Query considerations diff --git a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc index c656ad02e..5432dbbca 100644 --- a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc +++ b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc @@ -46,7 +46,7 @@ A fresh installation of Neo4j includes two databases: * `system` - the system database described above, containing meta-data on the DBMS and security configuration. * `neo4j` - the default database, named using the config option `dbms.default_database=neo4j`. -For more information about the _system_ database, see the sections on xref::databases.adoc[Database management] and xref::access-control/index.adoc[Access control]. +For more information about the _system_ database, see the sections on xref::/administration/databases.adoc[Database management] and xref::administration/access-control/index.adoc[Access control]. == Different editions of Neo4j @@ -60,7 +60,7 @@ However, it is worth listing up-front the key areas that are not supported in th |=== | Feature | Enterprise | Community -| xref::databases.adoc[Multi-database] +| xref::administration/databases.adoc[Multi-database] a| Any number of user databases. a| @@ -68,9 +68,9 @@ Only `system` and one user database. | Role-based security a| -User, role, and privilege management for flexible xref::access-control/index.adoc[access control] and xref::access-control/manage-privileges.adoc[sub-graph access control]. +User, role, and privilege management for flexible xref::administration/access-control/index.adoc[access control] and xref::administration/access-control/manage-privileges.adoc[sub-graph access control]. a| -xref::access-control/manage-users.adoc[Multi-user management]. +xref::administration/access-control/manage-users.adoc[Multi-user management]. All users have full access rights. | Constraints diff --git a/modules/ROOT/pages/keyword-glossary.adoc b/modules/ROOT/pages/keyword-glossary.adoc index 16e3bfecf..a3bf84cc4 100644 --- a/modules/ROOT/pages/keyword-glossary.adoc +++ b/modules/ROOT/pages/keyword-glossary.adoc @@ -1076,179 +1076,179 @@ The following commands are only executable against the `system` database: |=== | Command | Admin category | Description -| xref::aliases.adoc#alias-management-alter-database-alias[ALTER ALIAS ... [IF EXISTS\] SET DATABASE ...] +| xref::administration/aliases.adoc#alias-management-alter-database-alias[ALTER ALIAS ... [IF EXISTS\] SET DATABASE ...] | Database alias | Modifies a database alias. -| xref::access-control/manage-users.adoc#access-control-alter-password[ALTER CURRENT USER SET PASSWORD FROM ... TO] +| xref::administration/access-control/manage-users.adoc#access-control-alter-password[ALTER CURRENT USER SET PASSWORD FROM ... TO] | User and role | Change the password of the user that is currently logged in. -| xref:databases.adoc#administration-databases-alter-database[ALTER DATABASE ... [IF EXISTS\] [SET ACCESS {READ ONLY \| READ WRITE}\] [SET TOPOLOGY n PRIMAR{Y\|IES} [m SECONDAR{Y\|IES}\]\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] +| xref::administration/databases.adoc#administration-databases-alter-database[ALTER DATABASE ... [IF EXISTS\] [SET ACCESS {READ ONLY \| READ WRITE}\] [SET TOPOLOGY n PRIMAR{Y\|IES} [m SECONDAR{Y\|IES}\]\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] | Database | Modifies the database access mode and / or topology. -| xref::access-control/manage-servers.adoc#server-management-syntax[ALTER SERVER ... [SET OPTIONS\] {...}] +| xref::administration/servers.adoc#server-management-syntax[ALTER SERVER ... [SET OPTIONS\] {...}] | Server management | Modifies the options for a server. -| xref::access-control/manage-users.adoc#access-control-alter-users[ALTER USER ... [IF EXISTS\] [SET [PLAINTEXT \| ENCRYPTED\] PASSWORD {password [CHANGE [NOT\] REQUIRED\] \| CHANGE [NOT\] REQUIRED}\] [SET STATUS {ACTIVE \| SUSPENDED}\] [SET HOME DATABASE name\] [REMOVE HOME DATABASE\]] +| xref::administration/access-control/manage-users.adoc#access-control-alter-users[ALTER USER ... [IF EXISTS\] [SET [PLAINTEXT \| ENCRYPTED\] PASSWORD {password [CHANGE [NOT\] REQUIRED\] \| CHANGE [NOT\] REQUIRED}\] [SET STATUS {ACTIVE \| SUSPENDED}\] [SET HOME DATABASE name\] [REMOVE HOME DATABASE\]] | User and role | Changes a user account. Changes can include setting a new password, setting the account status, setting or removing home database and enabling that the user should change the password upon next login. -| xref::aliases.adoc#alias-management-create-database-alias[CREATE [OR REPLACE\] ALIAS ... [IF NOT EXISTS\] FOR DATABASE ...] +| xref::administration/aliases.adoc#alias-management-create-database-alias[CREATE [OR REPLACE\] ALIAS ... [IF NOT EXISTS\] FOR DATABASE ...] | Database alias | Creates a new database alias. -| xref::databases.adoc#administration-databases-create-composite-database[CREATE [OR REPLACE\] COMPOSITE DATABASE ... [IF NOT EXISTS\] [OPTIONS {}\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] +| xref::administration/databases.adoc#administration-databases-create-composite-database[CREATE [OR REPLACE\] COMPOSITE DATABASE ... [IF NOT EXISTS\] [OPTIONS {}\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] | Database | Creates a new composite database. -| xref:databases.adoc#administration-databases-create-database[CREATE [OR REPLACE\] DATABASE ... [IF NOT EXISTS\] [TOPOLOGY n PRIMAR{Y\|IES} [m SECONDAR{Y\|IES}\]\] [OPTIONS {optionKey: optionValue[, ...\]}\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] +| xref:administration/databases.adoc#administration-databases-create-database[CREATE [OR REPLACE\] DATABASE ... [IF NOT EXISTS\] [TOPOLOGY n PRIMAR{Y\|IES} [m SECONDAR{Y\|IES}\]\] [OPTIONS {optionKey: optionValue[, ...\]}\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] | Database | Creates a new database. -| xref::access-control/manage-roles.adoc#access-control-create-roles[CREATE [OR REPLACE\] ROLE ... [IF NOT EXISTS\] [AS COPY OF\]] +| xref::administration/access-control/manage-roles.adoc#access-control-create-roles[CREATE [OR REPLACE\] ROLE ... [IF NOT EXISTS\] [AS COPY OF\]] | User and role | Creates new roles. -| xref::access-control/manage-users.adoc#access-control-create-users[CREATE [OR REPLACE\] USER ... [IF NOT EXISTS\] SET [PLAINTEXT \| ENCRYPTED\] PASSWORD ... [[SET PASSWORD\] CHANGE [NOT\] REQUIRED\] [SET STATUS {ACTIVE \| SUSPENDED}\] [SET HOME DATABASE name\]] +| xref::administration/access-control/manage-users.adoc#access-control-create-users[CREATE [OR REPLACE\] USER ... [IF NOT EXISTS\] SET [PLAINTEXT \| ENCRYPTED\] PASSWORD ... [[SET PASSWORD\] CHANGE [NOT\] REQUIRED\] [SET STATUS {ACTIVE \| SUSPENDED}\] [SET HOME DATABASE name\]] | User and role | Creates a new user and sets the password for the new account. Optionally the account status and home database can also be set and if the user should change the password upon first login. -| xref::access-control/manage-servers.adoc#server-management-deallocate[DEALLOCATE DATABASE(S) FROM SERVER(S) ...] +| xref::administration/servers.adoc#server-management-deallocate[DEALLOCATE DATABASE(S) FROM SERVER(S) ...] | Server management | Removes databases from the specified servers. -| xref:access-control/database-administration.adoc[DENY [IMMUTABLE\] ... ON DATABASE ... TO] +| xref:administration/access-control/database-administration.adoc[DENY [IMMUTABLE\] ... ON DATABASE ... TO] | Privilege | Denies a database or schema privilege to one or multiple roles. -| xref:access-control/dbms-administration.adoc[DENY [IMMUTABLE\] ... ON DBMS TO] +| xref:administration/access-control/dbms-administration.adoc[DENY [IMMUTABLE\] ... ON DBMS TO] | Privilege | Denies a DBMS privilege to one or multiple roles. -| xref:access-control/manage-privileges.adoc#access-control-graph-privileges[DENY [IMMUTABLE\] ... ON GRAPH ... [NODES \| RELATIONSHIPS \| ELEMENTS\] ... TO] +| xref:administration/access-control/manage-privileges.adoc#access-control-graph-privileges[DENY [IMMUTABLE\] ... ON GRAPH ... [NODES \| RELATIONSHIPS \| ELEMENTS\] ... TO] | Privilege | Denies a graph privilege for one or multiple specified elements to one or multiple roles. -| xref::aliases.adoc#alias-management-drop-database-alias[DROP ALIAS ... [IF EXISTS\] FOR DATABASE] +| xref::administration/aliases.adoc#alias-management-drop-database-alias[DROP ALIAS ... [IF EXISTS\] FOR DATABASE] | Database alias | Deletes a specified database alias. -| xref::databases.adoc#administration-databases-drop-database[DROP COMPOSITE DATABASE ... [IF EXISTS\] [DUMP DATA \| DESTROY DATA\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] +| xref::administration/databases.adoc#administration-databases-drop-database[DROP COMPOSITE DATABASE ... [IF EXISTS\] [DUMP DATA \| DESTROY DATA\] [WAIT [n [SEC[OND[S\]\]\]\]\|NOWAIT\]] | Database | Deletes a specified composite database. -| xref::databases.adoc#administration-databases-drop-database[DROP DATABASE ... [IF EXISTS\] [DUMP DATA \| DESTROY DATA\]] +| xref::administration/databases.adoc#administration-databases-drop-database[DROP DATABASE ... [IF EXISTS\] [DUMP DATA \| DESTROY DATA\]] | Database | Deletes a specified database (either standard or composite). -| xref::access-control/manage-roles.adoc#access-control-drop-roles[DROP ROLE ... [IF EXISTS\]] +| xref::administration/access-control/manage-roles.adoc#access-control-drop-roles[DROP ROLE ... [IF EXISTS\]] | User and role | Deletes a specified role. -| xref::access-control/manage-servers.adoc#server-management-drop-server[DROP SERVER ...] +| xref::administration/servers.adoc#server-management-drop-server[DROP SERVER ...] | Server management | Removes a specified server. -| xref::access-control/manage-users.adoc#access-control-drop-users[DROP USER ... [IF EXISTS\]] +| xref::administration/access-control/manage-users.adoc#access-control-drop-users[DROP USER ... [IF EXISTS\]] | User and role | Deletes a specified user. -| xref::access-control/manage-servers.adoc#server-management-enable-server[ENABLE SERVER [OPTIONS\]] +| xref::administration/servers.adoc#server-management-enable-server[ENABLE SERVER [OPTIONS\]] | Server management | Enables a specified server. -| xref:access-control/database-administration.adoc[GRANT [IMMUTABLE\] ... ON DATABASE ... TO] +| xref:administration/access-control/database-administration.adoc[GRANT [IMMUTABLE\] ... ON DATABASE ... TO] | Privilege | Assigns a database or schema privilege to one or multiple roles. -| xref:access-control/dbms-administration.adoc[GRANT [IMMUTABLE\] ... ON DBMS TO] +| xref:administration/access-control/dbms-administration.adoc[GRANT [IMMUTABLE\] ... ON DBMS TO] | Privilege | Assigns a DBMS privilege to one or multiple roles. -| xref:access-control/manage-privileges.adoc#access-control-graph-privileges[GRANT [IMMUTABLE\] ... ON GRAPH ... [NODES \| RELATIONSHIPS \| ELEMENTS\] ... TO] +| xref:administration/access-control/manage-privileges.adoc#access-control-graph-privileges[GRANT [IMMUTABLE\] ... ON GRAPH ... [NODES \| RELATIONSHIPS \| ELEMENTS\] ... TO] | Privilege | Assigns a graph privilege for one or multiple specified elements to one or multiple roles. -| xref:access-control/manage-roles.adoc#access-control-assign-roles[GRANT [IMMUTABLE\] ROLE[S\] ... TO] +| xref:administration/access-control/manage-roles.adoc#access-control-assign-roles[GRANT [IMMUTABLE\] ROLE[S\] ... TO] | User and role | Assigns one or multiple roles to one or multiple users. -| xref::access-control/manage-servers.adoc#server-management-reallocate[REALLOCATE DATABASE(S)] +| xref::administration/servers.adoc#server-management-reallocate[REALLOCATE DATABASE(S)] | Server management | Re-balance databases among all servers. -| xref::access-control/manage-roles.adoc#access-control-rename-roles[RENAME ROLE ... [IF EXISTS\] TO ...] +| xref::administration/access-control/manage-roles.adoc#access-control-rename-roles[RENAME ROLE ... [IF EXISTS\] TO ...] | User and role | Changes the name of a role. -| xref::access-control/manage-users.adoc#access-control-rename-users[RENAME USER ... [IF EXISTS\] TO ...] +| xref::administration/access-control/manage-users.adoc#access-control-rename-users[RENAME USER ... [IF EXISTS\] TO ...] | User and role | Changes the name of a user. -| xref:access-control/database-administration.adoc[REVOKE [IMMUTABLE\] [GRANT \| DENY\] ... ON DATABASE ... FROM] +| xref:administration/access-control/database-administration.adoc[REVOKE [IMMUTABLE\] [GRANT \| DENY\] ... ON DATABASE ... FROM] | Privilege | Removes a database or schema privilege from one or multiple roles. -| xref:access-control/dbms-administration.adoc[REVOKE [IMMUTABLE\] [GRANT \| DENY\] ... ON DBMS FROM] +| xref:administration/access-control/dbms-administration.adoc[REVOKE [IMMUTABLE\] [GRANT \| DENY\] ... ON DBMS FROM] | Privilege | Removes a DBMS privilege from one or multiple roles. -| xref:access-control/manage-privileges.adoc#access-control-revoke-privileges[REVOKE [IMMUTABLE\] [GRANT \| DENY\] ... ON GRAPH ... [NODES \| RELATIONSHIPS \| ELEMENTS\] ... FROM] +| xref:administration/access-control/manage-privileges.adoc#access-control-revoke-privileges[REVOKE [IMMUTABLE\] [GRANT \| DENY\] ... ON GRAPH ... [NODES \| RELATIONSHIPS \| ELEMENTS\] ... FROM] | Privilege | Removes a graph privilege for one or multiple specified elements from one or multiple roles. -| xref::access-control/manage-roles.adoc#access-control-revoke-roles[REVOKE ROLE[S\] ... FROM] +| xref::administration/access-control/manage-roles.adoc#access-control-revoke-roles[REVOKE ROLE[S\] ... FROM] | User and role | Removes one or multiple roles from one or multiple users. -| xref::aliases.adoc#alias-management-show-alias[SHOW ALIASES FOR DATABASE] +| xref::administration/aliases.adoc#alias-management-show-alias[SHOW ALIASES FOR DATABASE] | Database alias | Returns information about all aliases, optionally including driver settings. -| xref::access-control/manage-roles.adoc#access-control-list-roles[SHOW [ALL \| POPULATED\] ROLES [WITH USERS\]] +| xref::administration/access-control/manage-roles.adoc#access-control-list-roles[SHOW [ALL \| POPULATED\] ROLES [WITH USERS\]] | User and role | Returns information about all or populated roles, optionally including the assigned users. -| xref::databases.adoc#administration-databases-show-databases[SHOW DATABASE] +| xref::administration/databases.adoc#administration-databases-show-databases[SHOW DATABASE] | Database | Returns information about a specified database. -| xref::databases.adoc#administration-databases-show-databases[SHOW DATABASES] +| xref::administration/databases.adoc#administration-databases-show-databases[SHOW DATABASES] | Database | Returns information about all databases. -| xref::access-control/manage-servers.adoc#server-management-show-servers[SHOW SERVERS] +| xref::administration/servers.adoc#server-management-show-servers[SHOW SERVERS] | Server management | Returns information about all servers. -| xref::databases.adoc#administration-databases-show-databases[SHOW DEFAULT DATABASE] +| xref::administration/databases.adoc#administration-databases-show-databases[SHOW DEFAULT DATABASE] | Database | Returns information about the default database. -| xref::databases.adoc#administration-databases-show-databases[SHOW HOME DATABASE] +| xref::administration/databases.adoc#administration-databases-show-databases[SHOW HOME DATABASE] | Database | Returns information about the current users home database. -| xref::access-control/manage-roles.adoc#access-control-list-roles[SHOW [ROLE ... \| USER ... \| ALL \] PRIVILEGES [AS [REVOKE\] COMMAND[S\]\]] +| xref::administration/access-control/manage-roles.adoc#access-control-list-roles[SHOW [ROLE ... \| USER ... \| ALL \] PRIVILEGES [AS [REVOKE\] COMMAND[S\]\]] | Privilege | Returns information about role, user or all privileges. -| xref::access-control/manage-users.adoc#access-control-list-users[SHOW USERS] +| xref::administration/access-control/manage-users.adoc#access-control-list-users[SHOW USERS] | User and role | Returns information about all users. -| xref::databases.adoc#administration-databases-start-database[START DATABASE] +| xref::administration/databases.adoc#administration-databases-start-database[START DATABASE] | Database | Starts up a specified database. -| xref::databases.adoc#administration-databases-stop-database[STOP DATABASE] +| xref::administration/databases.adoc#administration-databases-stop-database[STOP DATABASE] | Database | Stops a specified database. @@ -1262,295 +1262,295 @@ Optionally the account status and home database can also be set and if the user |=== | Name | Category | Description -| xref::access-control/database-administration.adoc#access-control-database-administration-access[ACCESS] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-access[ACCESS] | Database | Determines whether a user can access a specific database. -| xref::access-control/database-administration.adoc#access-control-database-administration-all[ALL DATABASE PRIVILEGES] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-all[ALL DATABASE PRIVILEGES] | Database and schema | Determines whether a user is allowed to access, create, drop, and list indexes and constraints, create new labels, types and property names on a specific database. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-all[ALL DBMS PRIVILEGES] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-all[ALL DBMS PRIVILEGES] | DBMS | Determines whether a user is allowed to perform role, user, database and privilege management. -| xref::access-control/privileges-writes.adoc#access-control-privileges-writes-all[ALL GRAPH PRIVILEGES] +| xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-all[ALL GRAPH PRIVILEGES] | GRAPH | Determines whether a user is allowed to perform reads and writes. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[ALTER ALIAS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[ALTER ALIAS] | DBMS | Determines whether the user can modify aliases. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[ALTER DATABASE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[ALTER DATABASE] | DBMS | Determines whether the user can modify databases and aliases. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[ALTER USER] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[ALTER USER] | DBMS | Determines whether the user can modify users. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[ASSIGN PRIVILEGE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[ASSIGN PRIVILEGE] | DBMS | Determines whether the user can assign privileges using the `GRANT` and `DENY` commands. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[ASSIGN ROLE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[ASSIGN ROLE] | DBMS | Determines whether the user can grant roles. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[COMPOSITE DATABASE MANAGEMENT] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[COMPOSITE DATABASE MANAGEMENT] | DBMS | Determines whether the user can create and delete composite databases. -| xref::access-control/database-administration.adoc#access-control-database-administration-constraints[CONSTRAINT MANAGEMENT] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-constraints[CONSTRAINT MANAGEMENT] | Schema | Determines whether a user is allowed to create, drop, and list constraints on a specific database. -| xref::access-control/privileges-writes.adoc#access-control-privileges-writes-create[CREATE] +| xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-create[CREATE] | GRAPH | Determines whether the user can create a new element (node, relationship or both). -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[CREATE ALIAS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[CREATE ALIAS] | DBMS | Determines whether the user can create new aliases. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[CREATE COMPOSITE DATABASE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[CREATE COMPOSITE DATABASE] | DBMS | Determines whether the user can create new composite databases. -| xref::access-control/database-administration.adoc#access-control-database-administration-constraints[CREATE CONSTRAINT] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-constraints[CREATE CONSTRAINT] | Schema | Determines whether a user is allowed to create constraints on a specific database. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[CREATE DATABASE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[CREATE DATABASE] | DBMS | Determines whether the user can create new databases and aliases. -| xref::access-control/database-administration.adoc#access-control-database-administration-index[CREATE INDEX] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[CREATE INDEX] | Schema | Determines whether a user is allowed to create indexes on a specific database. -| xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW NODE LABEL] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW NODE LABEL] | Schema | Determines whether a user is allowed to create new node labels on a specific database. -| xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW PROPERTY NAME] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW PROPERTY NAME] | Schema | Determines whether a user is allowed to create new property names on a specific database. -| xref::access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW RELATIONSHIP TYPE] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[CREATE NEW RELATIONSHIP TYPE] | Schema | Determines whether a user is allowed to create new relationship types on a specific database. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[CREATE ROLE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[CREATE ROLE] | DBMS | Determines whether the user can create new roles. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[CREATE USER] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[CREATE USER] | DBMS | Determines whether the user can create new users. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[ALIAS MANAGEMENT] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[ALIAS MANAGEMENT] | DBMS | Determines whether the user can create, delete, modify and list aliases. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[DATABASE MANAGEMENT] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[DATABASE MANAGEMENT] | DBMS | Determines whether the user can create, delete, and modify databases and aliases. -| xref::access-control/privileges-writes.adoc#access-control-privileges-writes-delete[DELETE] +| xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-delete[DELETE] | GRAPH | Determines whether the user can delete an element (node, relationship or both). -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[DROP ALIAS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[DROP ALIAS] | DBMS | Determines whether the user can delete aliases. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[DROP COMPOSITE DATABASE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[DROP COMPOSITE DATABASE] | DBMS | Determines whether the user can delete composite databases. -| xref::access-control/database-administration.adoc#access-control-database-administration-constraints[DROP CONSTRAINT] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-constraints[DROP CONSTRAINT] | Schema | Determines whether a user is allowed to drop constraints on a specific database. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[DROP DATABASE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[DROP DATABASE] | DBMS | Determines whether the user can delete databases and aliases. -| xref::access-control/database-administration.adoc#access-control-database-administration-index[DROP INDEX] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[DROP INDEX] | Schema | Determines whether a user is allowed to drop indexes on a specific database. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DROP ROLE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DROP ROLE] | DBMS | Determines whether the user can delete roles. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DROP USER] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[DROP USER] | DBMS | Determines whether the user can delete users. -| xref::access-control/dbms-administration.adoc#access-control-admin-procedure[EXECUTE ADMIN PROCEDURE] +| xref::administration/access-control/dbms-administration.adoc#access-control-admin-procedure[EXECUTE ADMIN PROCEDURE] | DBMS | Determines whether the user can execute admin procedures. -| xref::access-control/dbms-administration.adoc#access-control-execute-boosted-user-defined-function[EXECUTE BOOSTED FUNCTION] +| xref::administration/access-control/dbms-administration.adoc#access-control-execute-boosted-user-defined-function[EXECUTE BOOSTED FUNCTION] | DBMS | Determines whether the user gets elevated privileges when executing functions. -| xref::access-control/dbms-administration.adoc#access-control-execute-boosted-procedure[EXECUTE BOOSTED PROCEDURE] +| xref::administration/access-control/dbms-administration.adoc#access-control-execute-boosted-procedure[EXECUTE BOOSTED PROCEDURE] | DBMS | Determines whether the user gets elevated privileges when executing procedures. -| xref::access-control/dbms-administration.adoc#access-control-execute-user-defined-function[EXECUTE FUNCTION] +| xref::administration/access-control/dbms-administration.adoc#access-control-execute-user-defined-function[EXECUTE FUNCTION] | DBMS | Determines whether the user can execute functions. -| xref::access-control/dbms-administration.adoc#access-control-execute-procedure[EXECUTE PROCEDURE] +| xref::administration/access-control/dbms-administration.adoc#access-control-execute-procedure[EXECUTE PROCEDURE] | DBMS | Determines whether the user can execute procedures. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-impersonation[IMPERSONATE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-impersonation[IMPERSONATE] | DBMS | Determines whether a user can impersonate another one and assume their privileges. -| xref::access-control/database-administration.adoc#access-control-database-administration-index[INDEX MANAGEMENT] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[INDEX MANAGEMENT] | Schema | Determines whether a user is allowed to create, drop, and list indexes on a specific database. -| xref::access-control/database-administration.adoc#access-control-database-administration-index[MATCH] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[MATCH] | GRAPH | Determines whether the properties of an element (node, relationship or both) can be read and the element can be found and traversed while executing queries on the specified graph. -| xref::access-control/privileges-writes.adoc#access-control-privileges-writes-merge[MERGE] +| xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-merge[MERGE] | GRAPH | Determines whether the user can find, read, create and set properties on an element (node, relationship or both). -| xref::access-control/database-administration.adoc#access-control-database-administration-tokens[NAME MANAGEMENT] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-tokens[NAME MANAGEMENT] | Schema | Determines whether a user is allowed to create new labels, types and property names on a specific database. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[PRIVILEGE MANAGEMENT] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[PRIVILEGE MANAGEMENT] | DBMS | Determines whether the user can show, assign and remove privileges. -| xref::access-control/privileges-reads.adoc#access-control-privileges-reads-read[READ] +| xref::administration/access-control/privileges-reads.adoc#access-control-privileges-reads-read[READ] | GRAPH | Determines whether the properties of an element (node, relationship or both) can be read while executing queries on the specified graph. -| xref::access-control/privileges-writes.adoc#access-control-privileges-writes-remove-label[REMOVE LABEL] +| xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-remove-label[REMOVE LABEL] | GRAPH | Determines whether the user can remove a label from a node using the `REMOVE` clause. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[REMOVE PRIVILEGE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[REMOVE PRIVILEGE] | DBMS | Determines whether the user can remove privileges using the `REVOKE` command. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[REMOVE ROLE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[REMOVE ROLE] | DBMS | Determines whether the user can revoke roles. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[RENAME ROLE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[RENAME ROLE] | DBMS | Determines whether the user can rename roles. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[RENAME USER] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[RENAME USER] | DBMS | Determines whether the user can rename users. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[ROLE MANAGEMENT] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[ROLE MANAGEMENT] | DBMS | Determines whether the user can create, drop, grant, revoke and show roles. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SERVER MANAGEMENT] | DBMS | Determines whether the user can enable, alter, rename, reallocate, deallocate, drop, and show servers. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[SET DATABASE ACCESS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-database-management[SET DATABASE ACCESS] | DBMS | Determines whether the user can modify the database access mode. -| xref::access-control/privileges-writes.adoc#access-control-privileges-writes-set-label[SET LABEL] +| xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-set-label[SET LABEL] | GRAPH | Determines whether the user can set a label to a node using the SET clause. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[SET PASSWORDS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[SET PASSWORDS] | DBMS | Determines whether the user can modify users' passwords and whether those passwords must be changed upon first login. -| xref::access-control/privileges-writes.adoc#access-control-privileges-writes-set-property[SET PROPERTY] +| xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-set-property[SET PROPERTY] | GRAPH | Determines whether the user can set a property to an element (node, relationship or both) using the SET clause. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[SET USER HOME DATABASE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[SET USER HOME DATABASE] | DBMS | Determines whether the user can modify the home database of users. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[SET USER STATUS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[SET USER STATUS] | DBMS | Determines whether the user can modify the account status of users. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[SHOW ALIAS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-alias-management[SHOW ALIAS] | DBMS | Determines whether the user is allowed to list aliases. -| xref::access-control/database-administration.adoc#access-control-database-administration-constraints[SHOW CONSTRAINT] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-constraints[SHOW CONSTRAINT] | Schema | Determines whether the user is allowed to list constraints. -| xref::access-control/database-administration.adoc#access-control-database-administration-index[SHOW INDEX] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[SHOW INDEX] | Schema | Determines whether the user is allowed to list indexes. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[SHOW PRIVILEGE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-privilege-management[SHOW PRIVILEGE] | DBMS | Determines whether the user can get information about privileges assigned to users and roles. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[SHOW ROLE] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[SHOW ROLE] | DBMS | Determines whether the user can get information about existing and assigned roles. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SHOW SERVERS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-server-management[SHOW SERVERS] | DBMS | Determines whether the user can get information about servers. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-setting[SHOW SETTINGS] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-setting[SHOW SETTINGS] | DBMS | Determines whether the user can get information about configuration settings. -| xref::access-control/database-administration.adoc#access-control-database-administration-transaction[SHOW TRANSACTION] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-transaction[SHOW TRANSACTION] | Database | Determines whether a user is allowed to list transactions and queries. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[SHOW USER] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[SHOW USER] | DBMS | Determines whether the user can get information about existing users. -| xref::access-control/database-administration.adoc#access-control-database-administration-startstop[START] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-startstop[START] | Database | Determines whether a user can start up a specific database. -| xref::access-control/database-administration.adoc#access-control-database-administration-startstop[STOP] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-startstop[STOP] | Database | Determines whether a user can stop a specific running database. -| xref::access-control/database-administration.adoc#access-control-database-administration-transaction[TERMINATE TRANSACTION] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-transaction[TERMINATE TRANSACTION] | Database | Determines whether a user is allowed to end running transactions and queries. -| xref::access-control/database-administration.adoc#access-control-database-administration-transaction[TRANSACTION MANAGEMENT] +| xref::administration/access-control/database-administration.adoc#access-control-database-administration-transaction[TRANSACTION MANAGEMENT] | Database | Determines whether a user is allowed to list and end running transactions and queries. -| xref::access-control/privileges-reads.adoc#access-control-privileges-reads-traverse[TRAVERSE] +| xref::administration/access-control/privileges-reads.adoc#access-control-privileges-reads-traverse[TRAVERSE] | GRAPH | Determines whether an element (node, relationship or both) can be found and traversed while executing queries on the specified graph. -| xref::access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[USER MANAGEMENT] +| xref::administration/access-control/dbms-administration.adoc#access-control-dbms-administration-user-management[USER MANAGEMENT] | DBMS | Determines whether the user can create, drop, modify and show users. -| xref::access-control/privileges-writes.adoc#access-control-privileges-writes-write[WRITE] +| xref::administration/access-control/privileges-writes.adoc#access-control-privileges-writes-write[WRITE] | GRAPH | Determines whether the user can execute write operations on the specified graph. From 7a624468b6fd543fac9621c6db265b6237ccc265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 5 Apr 2023 08:46:55 +0200 Subject: [PATCH 170/383] Fix SHOW SETTINGS result table with updated list of procedures (#497) --- modules/ROOT/pages/clauses/listing-settings.adoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/ROOT/pages/clauses/listing-settings.adoc b/modules/ROOT/pages/clauses/listing-settings.adoc index c3cab006f..2fff996b7 100644 --- a/modules/ROOT/pages/clauses/listing-settings.adoc +++ b/modules/ROOT/pages/clauses/listing-settings.adoc @@ -208,10 +208,14 @@ LIMIT 10 | +"WARN"+ | +"The level of middleware logging"+ -| +"dbms.cluster.discovery.type"+ +| +"dbms.cluster.discovery.resolver_type"+ | +"LIST"+ | +"Configure the discovery type used for cluster name resolution"+ +| +"dbms.cluster.discovery.type"+ +| +"LIST"+ +| +"This setting has been moved to Cluster Address Settings"+ + | +"dbms.cluster.minimum_initial_system_primaries_count"+ | +"3"+ | +"This setting has been moved to Cluster Base Settings"+ @@ -232,10 +236,6 @@ LIMIT 10 | +"1d"+ | +"The time allowed for a database on a Neo4j server to either join a cluster or form a new cluster with at least the quorum of the members available. The members are provided by `dbms.cluster.discovery.endpoints` for the system database and by the topology graph for user databases."+ -| +"dbms.cluster.raft.client.max_channels"+ -| +"8"+ -| +"The maximum number of TCP channels between two nodes to operate the raft protocol. Each database gets allocated one channel, but a single channel can be used by more than one database."+ - 3+d|Rows: 10 |=== From 9fab0abd6eba8a822fd1c08a0c867ba324a743ce Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Tue, 11 Apr 2023 10:29:03 +0200 Subject: [PATCH 171/383] [Testing] Make a few more examples testable (#499) I removed a few `test-skip` here and there, and removed some useless attributes from code blocks where I was editing already. Also added some comments on why some examples need to be skipped. --- .../access-control/built-in-roles.adoc | 3 +- .../access-control/manage-users.adoc | 1 + modules/ROOT/pages/clauses/merge.adoc | 51 ++++++++++--------- modules/ROOT/pages/clauses/use.adoc | 13 +++-- .../pages/indexes-for-search-performance.adoc | 3 ++ .../pages/introduction/cypher_overview.adoc | 37 +++++++++----- 6 files changed, 62 insertions(+), 46 deletions(-) diff --git a/modules/ROOT/pages/administration/access-control/built-in-roles.adoc b/modules/ROOT/pages/administration/access-control/built-in-roles.adoc index cd29e7da2..24ebdaf37 100644 --- a/modules/ROOT/pages/administration/access-control/built-in-roles.adoc +++ b/modules/ROOT/pages/administration/access-control/built-in-roles.adoc @@ -453,8 +453,7 @@ To restore the role to its original capabilities two steps are needed. First, execute `DROP ROLE admin`. Secondly, run these queries: -// we cannot test this right now cause we would delete the role the test user is logged with - +// cannot test as it would require deleting the role the test user is logged with [source, cypher, role=noplay test-skip] ---- CREATE ROLE admin diff --git a/modules/ROOT/pages/administration/access-control/manage-users.adoc b/modules/ROOT/pages/administration/access-control/manage-users.adoc index 3e1d7bba8..b1d25ee05 100644 --- a/modules/ROOT/pages/administration/access-control/manage-users.adoc +++ b/modules/ROOT/pages/administration/access-control/manage-users.adoc @@ -817,6 +817,7 @@ Users can change their password using `ALTER CURRENT USER SET PASSWORD`. The old password is required in addition to the new one, and either or both can be a string value or a string parameter. When a user executes this command it will change their password as well as set the `CHANGE NOT REQUIRED` flag. +// can't test, don't want to hardcode test user password [source, cypher, role=test-skip] ---- ALTER CURRENT USER diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index bc0d2ff14..1e05639eb 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -7,7 +7,7 @@ == Introduction The `MERGE` clause either matches existing node patterns in the graph and binds them or, if not present, creates new data and binds that. -In this way, it acts as a combination of `MATCH` and `CREATE` that allows for specific actions depending on whether the specified data was matched or created. +In this way, it acts as a combination of `MATCH` and `CREATE` that allows for specific actions depending on whether the specified data was matched or created. For example, `MERGE` can be used to specify that a graph must contain a node with a `Person` label and a specific `name` property. If there isn't a node with the specific `name` property, a new node will be created with that `name` property. @@ -80,7 +80,7 @@ RETURN robert, labels(robert) A new node is created because there are no nodes labeled `Critic` in the database: .Result -[role="queryresult",options="header,footer",cols="2*(matrix) +---- +//// == What is Cypher? -Cypher is Neo4j’s declarative graph query language. -It was created in 2011 by Neo4j engineers as an SQL-equivalent language for graph databases. +Cypher is Neo4j’s declarative graph query language. +It was created in 2011 by Neo4j engineers as an SQL-equivalent language for graph databases. Similar to SQL, Cypher lets users focus on _what_ to retrieve from graph, rather than _how_ to retrieve it. As such, Cypher enables users to realize the full potential of their property graph databases by allowing for efficient and expressive queries that reveal previously unknown data connections and clusters. -Cypher provides a visual way of matching patterns and relationships. +Cypher provides a visual way of matching patterns and relationships. It relies on the following ascii-art type of syntax: `(nodes)-[:CONNECT_TO]->(otherNodes)`. -Rounded brackets are used for circular nodes, and `-[:ARROWS]->` for relationships. -Writing a query is effectively like drawing a pattern through the data in the graph. +Rounded brackets are used for circular nodes, and `-[:ARROWS]->` for relationships. +Writing a query is effectively like drawing a pattern through the data in the graph. In other words, entities such as nodes and their relationships are visually built into queries. -This makes Cypher a highly intuitive language to both read and write. +This makes Cypher a highly intuitive language to both read and write. == Cypher and SQL: key differences @@ -27,8 +36,8 @@ However, there are some important differences between the two: *Cypher is schema-flexible*:: While it is both possible and advised to enforce partial schemas using xref:constraints/index.adoc[indexes and constraints], Cypher and Neo4j offers a greater degree of schema-flexibility than SQL and a relational database. -More specifically, nodes and relationships in a Neo4j database do not have to have a specific property set to them because other nodes or relationships in the same graph have that property (unless there is an xref:constraints/examples.adoc#constraints-examples-node-property-existence[existence constraint created on that specific property]). -This means that users are not required to use a fixed schema to represent data and that they can add new attributes and relationships as their graphs evolve. +More specifically, nodes and relationships in a Neo4j database do not have to have a specific property set to them because other nodes or relationships in the same graph have that property (unless there is an xref:constraints/examples.adoc#constraints-examples-node-property-existence[existence constraint created on that specific property]). +This means that users are not required to use a fixed schema to represent data and that they can add new attributes and relationships as their graphs evolve. *Query order*:: @@ -41,11 +50,11 @@ FROM movie WHERE movie.rating > 7 ---- + -[source, cypher, role=test-skip] +[source, cypher] ---- MATCH (movie:Movie) WHERE movie.rating > 7 -RETURN movie.name +RETURN movie.title ---- *Cypher queries are more concise*:: @@ -62,7 +71,7 @@ FROM actors WHERE movies.title = "The Matrix" ---- + -[source, cypher, role=test-skip] +[source, cypher] ---- MATCH (actor:Actor)-[:ACTED_IN]->(movie:Movie {title: 'The Matrix'}) RETURN actor.name @@ -70,7 +79,7 @@ RETURN actor.name == Cypher and APOC -Neo4j supports the APOC (Awesome Procedures on Cypher) Core library. +Neo4j supports the APOC (Awesome Procedures on Cypher) Core library. The APOC Core library provides access to user-defined procedures and functions which extend the use of the Cypher query language into areas such as data integration, graph algorithms, and data conversion. -For more details, visit the link:{neo4j-docs-base-uri}/apoc/{page-version}[APOC Core page]. +For more details, visit the link:{neo4j-docs-base-uri}/apoc/{page-version}[APOC Core page]. From 696e2bafada648673343e9c4c49856599fc5abe2 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Thu, 13 Apr 2023 14:13:48 +0200 Subject: [PATCH 172/383] Restructure index section (#500) This will hopefully remove some confusion for users around just showing off the command vs an example as well as removing examples for index providers when only one provider exists. It also updates the links to not mention `administration` as the index chapter does not live in the administration chapter. --- modules/ROOT/pages/clauses/index.adoc | 2 +- ...ions-additions-removals-compatibility.adoc | 2 +- .../pages/indexes-for-full-text-search.adoc | 18 +- .../pages/indexes-for-search-performance.adoc | 1757 +++++++---------- modules/ROOT/pages/keyword-glossary.adoc | 20 +- modules/ROOT/pages/query-tuning/indexes.adoc | 10 +- modules/ROOT/pages/syntax/spatial.adoc | 2 +- 7 files changed, 728 insertions(+), 1083 deletions(-) diff --git a/modules/ROOT/pages/clauses/index.adoc b/modules/ROOT/pages/clauses/index.adoc index c5e139ea3..2da9224b7 100644 --- a/modules/ROOT/pages/clauses/index.adoc +++ b/modules/ROOT/pages/clauses/index.adoc @@ -21,7 +21,7 @@ These comprise clauses used to manage databases, schema and security; further de m| xref::administration/databases.adoc[CREATE \| DROP \| START \| STOP DATABASE] | Create, drop, start or stop a database. -m| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE \| DROP INDEX] +m| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE \| DROP INDEX] | Create or drop an index on all nodes with a particular label and property. m| xref::constraints/syntax.adoc[CREATE \| DROP CONSTRAINT] diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 7c9df9285..15348e1c3 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -3389,7 +3389,7 @@ label:new[] DROP INDEX name ---- a| -xref:indexes-for-search-performance.adoc#administration-indexes-drop-an-index[New command] for dropping an index by name. +xref:indexes-for-search-performance.adoc#indexes-drop-an-index[New command] for dropping an index by name. a| diff --git a/modules/ROOT/pages/indexes-for-full-text-search.adoc b/modules/ROOT/pages/indexes-for-full-text-search.adoc index 9547f0b6d..07824a587 100644 --- a/modules/ROOT/pages/indexes-for-full-text-search.adoc +++ b/modules/ROOT/pages/indexes-for-full-text-search.adoc @@ -1,6 +1,6 @@ :description: This chapter describes how to use full-text indexes, to enable full-text search. -[[administration-indexes-fulltext-search]] +[[indexes-fulltext-search]] = Full-text search index [abstract] @@ -40,13 +40,13 @@ In contrast to xref::indexes-for-search-performance.adoc[other indexes], a full- * applied to more than one label. * applied to more than one relationship type. -* applied to more than one property at a time (similar to a xref::indexes-for-search-performance.adoc#administration-indexes-create-a-composite-range-index-for-nodes[_composite index_]) but with an important difference: +* applied to more than one property at a time (similar to a xref::indexes-for-search-performance.adoc#indexes-create-a-composite-range-index-for-nodes[_composite index_]) but with an important difference: While a composite index applies only to entities that match the indexed label and _all_ of the indexed properties, full-text index will index entities that have at least one of the indexed labels or relationship types, and at least one of the indexed properties. For information on how to configure full-text indexes, refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/performance/index-configuration#index-configuration-fulltext[Operations Manual -> Indexes to support full-text search]. -[[administration-indexes-fulltext-search-manage]] +[[indexes-fulltext-search-manage]] == Full-text search procedures Full-text indexes are managed through commands and used through built-in procedures, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures[Operations Manual -> Procedures] for a complete reference. @@ -89,12 +89,12 @@ Create a relationship full-text index for the given relationship types and prope | Listing all full-text indexes. | `SHOW FULLTEXT INDEXES` -| Lists all full-text indexes, see xref::indexes-for-search-performance.adoc#administration-indexes-list-indexes[the `SHOW INDEXES` command] for details. +| Lists all full-text indexes, see xref::indexes-for-search-performance.adoc#indexes-list-indexes[the `SHOW INDEXES` command] for details. |=== -[[administration-indexes-fulltext-search-create-and-configure]] +[[indexes-fulltext-search-create-and-configure]] == Create and configure full-text indexes Full-text indexes are created with the `CREATE FULLTEXT INDEX` command. @@ -247,7 +247,7 @@ Added 1 index. ====== -[[administration-indexes-fulltext-search-query]] +[[indexes-fulltext-search-query]] == Query full-text indexes Full-text indexes will, in addition to any exact matches, also return _approximate_ matches to a given query. @@ -375,7 +375,7 @@ RETURN node.title, node.description, score A complete description of the Lucene query syntax can be found in the link:https://lucene.apache.org/core/8_2_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description[Lucene documentation]. -[[administration-indexes-fulltext-search-text-array-properties]] +[[indexes-fulltext-search-text-array-properties]] == Handling of Text Array properties If the indexed property contains a text array, each element of this array is analyzed independently and all produced terms are associated with the same property name. @@ -443,10 +443,10 @@ RETURN ====== -[[administration-indexes-fulltext-search-drop]] +[[indexes-fulltext-search-drop]] == Drop full-text indexes -A full-text node index is dropped by using the xref::indexes-for-search-performance.adoc#administration-indexes-drop-an-index[same command as for other indexes], `DROP INDEX`. +A full-text node index is dropped by using the xref::indexes-for-search-performance.adoc#indexes-drop-an-index[same command as for other indexes], `DROP INDEX`. .+DROP INDEX+ diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index bcf3444c0..a3e1bbd01 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -1,6 +1,6 @@ :description: This section explains how to manage indexes used for search performance. -[[administration-indexes-search-performance]] +[[indexes-search-performance]] = Indexes for search performance [abstract] @@ -15,7 +15,7 @@ See specifically xref::query-tuning/indexes.adoc[The use of indexes] for example For information on index configuration and limitations, refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/performance/index-configuration[Operations Manual -> Index configuration]. -[[administration-indexes-types]] +[[indexes-types-and-limitations]] == Indexes (types and limitations) A database index is a redundant copy of some of the data in the database for the purpose of making searches of related data more efficient. @@ -32,9 +32,6 @@ There are multiple index types available: * Point index. * Full-text index. -// The BTREE index type was replaced in 5.0 by more specific index types (RANGE, POINT, and TEXT). -// RANGE is now the default index type for CREATE INDEX. - See xref::indexes-for-full-text-search.adoc[Full-text search index] for more information about full-text indexes. Lookup indexes contain nodes with one or more labels or relationship types, without regard for any properties. @@ -43,7 +40,7 @@ Cypher enables the creation of range indexes on one or more properties for all n * An index created on a single property for any given label or relationship type is called a _single-property index_. * An index created on more than one property for any given label or relationship type is called a _composite index_. -Differences in the usage patterns between composite and single-property indexes are described in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[]. +Differences in the usage patterns between composite and single-property indexes are described in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[]. Additionally, text and point indexes are a kind of single-property indexes, with the limitation that they only recognize properties with string and point values, respectively. Nodes or relationships with the indexed label or relationship type where the indexed property is of another value type are not included in the index. @@ -56,40 +53,39 @@ If the index is not explicitly named, it gets an auto-generated name. * Index creation is by default not idempotent, and an error will be thrown if you attempt to create the same index twice. Using the keyword `IF NOT EXISTS` makes the command idempotent, and no error will be thrown if you attempt to create the same index twice. +For a brief overview of the syntax, for all the index commands, see xref::indexes-for-search-performance.adoc#indexes-syntax[]. -[[administration-indexes-syntax]] -== Syntax +[[indexes-create-indexes]] +== +CREATE INDEX+ -[IMPORTANT] -==== -The index name must be unique among both indexes and constraints. -==== +Creating an index is done with the `+CREATE ... INDEX ...+` command. +If no index type is specified in the create command a range index will be created. -[NOTE] -==== Best practice is to give the index a name when it is created. If the index is not explicitly named, it gets an auto-generated name. -==== -[NOTE] +[IMPORTANT] ==== -The `+CREATE ... INDEX ...+` command is optionally idempotent. This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. +The index name must be unique among both indexes and constraints. +==== + +The `+CREATE INDEX+` command is optionally idempotent. This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. With `IF NOT EXISTS`, no error is thrown and nothing happens should an index with the same name or same schema and index type already exist. It may still throw an error if conflicting constraints exist, such as constraints with the same name or schema and backing index type. -==== + +Creating an index requires xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `CREATE INDEX` privilege]. [NOTE] ==== -More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. +The new index is not immediately available, but is created in the background. ==== +[[indexes-create-range-index]] +=== Creating a range index -.+Create a range index on nodes+ -[options="noheader", width="100%", cols="2, 8a"] -|=== +Creating a range index can be done with the `CREATE INDEX` command. +Note that the index name must be unique. -| Syntax -| [source, syntax, role="noheader"] ---- CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] @@ -98,24 +94,7 @@ ON (n.propertyName_1[, n.propertyName_2, ... n.propertyName_n]) -[OPTIONS "{" option: value[, ...] "}"] ---- - -| Description -| -Create a range index on nodes, either on a single property or composite. - -Index provider can be specified using the `OPTIONS` clause. - -|=== - - -.+Create a range index on relationships+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| [source, syntax, role="noheader"] ---- CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] @@ -124,93 +103,104 @@ ON (r.propertyName_1[, r.propertyName_2, ... r.propertyName_n]) -[OPTIONS "{" option: value[, ...] "}"] ---- -| Description -| -Create a range index on relationships, either on a single property or composite. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. -Index provider can be specified using the `OPTIONS` clause. +Range indexes have only one index provider available, `range-1.0`, and no supported index configuration. -|=== +[discrete] +==== Examples +* xref::indexes-for-search-performance.adoc#indexes-create-a-single-property-range-index-for-nodes[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-single-property-range-index-for-relationships[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-composite-range-index-for-nodes[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-composite-range-index-for-relationships[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-range-index-only-if-it-does-not-already-exist[] -.+Create a node label lookup index+ -[options="noheader", width="100%", cols="2, 8a"] -|=== +[discrete] +[[indexes-create-a-single-property-range-index-for-nodes]] +===== Create a single-property range index for nodes -| Syntax -| -[source, syntax, role="noheader"] +The following statement will create a named range index on all nodes labeled with `Person` and which have the `surname` property. + +.Creating a node range index on a single property +[source, cypher] ---- -CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] -FOR (n) -ON EACH labels(n) -[OPTIONS "{" option: value[, ...] "}"] +CREATE INDEX node_range_index_name FOR (n:Person) ON (n.surname) ---- -| Description -| -Create a node label lookup index. +[discrete] +[[indexes-create-a-single-property-range-index-for-relationships]] +===== Create a single-property range index for relationships -Index provider can be specified using the `OPTIONS` clause. +The following statement will create a named range index on all relationships with relationship type `KNOWS` and property `since`. -|=== +.Creating a relationship range index on a single property +[source, cypher] +---- +CREATE INDEX rel_range_index_name FOR ()-[r:KNOWS]-() ON (r.since) +---- +[discrete] +[[indexes-create-a-composite-range-index-for-nodes]] +===== Create a composite range index for nodes -.+Create a relationship type lookup index+ -[options="noheader", width="100%", cols="2, 8a"] -|=== +A range index on multiple properties is also called a composite index. +For node range indexes, only nodes with the specified label and that contain all the specified properties will be added to the index. -| Syntax -| -[source, syntax, role="noheader"] +The following statement will create a named composite range index on all nodes labeled with `Person` and which have both an `age` and `country` property. + +.Creating a composite node range index on multiple properties +[source, cypher] ---- -CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r"]"-() -ON [EACH] type(r) -[OPTIONS "{" option: value[, ...] "}"] +CREATE INDEX composite_range_node_index_name FOR (n:Person) ON (n.age, n.country) ---- -| Description -| -Create a relationship type lookup index. +[discrete] +[[indexes-create-a-composite-range-index-for-relationships]] +===== Create a composite range index for relationships -Index provider can be specified using the `OPTIONS` clause. +A range index on multiple properties is also called a composite index. +For relationship range indexes, only relationships with the specified type and that contain all the specified properties will be added to the index. -|=== +The following statement will create a named composite range index on all relationships labeled with `PURCHASED` and which have both a `date` and `amount` property. + +.Creating a composite relationship range index on multiple properties +[source, cypher] +---- +CREATE INDEX composite_range_rel_index_name FOR ()-[r:PURCHASED]-() ON (r.date, r.amount) +---- +[discrete] +[[indexes-create-a-range-index-only-if-it-does-not-already-exist]] +===== Create a range index only if it does not already exist -.+Create a text index on nodes+ -[options="noheader", width="100%", cols="2, 8a"] -|=== +If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. -| Syntax -| -[source, syntax, role="noheader"] +.Creating a range index with `IF NOT EXISTS` +[source, cypher] ---- -CREATE TEXT INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName) -ON (n.propertyName) -[OPTIONS "{" option: value[, ...] "}"] +CREATE INDEX node_range_index_name IF NOT EXISTS +FOR (n:Person) ON (n.surname) ---- -| Description -| -Create a text index on nodes where the property has a string value. - -Index provider can be specified using the `OPTIONS` clause. +The index will not be created if there already exists an index with the same schema and type, same name or both. -|=== +[[indexes-create-text-index]] +=== Creating a text index -.+Create a text index on relationships+ -[options="noheader", width="100%", cols="2, 8a"] -|=== +Creating a text index can be done with the `CREATE TEXT INDEX` command. +Note that the index name must be unique. -| Syntax -| +[source, syntax, role="noheader"] +---- +CREATE TEXT INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName) +[OPTIONS "{" option: value[, ...] "}"] +---- [source, syntax, role="noheader"] ---- CREATE TEXT INDEX [index_name] [IF NOT EXISTS] @@ -219,1324 +209,979 @@ ON (r.propertyName) [OPTIONS "{" option: value[, ...] "}"] ---- -| Description -| -Create a text index on relationships where the property has a string value. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. -Index provider can be specified using the `OPTIONS` clause. +Text indexes have two index providers available, `text-2.0` (default) and `text-1.0` (deprecated), and no supported index configuration. -|=== +[NOTE] +==== +Text indexes only recognize string values and do not support multiple properties. +==== +[discrete] +==== Examples -.+Create a point index on nodes+ -[options="noheader", width="100%", cols="2, 8a"] -|=== +* xref::indexes-for-search-performance.adoc#indexes-create-a-node-text-index[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-relationship-text-index[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-text-index-only-if-it-does-not-already-exist[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-text-index-specifying-the-index-provider[] -| Syntax -| -[source, syntax, role="noheader"] +[discrete] +[[indexes-create-a-node-text-index]] +===== Create a node text index + +The following statement will create a named text index on all nodes labeled with `Person` and which have the `nickname` string property. + +.Creating a node text index on a single property +[source, cypher] ---- -CREATE POINT INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName) -ON (n.propertyName) -[OPTIONS "{" option: value[, ...] "}"] +CREATE TEXT INDEX node_text_index_nickname FOR (n:Person) ON (n.nickname) ---- -| Description -| -Create a point index on nodes where the property has a point value. +[discrete] +[[indexes-create-a-relationship-text-index]] +===== Create a relationship text index -Index provider and configuration can be specified using the `OPTIONS` clause. +The following statement will create a named text index on all relationships with relationship type `KNOWS` and string property `interest`. -|=== +.Creating a relationship text index on a single property +[source, cypher] +---- +CREATE TEXT INDEX rel_text_index_name FOR ()-[r:KNOWS]-() ON (r.interest) +---- +[discrete] +[[indexes-create-a-text-index-only-if-it-does-not-already-exist]] +===== Create a text index only if it does not already exist -.+Create a point index on relationships+ -[options="noheader", width="100%", cols="2, 8a"] -|=== +If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. -| Syntax -| -[source, syntax, role="noheader"] +The following statement will attempt to create a named text index on all nodes labeled with `Person` and which have the `nickname` string property. + +.Creating a text index with `IF NOT EXISTS` +[source, cypher] ---- -CREATE POINT INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r:TYPE_NAME"]"-() -ON (r.propertyName) -[OPTIONS "{" option: value[, ...] "}"] +CREATE TEXT INDEX node_index_name IF NOT EXISTS FOR (n:Person) ON (n.nickname) ---- -| Description -| -Create a point index on relationships where the property has a point value. - -Index provider and configuration can be specified using the `OPTIONS` clause. - -|=== +Note that the index will not be created if there already exists an index with the same schema and type, same name or both. +[discrete] +[[indexes-create-a-text-index-specifying-the-index-provider]] +===== Create a text index specifying the index provider -.+Drop an index+ -[options="noheader", width="100%", cols="2, 8a"] -|=== +To create a text index with a specific index provider, the `OPTIONS` clause is used. +The valid values for the index provider are `text-2.0` and `text-1.0` (deprecated). +The default provider is `text-2.0`. -| Syntax -| -[source, syntax, role="noheader"] +.Creating a text index with index provider +[source, cypher] ---- -DROP INDEX index_name [IF EXISTS] +CREATE TEXT INDEX text_index_with_indexprovider FOR ()-[r:TYPE]-() ON (r.prop1) +OPTIONS {indexProvider: 'text-2.0'} ---- -| Description -| Drop an index of any index type. +There is no supported index configuration for text indexes. -| Note -| -The command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. -With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. -|=== +[[indexes-create-point-index]] +=== Creating a point index -.List indexes -[options="noheader", width="100%", cols="2, 8a"] -|=== +Creating a point index can be done with the `CREATE POINT INDEX` command. +Note that the index name must be unique. -| Syntax -| [source, syntax, role="noheader"] ---- -SHOW [ALL \| FULLTEXT \| LOOKUP \| POINT \| RANGE \| TEXT] INDEX[ES] - [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] - [WHERE expression] - [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] +CREATE POINT INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName) +[OPTIONS "{" option: value[, ...] "}"] +---- +[source, syntax, role="noheader"] +---- +CREATE POINT INDEX [index_name] [IF NOT EXISTS] +FOR ()-"["r:TYPE_NAME"]"-() +ON (r.propertyName) +[OPTIONS "{" option: value[, ...] "}"] ---- -| Description -| List indexes in the database, either all or filtered on index type. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. -| Note -| When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. +Point indexes have supported index configuration, see the last examples, but only one index provider available, `point-1.0`. -|=== +[NOTE] +==== +Note that point indexes only recognize point values and do not support multiple properties. +==== +[discrete] +==== Examples -Creating an index requires xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `CREATE INDEX` privilege], -while dropping an index requires xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `DROP INDEX` privilege] and -listing indexes require xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `SHOW INDEX` privilege]. +* xref::indexes-for-search-performance.adoc#indexes-create-a-node-point-index[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-relationship-point-index[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-point-index-only-if-it-does-not-already-exist[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-point-index-specifying-the-index-configuration[] -xref::query-tuning/using.adoc[Planner hints and the USING keyword] describes how to make the Cypher planner use specific indexes (especially in cases where the planner would not necessarily have used them). +[discrete] +[[indexes-create-a-node-point-index]] +===== Create a node point index +The following statement will create a named point index on all nodes labeled with `Person` and which have the `sublocation` point property. -[[administration-indexes-single-vs-composite-index]] -== Composite index limitations +.Creating a node point index on a single property +[source, cypher] +---- +CREATE POINT INDEX node_point_index_name FOR (n:Person) ON (n.sublocation) +---- -Like single-property range indexes, composite range indexes support all predicates: +[discrete] +[[indexes-create-a-relationship-point-index]] +===== Create a relationship point index -* equality check: `n.prop = value` -* list membership check: `n.prop IN list` -* existence check: `n.prop IS NOT NULL` -* range search: `n.prop > value` -* prefix search: `STARTS WITH` +The following statement will create a named point index on all relationships with relationship type `STREET` and point property `intersection`. -[NOTE] -==== -For details about each operator, see xref::syntax/operators.adoc[Operators]. -==== - -However, predicates might be planned as existence check and a filter. -For most predicates, this can be avoided by following these restrictions: - -* If there is any `equality check` and `list membership check` predicates, -they need to be for the first properties defined by the index. -* There can be up to one `range search` or `prefix search` predicate. -* There can be any number of `existence check` predicates. -* Any predicate after a `range search`, `prefix search` or `existence check` predicate has to be an `existence check` predicate. - -[NOTE] -==== -The `suffix search` (`ENDS WITH`) and `substring search` (`CONTAINS`) predicates can utilize the index as well. -However, they are always planned as an existence check and a filter and any predicates following after will therefore also be planned as such. -==== - -For example, an index on nodes with `:Label(prop1,prop2,prop3,prop4,prop5,prop6)` and predicates: - -[source, cypher, role=test-skip] +.Creating a relationship point index on a single property +[source, cypher] ---- -WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 < 'e' AND n.prop5 = true AND n.prop6 IS NOT NULL +CREATE POINT INDEX rel_point_index_name FOR ()-[r:STREET]-() ON (r.intersection) ---- -will be planned as: +[discrete] +[[indexes-create-a-point-index-only-if-it-does-not-already-exist]] +===== Create a point index only if it does not already exist -[source, cypher, role=test-skip] +If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. + +.Creating a point index with `IF NOT EXISTS` +[source, cypher] ---- -WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 IS NOT NULL AND n.prop5 IS NOT NULL AND n.prop6 IS NOT NULL +CREATE POINT INDEX node_point_index IF NOT EXISTS +FOR (n:Person) ON (n.sublocation) ---- -with filters on `n.prop4 < 'e'` and `n.prop5 = true`, since `n.prop3` has a `range search` predicate. - -And an index on nodes with `:Label(prop1,prop2)` with predicates: +Note that the index will not be created if there already exists an index with the same schema and type, same name or both. -[source, cypher, role=test-skip] ----- -WHERE n.prop1 ENDS WITH 'x' AND n.prop2 = false ----- +[discrete] +[[indexes-create-a-point-index-specifying-the-index-configuration]] +===== Create a point index specifying the index configuration -will be planned as: +To create a point index with a specific index configuration, the `OPTIONS` clause is used. -[source, cypher, role=test-skip] ----- -WHERE n.prop1 IS NOT NULL AND n.prop2 IS NOT NULL ----- +The valid configuration settings are: -with filters on `n.prop1 ENDS WITH 'x'` and `n.prop2 = false`, since `n.prop1` has a `suffix search` predicate. +* `spatial.cartesian.min` +* `spatial.cartesian.max` +* `spatial.cartesian-3d.min` +* `spatial.cartesian-3d.max` +* `spatial.wgs-84.min` +* `spatial.wgs-84.max` +* `spatial.wgs-84-3d.min` +* `spatial.wgs-84-3d.max` -Composite indexes require predicates on all properties indexed. -If there are predicates on only a subset of the indexed properties, it will not be possible to use the composite index. -To get this kind of fallback behavior, it is necessary to create additional indexes on the relevant sub-set of properties or on single properties. +Non-specified settings have their respective default values. +The following statement will create a point index specifying the `spatial.cartesian.min` and `spatial.cartesian.max` settings. -[[administration-indexes-examples]] -== +CREATE INDEX+ +.Creating a point index with index configuration +[source, cypher] +---- +CREATE POINT INDEX point_index_with_config +FOR (n:Label) ON (n.prop2) +OPTIONS { + indexConfig: { + `spatial.cartesian.min`: [-100.0, -100.0], + `spatial.cartesian.max`: [100.0, 100.0] + } +} +---- -*Examples:* - -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-single-property-range-index-for-nodes[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-single-property-range-index-for-relationships[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-range-index-only-if-it-does-not-already-exist[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-range-index-specifying-the-index-provider[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-composite-range-index-for-nodes[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-composite-range-index-for-relationships[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-node-label-lookup-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-relationship-type-lookup-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-token-lookup-index-specifying-the-index-provider[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-node-point-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-relationship-point-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-point-index-only-if-it-does-not-already-exist[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-point-index-specifying-the-index-provider[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-point-index-specifying-the-index-configuration[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-point-index-specifying-both-the-index-provider-and-configuration[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-node-text-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-relationship-text-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-text-index-only-if-it-does-not-already-exist[] -* xref::indexes-for-search-performance.adoc#administration-indexes-create-a-text-index-specifying-the-index-provider[] -* xref::indexes-for-search-performance.adoc#administration-indexes-failure-to-create-an-already-existing-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-failure-to-create-an-index-when-a-constraint-already-exists[] -* xref::indexes-for-search-performance.adoc#administration-indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-constraint[] +Specifying the index configuration can be combined with specifying index provider. +Though only one valid value exists for the index provider, `point-1.0`, which is the default value. -[discrete] -[[administration-indexes-create-a-single-property-range-index-for-nodes]] -=== Create a single-property range index for nodes +[[indexes-create-token-index]] +=== Creating a token lookup index -A named range index on a single property for all nodes with a particular label can be created with: +Creating a token lookup index (node label or relationship type lookup index) can be done with the `CREATE LOOKUP INDEX` command. +Note that the index name must be unique. [source, syntax, role="noheader"] ---- -CREATE INDEX index_name FOR (n:Label) ON (n.property) +CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] +FOR (n) +ON EACH labels(n) ---- - -Note that the index is not immediately available, but is created in the background. - - -.+CREATE INDEX+ -====== - -.Query -[source, cypher] +[source, syntax, role="noheader"] ---- -CREATE INDEX node_range_index_name FOR (n:Person) ON (n.surname) +CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] +FOR ()-"["r"]"-() +ON [EACH] type(r) ---- -[NOTE] -==== -The index name must be unique. -==== +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. -.Result -[queryresult] ----- -Added 1 index. ----- +Token lookup indexes have only one index provider available, `token-lookup-1.0`, and no supported index configuration. -====== +[discrete] +==== Examples +* xref::indexes-for-search-performance.adoc#indexes-create-a-node-label-lookup-index[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-relationship-type-lookup-index[] +* xref::indexes-for-search-performance.adoc#indexes-create-a-lookup-index-only-if-it-does-not-already-exist[] [discrete] -[[administration-indexes-create-a-single-property-range-index-for-relationships]] -=== Create a single-property range index for relationships +[[indexes-create-a-node-label-lookup-index]] +===== Create a node label lookup index -A named range index on a single property for all relationships with a particular relationship type can be created with: +The following statement will create a named node label lookup index on all nodes with one or more labels: -[source, syntax, role="noheader"] +// Lookup indexes exist by default, recreating them would raise an error +.Creating a node label lookup index +[source, cypher, role=test-skip] ---- -CREATE INDEX index_name FOR ()-[r:TYPE]-() ON (r.property) +CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) ---- -Note that the index is not immediately available, but is created in the background. +[NOTE] +==== +Only one node label lookup index can exist at a time. +==== +[discrete] +[[indexes-create-a-relationship-type-lookup-index]] +===== Create a relationship type lookup index -.+CREATE INDEX+ -====== +The following statement will create a named relationship type lookup index on all relationships with any relationship type. -.Query -[source, cypher, indent=0] +// Lookup indexes exist by default, recreating them would raise an error +.Creating a relationship type lookup index +[source, cypher, role=test-skip] ---- -CREATE INDEX rel_range_index_name FOR ()-[r:KNOWS]-() ON (r.since) +CREATE LOOKUP INDEX rel_type_lookup_index FOR ()-[r]-() ON EACH type(r) ---- [NOTE] ==== -The index name must be unique. +Only one relationship type lookup index can exist at a time. ==== -.Result -[queryresult] ----- -Added 1 index. ----- - -====== - - [discrete] -[[administration-indexes-create-a-range-index-only-if-it-does-not-already-exist]] -=== Create a range index only if it does not already exist +[[indexes-create-a-lookup-index-only-if-it-does-not-already-exist]] +===== Create a token lookup index only if it does not already exist If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. - -.+CREATE RANGE INDEX+ -====== - -.Query +.Creating a node label lookup index with `IF NOT EXISTS` [source, cypher] ---- -CREATE INDEX node_range_index_name IF NOT EXISTS -FOR (n:Person) ON (n.surname) +CREATE LOOKUP INDEX node_label_lookup IF NOT EXISTS FOR (n) ON EACH labels(n) ---- -[NOTE] -==== The index will not be created if there already exists an index with the same schema and type, same name or both. -==== -.Result -[queryresult] ----- -(no changes, no records) ----- -====== +[[indexes-create-conflicting-index]] +=== Creating an index when a conflicting index or constraint exists +* xref::indexes-for-search-performance.adoc#indexes-failure-to-create-an-already-existing-index[] +* xref::indexes-for-search-performance.adoc#indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-index[] +* xref::indexes-for-search-performance.adoc#indexes-failure-to-create-an-index-when-a-constraint-already-exists[] +* xref::indexes-for-search-performance.adoc#indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-constraint[] [discrete] -[[administration-indexes-create-a-range-index-specifying-the-index-provider]] -=== Create a range index specifying the index provider - -To create a range index with a specific index provider, the `OPTIONS` clause is used. -Only one valid value exists for the index provider, `range-1.0`, which is the default value. +[[indexes-failure-to-create-an-already-existing-index]] +==== Failure to create an already existing index +Create an index on the property `title` on nodes with the `Book` label, when that index already exists. -.+CREATE INDEX+ -====== - -.Query -[source, cypher, indent=0] +//// +[source, cypher, role=test-setup] ---- -CREATE INDEX range_index_with_provider -FOR ()-[r:TYPE]-() ON (r.prop1) -OPTIONS { - indexProvider: 'range-1.0' -} +CREATE INDEX example_index FOR (n:Book) ON (n.title) ---- +//// -.Result -[queryresult] +.Creating a duplicated index +[source, cypher, role=test-fail] ---- -Added 1 index. +CREATE INDEX bookTitleIndex FOR (book:Book) ON (book.title) ---- -====== - -There is no supported index configuration for range indexes. +In this case the index can not be created because it already exists. +.Error message +[source, error] +---- +There already exists an index (:Book {title}). +---- [discrete] -[[administration-indexes-create-a-composite-range-index-for-nodes]] -=== Create a composite range index for nodes +[[indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-index]] +==== Failure to create an index with the same name as an already existing index -A named range index on multiple properties for all nodes with a particular label -- i.e. a composite index -- can be created with: +Create a named index on the property `numberOfPages` on nodes with the `Book` label, when an index with the given name already exists. +The index type of the existing index does not matter. -[source, syntax, role="noheader"] +//// +[source, cypher, role=test-setup] ---- -CREATE INDEX index_name FOR (n:Label) ON (n.prop1, ..., n.propN) +CREATE TEXT INDEX indexOnBooks FOR (b:Label1) ON (b.prop1) ---- +//// -Only nodes with the specified label and that contain all the properties in the index definition will be added to the index. -Note that the composite index is not immediately available, but is created in the background. - - -.+CREATE INDEX+ -====== - -The following statement will create a named composite range index on all nodes labeled with `Person` and which have both an `age` and `country` property: - -.Query -[source, cypher] +.Creating an index with a duplicated name +[source, cypher, role=test-fail] ---- -CREATE INDEX composite_range_node_index_name FOR (n:Person) ON (n.age, n.country) +CREATE INDEX indexOnBooks FOR (book:Book) ON (book.numberOfPages) ---- -[NOTE] -==== -The index name must be unique. -==== +In this case the index can't be created because there already exists an index with the given name. -.Result -[queryresult] +.Error message +[source, error] ---- -Added 1 index. +There already exists an index called 'indexOnBooks'. ---- -====== - - [discrete] -[[administration-indexes-create-a-composite-range-index-for-relationships]] -=== Create a composite range index for relationships +[[indexes-failure-to-create-an-index-when-a-constraint-already-exists]] +==== Failure to create an index when a constraint already exists -A named range index on multiple properties for all relationships with a particular relationship type -- i.e. a composite index -- can be created with: +Create an index on the property `isbn` on nodes with the `Book` label, when an index-backed constraint already exists on that schema. +This is only relevant for range indexes. -[source, syntax, role="noheader"] +//// +[source, cypher, role=test-setup] ---- -CREATE INDEX index_name FOR ()-[r:TYPE]-() ON (r.prop1, ..., r.propN) +CREATE CONSTRAINT uniqueBookIsbn FOR (book:Book) REQUIRE (book.isbn) IS UNIQUE ---- +//// -Only relationships with the specified type and that contain all the properties in the index definition will be added to the index. -Note that the composite index is not immediately available, but is created in the background. - - -.+CREATE INDEX+ -====== - -The following statement will create a named composite range index on all relationships labeled with `PURCHASED` and which have both a `date` and `amount` property: - -.Query -[source, cypher] +.Creating a range index on same schema as existing index-backed constraint +[source, cypher, role=test-fail] ---- -CREATE INDEX composite_range_rel_index_name FOR ()-[r:PURCHASED]-() ON (r.date, r.amount) +CREATE INDEX bookIsbnIndex FOR (book:Book) ON (book.isbn) ---- -[NOTE] -==== -The index name must be unique. -==== +In this case the index can not be created because an index-backed constraint already exists on that label and property combination. -.Result -[queryresult] +.Error message +[source, error] ---- -Added 1 index. +There is a uniqueness constraint on (:Book {isbn}), so an index is already created that matches this. ---- -====== - - [discrete] -[[administration-indexes-create-a-node-label-lookup-index]] -=== Create a node label lookup index +[[indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-constraint]] +==== Failure to create an index with the same name as an already existing constraint -A named node label lookup index for all nodes with one or more labels can be created with: +Create a named index on the property `numberOfPages` on nodes with the `Book` label, when a constraint with the given name already exists. -[source, syntax, role="noheader"] +//// +[source, cypher, role=test-setup] ---- -CREATE LOOKUP INDEX index_name FOR (n) ON EACH labels(n) +CREATE CONSTRAINT bookRecommendations FOR (book:Book) REQUIRE (book.recommend) IS NOT NULL ---- +//// -[NOTE] -==== -The index is not immediately available, but is created in the background. -==== - - -.+CREATE LOOKUP INDEX+ -====== - -// Lookup indexes exist by default, recreating them would raise an error -.Query -[source, cypher, role=test-skip] +.Creating an index with same name as an existing constraint +[source, cypher, role=test-fail] ---- -CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) +CREATE INDEX bookRecommendations FOR (book:Book) ON (book.recommendations) ---- -[NOTE] -==== -Note that a node label lookup index can only be created once and that the index name must be unique. -==== +In this case the index can not be created because there already exists a constraint with the given name. -.Result -[queryresult] +.Error message +[source, error] ---- -Added 1 index. +There already exists a constraint called 'bookRecommendations'. ---- -====== - -[NOTE] -==== -Only one node label lookup index can exist at the time. -==== -[discrete] -[[administration-indexes-create-a-relationship-type-lookup-index]] -=== Create a relationship type lookup index +[[indexes-list-indexes]] +== +SHOW INDEXES+ -A named relationship type lookup index for all relationships with any relationship type can be created with: +Listing indexes can be done with `SHOW INDEXES`. [source, syntax, role="noheader"] ---- -CREATE LOOKUP INDEX index_name FOR ()-[r]-() ON EACH type(r) +SHOW [ALL \| FULLTEXT \| LOOKUP \| POINT \| RANGE \| TEXT] INDEX[ES] + [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] + [WHERE expression] + [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- -[NOTE] -==== -The index is not immediately available, but is created in the background. -==== - -.+CREATE LOOKUP INDEX+ -====== +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. -// Lookup indexes exist by default, recreating them would raise an error -.Query -[source, cypher, role=test-skip] ----- -CREATE LOOKUP INDEX rel_type_lookup_index FOR ()-[r]-() ON EACH type(r) ----- +This command will produce a table with the following columns: -[NOTE] -==== -Note that a relationship type lookup index can only be created once and that the index name must be unique. -==== +.List indexes output +[options="header", cols="4,6,2"] +|=== +| Column | Description | Type -.Result -[queryresult] ----- -Added 1 index. ----- +| `id` +| The id of the index. label:default-output[] +| `INTEGER` -====== +| `name` +| Name of the index (explicitly set by the user or automatically assigned). label:default-output[] +| `STRING` -[NOTE] -==== -Only one relationship type lookup index can exist at the time. -==== - -// do we want an IF NOT EXISTS example for LOOKUP indexes? - -[discrete] -[[administration-indexes-create-a-token-lookup-index-specifying-the-index-provider]] -=== Create a token lookup index specifying the index provider - -Token lookup indexes (node label and relationship type lookup indexes) allow setting the index provider using the `OPTIONS` clause. -Only one valid value exists for the index provider, `token-lookup-1.0`, which is the default value. - -// Cypher only has the keyword LOOKUP why is the option named `token-lookup` ??? -// -> the name `token-lookup` came from kernel but wasn't added as part of the cypher commands, -// because there is currently only one type of lookup indexes which are token lookup ones. -// The name `token` is a collective word for both node label and relationship type, -// hence the `node label lookup index` and `relationship type lookup index` variations above. - - -.+CREATE LOOKUP INDEX+ -====== - -// Lookup indexes exist by default, recreating them would raise an error -.Query -[source, cypher, role=test-skip] ----- -CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) -OPTIONS {indexProvider: 'token-lookup-1.0'} ----- - -[NOTE] -==== -Note that the above command will fail if any node label lookup index already exists. -==== +| `state` +| Current state of the index. label:default-output[] +| `STRING` -.Result -[queryresult] ----- -Added 1 index. ----- +| `populationPercent` +| % of index population. label:default-output[] +| `FLOAT` -====== +| `type` +| The IndexType of this index (`FULLTEXT`, `LOOKUP`, `POINT`, `RANGE`, or `TEXT`). label:default-output[] +| `STRING` -There is no supported index configuration for token lookup indexes. +| `entityType` +| Type of entities this index represents (nodes or relationship). label:default-output[] +| `STRING` +| `labelsOrTypes` +| The labels or relationship types of this index. label:default-output[] +| `LIST OF STRING` -[discrete] -[[administration-indexes-create-a-node-point-index]] -=== Create a node point index +| `properties` +| The properties of this index. label:default-output[] +| `LIST OF STRING` -A named point index on a single property for all nodes with a particular label can be created with: +| `indexProvider` +| The index provider for this index. label:default-output[] +| `STRING` -[source, syntax, role="noheader"] ----- -CREATE POINT INDEX index_name FOR (n:Label) ON (n.property) ----- +// New in 5.0 +| `owningConstraint` +| The name of the constraint the index is associated with or `null` if the index is not associated with any constraint. label:default-output[] +| `STRING` -Note that the index is not immediately available, but is created in the background. +| `options` +| The options passed to `CREATE` command. +| `MAP` +| `failureMessage` +| The failure description of a failed index. +| `STRING` -.+CREATE POINT INDEX+ -====== +| `createStatement` +| Statement used to create the index. +| `STRING` -.Query -[source, cypher] ----- -CREATE POINT INDEX node_index_name FOR (n:Person) ON (n.sublocation) ----- +|=== [NOTE] ==== -The index name must be unique. +The command `SHOW INDEXES` returns only the default output. +For a full output use the optional `YIELD` command. +Full output: `+SHOW INDEXES YIELD *+`. ==== -.Result -[queryresult] ----- -Added 1 index. ----- - -====== +Listing indexes also allows for `WHERE` and `YIELD` clauses to filter the returned rows and columns. -[NOTE] -==== -Note that point indexes only recognize point values and do not support multiple properties. -==== +Listing indexes require xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `SHOW INDEX` privilege]. [discrete] -[[administration-indexes-create-a-relationship-point-index]] -=== Create a relationship point index +=== Examples -A named point index on a single property for all relationships with a particular relationship type can be created with: - -[source, syntax] ----- -CREATE POINT INDEX index_name FOR ()-[r:TYPE]-() ON (r.property) ----- +* xref::indexes-for-search-performance.adoc#indexes-listing-all-indexes[] +* xref::indexes-for-search-performance.adoc#indexes-listing-indexes-with-filtering[] -Note that the index is not immediately available, but is created in the background. +[discrete] +[[indexes-listing-all-indexes]] +==== Listing all indexes -.+CREATE POINT INDEX+ -====== +To list all indexes with the default output columns, the `SHOW INDEXES` command can be used. +If all columns are required, use `SHOW INDEXES YIELD *`. -.Query -[source, cypher] +.Showing all indexes +[source, cypher, role=test-result-skip] ---- -CREATE POINT INDEX rel_index_name FOR ()-[r:STREET]-() ON (r.intersection) +SHOW INDEXES ---- -[NOTE] -==== -The index name must be unique. -==== +// SHOW INDEXES default outputs +// 4.4: id, name, state, populationPercent, uniqueness, type, entityType, labelsOrTypes, properties, indexProvider +// 5.0: id, name, state, populationPercent, type, entityType, labelsOrTypes, properties, indexProvider, owningConstraint .Result [queryresult] ---- -Added 1 index. ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | +| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | +| 13 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | +| 14 | "indexOnBooks" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Label1"] | ["prop1"] | "text-2.0" | NULL | +| 11 | "node_label_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "NODE" | NULL | NULL | "token-lookup-1.0" | NULL | +| 8 | "node_point_index_name" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Person"] | ["sublocation"] | "point-1.0" | NULL | +| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | +| 5 | "node_text_index_nickname" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["nickname"] | "text-2.0" | NULL | +| 10 | "point_index_with_config" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop2"] | "point-1.0" | NULL | +| 9 | "rel_point_index_name" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["intersection"] | "point-1.0" | NULL | +| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | +| 6 | "rel_text_index_name" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["KNOWS"] | ["interest"] | "text-2.0" | NULL | +| 12 | "rel_type_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "RELATIONSHIP" | NULL | NULL | "token-lookup-1.0" | NULL | +| 7 | "text_index_with_indexprovider" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "text-2.0" | NULL | +| 15 | "uniqueBookIsbn" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["isbn"] | "range-1.0" | "uniqueBookIsbn" | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +15 rows ---- -====== +One of the output columns from `SHOW INDEXES` is the name of the index. +This can be used to drop the index with the xref::indexes-for-search-performance.adoc#indexes-drop-an-index[`DROP INDEX` command]. -[NOTE] -==== -Note that point indexes only recognize point values and do not support multiple properties. -==== -[[administration-indexes-create-a-point-index-only-if-it-does-not-already-exist]] -=== Create a point index only if it does not already exist +[discrete] +[[indexes-listing-indexes-with-filtering]] +==== Listing indexes with filtering -If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. +One way of filtering the output from `SHOW INDEXES` by index type is the use of type keywords, listed in the syntax description. +For example, to show only range indexes, use `SHOW RANGE INDEXES`. -.+CREATE POINT INDEX+ -====== +Another more flexible way of filtering the output is to use the `WHERE` clause. +An example is to only show indexes not belonging to constraints. -.Query -[source, cypher] +To show only range indexes that does not belong to a constraint we can combine the filtering versions. + +.Showing range indexes +[source, cypher, role=test-result-skip] ---- -CREATE POINT INDEX node_index_name IF NOT EXISTS -FOR (n:Person) ON (n.sublocation) +SHOW RANGE INDEXES WHERE owningConstraint IS NULL ---- -[NOTE] -==== -Note that the index will not be created if there already exists an index with the same schema and type, same name or both. -==== - .Result [queryresult] ---- -(no changes, no records) ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | +| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | +| 13 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | +| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | +| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +5 rows ---- -====== +This will only return the default output columns. +To get all columns, use: -[discrete] -[[administration-indexes-create-a-point-index-specifying-the-index-provider]] -=== Create a point index specifying the index provider +[source, syntax, role="noheader"] +---- +SHOW RANGE INDEXES YIELD * WHERE owningConstraint IS NULL +---- -To create a point index with a specific index provider, the `OPTIONS` clause is used. -Only one valid value exists for the index provider, `point-1.0`, which is the default value. -.+CREATE POINT INDEX+ -====== +[[indexes-drop-indexes]] +== +DROP INDEX+ -.Query -[source, cypher] ----- -CREATE POINT INDEX index_with_provider -FOR (n:Label) ON (n.prop1) -OPTIONS { - indexProvider: 'point-1.0' -} ----- +An index can be dropped (removed) using the name with the `DROP INDEX index_name` command. +This command can drop indexes of any type, except those backing constraints. +The name of the index can be found using the xref::indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES` command], given in the output column `name`. -.Result -[queryresult] +[source, syntax, role="noheader"] ---- -Added 1 index. +DROP INDEX index_name [IF EXISTS] ---- -====== +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. -Specifying the index provider can be combined with specifying index configuration. +The `DROP INDEX` command is optionally idempotent. +This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. +With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. +Dropping an index requires xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `DROP INDEX` privilege]. [discrete] -[[administration-indexes-create-a-point-index-specifying-the-index-configuration]] -=== Create a point index specifying the index configuration - -To create a point index with a specific index configuration, the `OPTIONS` clause is used. - -The valid configuration settings are: - -* `spatial.cartesian.min` -* `spatial.cartesian.max` -* `spatial.cartesian-3d.min` -* `spatial.cartesian-3d.max` -* `spatial.wgs-84.min` -* `spatial.wgs-84.max` -* `spatial.wgs-84-3d.min` -* `spatial.wgs-84-3d.max` +=== Examples -Non-specified settings have their respective default values. +* xref::indexes-for-search-performance.adoc#indexes-drop-an-index[] +* xref::indexes-for-search-performance.adoc#indexes-drop-a-non-existing-index[] -.+CREATE POINT INDEX+ -====== +[discrete] +[[indexes-drop-an-index]] +==== Drop an index -.Query -[source, cypher, indent=0] ----- -CREATE POINT INDEX index_with_config -FOR (n:Label) ON (n.prop2) -OPTIONS { - indexConfig: { - `spatial.cartesian.min`: [-100.0, -100.0], - `spatial.cartesian.max`: [100.0, 100.0] - } -} ----- +The following statement will attempt to drop the index named `example_index`. -.Result -[queryresult] +.Dropping an index +[source, cypher] ---- -Added 1 index. +DROP INDEX example_index ---- -====== - -Specifying the index configuration can be combined with specifying index provider. +If an index with that name exists it is removed, if not the command fails. [discrete] -[[administration-indexes-create-a-point-index-specifying-both-the-index-provider-and-configuration]] -=== Create a point index specifying both the index provider and configuration - -To create a point index with a specific index provider and configuration, the `OPTIONS` clause is used. -Only one valid value exists for the index provider, `point-1.0`, which is the default value. - -The valid configuration settings are: - -* `spatial.cartesian.min` -* `spatial.cartesian.max` -* `spatial.cartesian-3d.min` -* `spatial.cartesian-3d.max` -* `spatial.wgs-84.min` -* `spatial.wgs-84.max` -* `spatial.wgs-84-3d.min` -* `spatial.wgs-84-3d.max` +[[indexes-drop-a-non-existing-index]] +==== Drop a non-existing index -Non-specified settings have their respective default values. +If it is uncertain if an index exists and you want to drop it if it does but not get an error should it not, use `IF EXISTS`. -.+CREATE POINT INDEX+ -====== +The following statement will attempt to drop the index named `missing_index_name`. -.Query +.Dropping an index with `IF EXISTS` [source, cypher] ---- -CREATE POINT INDEX index_with_options -FOR ()-[r:TYPE]-() ON (r.prop1) -OPTIONS { - indexProvider: 'point-1.0', - indexConfig: { - `spatial.wgs-84.min`: [-100.0, -80.0], - `spatial.wgs-84.max`: [100.0, 80.0] - } -} ----- - -.Result -[queryresult] ----- -Added 1 index. +DROP INDEX missing_index_name IF EXISTS ---- -====== - -Index provider and configuration can also be specified separately. - +If an index with that name exists it is removed, if not the command does nothing. -[discrete] -[[administration-indexes-create-a-node-text-index]] -=== Create a node text index -A named text index on a single property for all nodes with a particular label can be created with: +[[indexes-syntax]] +== Syntax -[source, syntax, role="noheader"] ----- -CREATE TEXT INDEX index_name FOR (n:Label) ON (n.property) ----- +[IMPORTANT] +==== +The index name must be unique among both indexes and constraints. +==== [NOTE] ==== -The index is not immediately available, but is created in the background. +Best practice is to give the index a name when it is created. +If the index is not explicitly named, it gets an auto-generated name. ==== -.+CREATE TEXT INDEX+ -====== - -.Query -[source, cypher, indent=0] ----- -CREATE TEXT INDEX node_index_nickname FOR (n:Person) ON (n.nickname) ----- - [NOTE] ==== -The index name must be unique. +The `+CREATE ... INDEX ...+` command is optionally idempotent. This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. +With `IF NOT EXISTS`, no error is thrown and nothing happens should an index with the same name or same schema and index type already exist. +It may still throw an error if conflicting constraints exist, such as constraints with the same name or schema and backing index type. ==== -.Result -[queryresult] ----- -Added 1 index. ----- - -====== - [NOTE] ==== -Text indexes only recognize string values and do not support multiple properties. +More details about the syntax descriptions can be found xref:administration/index.adoc#administration-syntax[here]. ==== -[discrete] -[[administration-indexes-create-a-relationship-text-index]] -=== Create a relationship text index -A named text index on a single property for all relationships with a particular relationship type can be created with: +.+Create a range index on nodes+ +[options="noheader", width="100%", cols="2, 8a"] +|=== +| Syntax +| [source, syntax, role="noheader"] ---- -CREATE TEXT INDEX index_name FOR ()-[r:TYPE]-() ON (r.property) +CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName_1[, + n.propertyName_2, + ... + n.propertyName_n]) +[OPTIONS "{" option: value[, ...] "}"] ---- -[NOTE] -==== -The index is not immediately available, but is created in the background. -==== - -.+CREATE TEXT INDEX+ -====== +| Description +| +Create a range index on nodes, either on a single property or composite. -.Query -[source, cypher] ----- -CREATE TEXT INDEX relprop_index_name FOR ()-[r:KNOWS]-() ON (r.interest) ----- +Index provider can be specified using the `OPTIONS` clause. +There is only one available index provider for this index. -[NOTE] -==== -The index name must be unique. -==== +|=== -.Result -[queryresult] ----- -Added 1 index. ----- -====== +.+Create a range index on relationships+ +[options="noheader", width="100%", cols="2, 8a"] +|=== -[NOTE] -==== -Note that text indexes only recognize string values and do not support multiple properties. -==== - -[discrete] -[[administration-indexes-create-a-text-index-only-if-it-does-not-already-exist]] -=== Create a text index only if it does not already exist - -If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. - - -.+CREATE TEXT INDEX+ -====== - -.Query -[source, cypher] ----- -CREATE TEXT INDEX node_index_name IF NOT EXISTS FOR (n:Person) ON (n.nickname) ----- - -[NOTE] -==== -Note that the index will not be created if there already exists an index with the same schema and type, same name or both. -==== - -.Result -[queryresult] +| Syntax +| +[source, syntax, role="noheader"] ---- -(no changes, no records) +CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] +FOR ()-"["r:TYPE_NAME"]"-() +ON (r.propertyName_1[, + r.propertyName_2, + ... + r.propertyName_n]) +[OPTIONS "{" option: value[, ...] "}"] ---- -====== - +| Description +| +Create a range index on relationships, either on a single property or composite. -[discrete] -[[administration-indexes-create-a-text-index-specifying-the-index-provider]] -=== Create a text index specifying the index provider +Index provider can be specified using the `OPTIONS` clause. +There is only one available index provider for this index. -To create a text index with a specific index provider, the `OPTIONS` clause is used. -The valid values for the index provider are `text-2.0` and `text-1.0` (deprecated). The default provider is `text-2.0`. +|=== -.+CREATE TEXT INDEX+ -====== -.Query -[source, cypher] ----- -CREATE TEXT INDEX index_with_indexprovider FOR ()-[r:TYPE]-() ON (r.prop1) -OPTIONS {indexProvider: 'text-2.0'} ----- +.+Create a text index on nodes+ +[options="noheader", width="100%", cols="2, 8a"] +|=== -.Result -[queryresult] +| Syntax +| +[source, syntax, role="noheader"] ---- -Added 1 index. +CREATE TEXT INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName) +[OPTIONS "{" option: value[, ...] "}"] ---- -====== - -There is no supported index configuration for text indexes. +| Description +| +Create a text index on nodes where the property has a string value. -[discrete] -[[administration-indexes-failure-to-create-an-already-existing-index]] -=== Failure to create an already existing index +Index provider can be specified using the `OPTIONS` clause. -Create an index on the property `title` on nodes with the `Book` label, when that index already exists. +|=== -.+CREATE RANGE INDEX+ -====== -//// -[source, cypher, role=test-setup] ----- -CREATE INDEX example_index FOR (n:Book) ON (n.title) ----- -//// +.+Create a text index on relationships+ +[options="noheader", width="100%", cols="2, 8a"] +|=== -.Query -[source, cypher, role=test-fail] +| Syntax +| +[source, syntax, role="noheader"] ---- -CREATE INDEX bookTitleIndex FOR (book:Book) ON (book.title) +CREATE TEXT INDEX [index_name] [IF NOT EXISTS] +FOR ()-"["r:TYPE_NAME"]"-() +ON (r.propertyName) +[OPTIONS "{" option: value[, ...] "}"] ---- -In this case the index can not be created because it already exists. +| Description +| +Create a text index on relationships where the property has a string value. -.Error message -[source, error] ----- -There already exists an index (:Book {title}). ----- +Index provider can be specified using the `OPTIONS` clause. -====== +|=== -[discrete] -[[administration-indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-index]] -=== Failure to create an index with the same name as an already existing index +.+Create a point index on nodes+ +[options="noheader", width="100%", cols="2, 8a"] +|=== -Create a named index on the property `numberOfPages` on nodes with the `Book` label, when an index with the given name already exists. +| Syntax +| +[source, syntax, role="noheader"] +---- +CREATE POINT INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName) +[OPTIONS "{" option: value[, ...] "}"] +---- +| Description +| +Create a point index on nodes where the property has a point value. -.+CREATE RANGE INDEX+ -====== +Index provider and configuration can be specified using the `OPTIONS` clause. +There is only one available index provider for this index. -//// -[source, cypher, role=test-setup] ----- -CREATE INDEX indexOnBooks FOR (b:Label1) ON (b.prop1) ----- -//// +|=== -.Query -[source, cypher, role=test-fail] ----- -CREATE INDEX indexOnBooks FOR (book:Book) ON (book.numberOfPages) ----- -In this case the index can't be created because there already exists an index with the given name. +.+Create a point index on relationships+ +[options="noheader", width="100%", cols="2, 8a"] +|=== -.Error message -[source, error] +| Syntax +| +[source, syntax, role="noheader"] ---- -There already exists an index called 'indexOnBooks'. +CREATE POINT INDEX [index_name] [IF NOT EXISTS] +FOR ()-"["r:TYPE_NAME"]"-() +ON (r.propertyName) +[OPTIONS "{" option: value[, ...] "}"] ---- -====== - - -[discrete] -[[administration-indexes-failure-to-create-an-index-when-a-constraint-already-exists]] -=== Failure to create an index when a constraint already exists +| Description +| +Create a point index on relationships where the property has a point value. -Create an index on the property `isbn` on nodes with the `Book` label, when an index-backed constraint already exists on that schema. +Index provider and configuration can be specified using the `OPTIONS` clause. +There is only one available index provider for this index. +|=== -.+CREATE RANGE INDEX+ -====== -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT FOR (book:Book) REQUIRE (book.isbn) IS UNIQUE ----- -//// +.+Create a node label lookup index+ +[options="noheader", width="100%", cols="2, 8a"] +|=== -.Query -[source, cypher, role=test-fail] +| Syntax +| +[source, syntax, role="noheader"] ---- -CREATE INDEX bookIsbnIndex FOR (book:Book) ON (book.isbn) +CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] +FOR (n) +ON EACH labels(n) +[OPTIONS "{" option: value[, ...] "}"] ---- -In this case the index can not be created because a index-backed constraint already exists on that label and property combination. +| Description +| +Create a node label lookup index. -.Error message -[source, error] ----- -There is a uniqueness constraint on (:Book {isbn}), so an index is already created that matches this. ----- +Index provider can be specified using the `OPTIONS` clause. +There is only one available index provider for this index. -====== +|=== -[discrete] -[[administration-indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-constraint]] -=== Failure to create an index with the same name as an already existing constraint +.+Create a relationship type lookup index+ +[options="noheader", width="100%", cols="2, 8a"] +|=== -Create a named index on the property `numberOfPages` on nodes with the `Book` label, when a constraint with the given name already exists. +| Syntax +| +[source, syntax, role="noheader"] +---- +CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] +FOR ()-"["r"]"-() +ON [EACH] type(r) +[OPTIONS "{" option: value[, ...] "}"] +---- +| Description +| +Create a relationship type lookup index. -.+CREATE RANGE INDEX+ -====== +Index provider can be specified using the `OPTIONS` clause. +There is only one available index provider for this index. -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT bookRecommendations FOR (book:Book) REQUIRE (book.recommend) IS NOT NULL ----- -//// +|=== -.Query -[source, cypher, role=test-fail] ----- -CREATE INDEX bookRecommendations FOR (book:Book) ON (book.recommendations) ----- -In this case the index can not be created because there already exists a constraint with the given name. +.+Drop an index+ +[options="noheader", width="100%", cols="2, 8a"] +|=== -.Error message -[source, error] +| Syntax +| +[source, syntax, role="noheader"] ---- -There already exists a constraint called 'bookRecommendations'. +DROP INDEX index_name [IF EXISTS] ---- -====== - - -[[administration-indexes-list-indexes]] -== +SHOW INDEXES+ +| Description +| Drop an index of any index type. -Listing indexes can be done with `SHOW INDEXES`, which will produce a table with the following columns: +| Note +| +The command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. +With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. -.List indexes output -[options="header", cols="4,6,2"] |=== -| Column | Description | Type -| `id` -| The id of the index. label:default-output[] -| `INTEGER` -| `name` -| Name of the index (explicitly set by the user or automatically assigned). label:default-output[] -| `STRING` - -| `state` -| Current state of the index. label:default-output[] -| `STRING` +.List indexes +[options="noheader", width="100%", cols="2, 8a"] +|=== -| `populationPercent` -| % of index population. label:default-output[] -| `FLOAT` +| Syntax +| +[source, syntax, role="noheader"] +---- +SHOW [ALL \| FULLTEXT \| LOOKUP \| POINT \| RANGE \| TEXT] INDEX[ES] + [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] + [WHERE expression] + [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] +---- -| `type` -| The IndexType of this index (`FULLTEXT`, `LOOKUP`, `POINT`, `RANGE`, or `TEXT`). label:default-output[] -| `STRING` +| Description +| List indexes in the database, either all or filtered on index type. -| `entityType` -| Type of entities this index represents (nodes or relationship). label:default-output[] -| `STRING` +| Note +| When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. -| `labelsOrTypes` -| The labels or relationship types of this index. label:default-output[] -| `LIST OF STRING` +|=== -| `properties` -| The properties of this index. label:default-output[] -| `LIST OF STRING` -| `indexProvider` -| The index provider for this index. label:default-output[] -| `STRING` +Creating an index requires xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `CREATE INDEX` privilege], +while dropping an index requires xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `DROP INDEX` privilege] and +listing indexes require xref::administration/access-control/database-administration.adoc#access-control-database-administration-index[the `SHOW INDEX` privilege]. -// New in 5.0 -| `owningConstraint` -| The name of the constraint the index is associated with or `null` if the index is not associated with any constraint. label:default-output[] -| `STRING` +xref:query-tuning/using.adoc[Planner hints and the USING keyword] describes how to make the Cypher planner use specific indexes (especially in cases where the planner would not necessarily have used them). -| `options` -| The options passed to `CREATE` command. -| `MAP` -| `failureMessage` -| The failure description of a failed index. -| `STRING` +[[indexes-single-vs-composite-index]] +== Composite index limitations -| `createStatement` -| Statement used to create the index. -| `STRING` +Like single-property range indexes, composite range indexes support all predicates: -|=== +* equality check: `n.prop = value` +* list membership check: `n.prop IN list` +* existence check: `n.prop IS NOT NULL` +* range search: `n.prop > value` +* prefix search: `STARTS WITH` [NOTE] ==== -The command `SHOW INDEXES` returns only the default output. -For a full output use the optional `YIELD` command. -Full output: `+SHOW INDEXES YIELD *+`. +For details about each operator, see xref::syntax/operators.adoc[Operators]. ==== -Listing indexes also allows for `WHERE` and `YIELD` clauses to filter the returned rows and columns. - - -== +SHOW INDEXES+ - -*Examples:* - -* xref::indexes-for-search-performance.adoc#administration-indexes-listing-all-indexes[] -* xref::indexes-for-search-performance.adoc#administration-indexes-listing-indexes-with-filtering[] - - -[discrete] -[[administration-indexes-listing-all-indexes]] -=== Listing all indexes - -To list all indexes with the default output columns, the `SHOW INDEXES` command can be used. -If all columns are required, use `SHOW INDEXES YIELD *`. - - -.+SHOW INDEXES+ -====== - -.Query -[source, cypher, role=test-result-skip] ----- -SHOW INDEXES ----- - -One of the output columns from `SHOW INDEXES` is the name of the index. -This can be used to drop the index with the xref::indexes-for-search-performance.adoc#administration-indexes-drop-an-index[`DROP INDEX` command]. - -// SHOW INDEXES default outputs -// 4.4: id, name, state, populationPercent, uniqueness, type, entityType, labelsOrTypes, properties, indexProvider -// 5.0: id, name, state, populationPercent, type, entityType, labelsOrTypes, properties, indexProvider, owningConstraint - -.Result -[queryresult] ----- -+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | -+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 20 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | -| 2 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | -| 14 | "constraint_1bc95fcb" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["isbn"] | "range-1.0" | "constraint_1bc95fcb" | -| 4 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | -| 7 | "indexOnBooks" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Label1"] | ["prop1"] | "range-1.0" | NULL | -| 10 | "index_with_config" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop2"] | "point-1.0" | NULL | -| 6 | "index_with_indexprovider" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "text-2.0" | NULL | -| 9 | "index_with_options" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "point-1.0" | NULL | -| 8 | "index_with_provider" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop1"] | "point-1.0" | NULL | -| 15 | "node_index_name" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Person"] | ["sublocation"] | "point-1.0" | NULL | -| 3 | "node_index_nickname" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["nickname"] | "text-2.0" | NULL | -| 11 | "node_label_lookup_index_2" | "ONLINE" | 100.0 | "LOOKUP" | "NODE" | NULL | NULL | "token-lookup-1.0" | NULL | -| 16 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | -| 19 | "range_index_with_provider" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "range-1.0" | NULL | -| 13 | "rel_index_name" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["intersection"] | "point-1.0" | NULL | -| 18 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | -| 17 | "rel_type_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "RELATIONSHIP" | NULL | NULL | "token-lookup-1.0" | NULL | -| 1 | "relprop_index_name" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["KNOWS"] | ["interest"] | "text-2.0" | NULL | -+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -18 rows ----- - -====== - - -[discrete] -[[administration-indexes-listing-indexes-with-filtering]] -=== Listing indexes with filtering - -One way of filtering the output from `SHOW INDEXES` by index type is the use of type keywords, listed in the xref::indexes-for-search-performance.adoc#administration-indexes-syntax[syntax table]. - -For example, to show only range indexes, use `SHOW RANGE INDEXES`. +However, predicates might be planned as existence check and a filter. +For most predicates, this can be avoided by following these restrictions: -Another more flexible way of filtering the output is to use the `WHERE` clause. -An example is to only show indexes not belonging to constraints. +* If there is any `equality check` and `list membership check` predicates, +they need to be for the first properties defined by the index. +* There can be up to one `range search` or `prefix search` predicate. +* There can be any number of `existence check` predicates. +* Any predicate after a `range search`, `prefix search` or `existence check` predicate has to be an `existence check` predicate. +[NOTE] +==== +The `suffix search` (`ENDS WITH`) and `substring search` (`CONTAINS`) predicates can utilize the index as well. +However, they are always planned as an existence check and a filter and any predicates following after will therefore also be planned as such. +==== -.+SHOW RANGE INDEXES+ -====== +For example, an index on nodes with `:Label(prop1,prop2,prop3,prop4,prop5,prop6)` and predicates: -.Query -[source, cypher, role=test-result-skip] +[source, cypher, role=test-skip] ---- -SHOW RANGE INDEXES WHERE owningConstraint IS NULL +WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 < 'e' AND n.prop5 = true AND n.prop6 IS NOT NULL ---- -This will only return the default output columns. - -To get all columns, use: - -[source, syntax, role="noheader"] ----- -SHOW INDEXES YIELD * WHERE ... ----- +will be planned as: -.Result -[queryresult] +[source, cypher, role=test-skip] ---- -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 20 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | -| 2 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | -| 4 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | -| 7 | "indexOnBooks" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Label1"] | ["prop1"] | "range-1.0" | NULL | -| 16 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | -| 19 | "range_index_with_provider" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "range-1.0" | NULL | -| 18 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -7 rows +WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 IS NOT NULL AND n.prop5 IS NOT NULL AND n.prop6 IS NOT NULL ---- -====== - - -[[administration-indexes-drop-indexes]] -== +DROP INDEX+ - -An index can be dropped (removed) using the name with the `DROP INDEX index_name` command. -This command can drop indexes of any type, except those backing constraints. -The name of the index can be found using the xref::indexes-for-search-performance.adoc#administration-indexes-list-indexes[`SHOW INDEXES` command], given in the output column `name`. - - -[[drop-indexes-examples]] -== +DROP INDEX+ - -*Examples:* - -* xref::indexes-for-search-performance.adoc#administration-indexes-drop-an-index[] -* xref::indexes-for-search-performance.adoc#administration-indexes-drop-a-non-existing-index[] - - -[discrete] -[[administration-indexes-drop-an-index]] -=== Drop an index - - -.+DROP INDEX+ -====== +with filters on `n.prop4 < 'e'` and `n.prop5 = true`, since `n.prop3` has a `range search` predicate. -.Query -[source, cypher, indent=0] ----- -DROP INDEX example_index ----- +And an index on nodes with `:Label(prop1,prop2)` with predicates: -.Result -[queryresult] +[source, cypher, role=test-skip] ---- -Removed 1 index. +WHERE n.prop1 ENDS WITH 'x' AND n.prop2 = false ---- -====== - - -[discrete] -[[administration-indexes-drop-a-non-existing-index]] -=== Drop a non-existing index - -If it is uncertain if an index exists and you want to drop it if it does but not get an error should it not, use `IF EXISTS`. - - -.+DROP INDEX+ -====== +will be planned as: -.Query -[source, cypher, indent=0] +[source, cypher, role=test-skip] ---- -DROP INDEX missing_index_name IF EXISTS +WHERE n.prop1 IS NOT NULL AND n.prop2 IS NOT NULL ---- -.Result -[queryresult] ----- -(no changes, no records) ----- +with filters on `n.prop1 ENDS WITH 'x'` and `n.prop2 = false`, since `n.prop1` has a `suffix search` predicate. -====== +Composite indexes require predicates on all properties indexed. +If there are predicates on only a subset of the indexed properties, it will not be possible to use the composite index. +To get this kind of fallback behavior, it is necessary to create additional indexes on the relevant sub-set of properties or on single properties. diff --git a/modules/ROOT/pages/keyword-glossary.adoc b/modules/ROOT/pages/keyword-glossary.adoc index a3bf84cc4..2b43d610d 100644 --- a/modules/ROOT/pages/keyword-glossary.adoc +++ b/modules/ROOT/pages/keyword-glossary.adoc @@ -76,35 +76,35 @@ Typically used when modifying or importing large amounts of data. | Schema | Create a fulltext index on relationships. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE LOOKUP INDEX [name\] [IF NOT EXISTS\] FOR (n) ON EACH labels(n) [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE LOOKUP INDEX [name\] [IF NOT EXISTS\] FOR (n) ON EACH labels(n) [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create an index on all nodes with any label. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE LOOKUP INDEX [name\] [IF NOT EXISTS\] FOR ()-"["r"\]"-() ON [EACH\] type(r) [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE LOOKUP INDEX [name\] [IF NOT EXISTS\] FOR ()-"["r"\]"-() ON [EACH\] type(r) [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create an index on all relationships with any relationship type. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE POINT INDEX [name\] [IF NOT EXISTS\] FOR (n:Label) ON (n.property) [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE POINT INDEX [name\] [IF NOT EXISTS\] FOR (n:Label) ON (n.property) [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a point index on nodes. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE POINT INDEX [name\] [IF NOT EXISTS\] FOR ()-"["r:TYPE"\]"-() ON (r.property) [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE POINT INDEX [name\] [IF NOT EXISTS\] FOR ()-"["r:TYPE"\]"-() ON (r.property) [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a point index on relationships. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE [RANGE\] INDEX [name\] [IF NOT EXISTS\] FOR (n:Label) ON (n.property[, ..., n.propertyN\]) [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE [RANGE\] INDEX [name\] [IF NOT EXISTS\] FOR (n:Label) ON (n.property[, ..., n.propertyN\]) [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a range index on nodes. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE [RANGE\] INDEX [name\] [IF NOT EXISTS\] FOR ()-"["r:TYPE"\]"-() ON (r.property[, ..., r.propertyN\]) [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE [RANGE\] INDEX [name\] [IF NOT EXISTS\] FOR ()-"["r:TYPE"\]"-() ON (r.property[, ..., r.propertyN\]) [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a range index on relationships. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE TEXT INDEX [name\] [IF NOT EXISTS\] FOR (n:Label) ON (n.property) [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE TEXT INDEX [name\] [IF NOT EXISTS\] FOR (n:Label) ON (n.property) [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a text index on nodes. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[CREATE TEXT INDEX [name\] [IF NOT EXISTS\] FOR ()-"["r:TYPE"\]"-() ON (r.property) [OPTIONS {optionKey: optionValue[, ...\]}\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE TEXT INDEX [name\] [IF NOT EXISTS\] FOR ()-"["r:TYPE"\]"-() ON (r.property) [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a text index on relationships. @@ -124,7 +124,7 @@ All associated relationships will automatically be deleted. | Schema | Drop a constraint using the name. -| xref::indexes-for-search-performance.adoc#administration-indexes-syntax[DROP INDEX name [IF EXISTS\]] +| xref::indexes-for-search-performance.adoc#indexes-syntax[DROP INDEX name [IF EXISTS\]] | Schema | Drop an index using the name. @@ -186,7 +186,7 @@ Either the pattern already exists, or it needs to be created. List constraints in the database, either all or filtered on type. Also allows `WHERE` and `YIELD` clauses. -| xref::indexes-for-search-performance.adoc#administration-indexes-list-indexes[SHOW [ALL\|FULLTEXT\|LOOKUP\|POINT\|RANGE\|TEXT\] INDEX[ES\]] +| xref::indexes-for-search-performance.adoc#indexes-list-indexes[SHOW [ALL\|FULLTEXT\|LOOKUP\|POINT\|RANGE\|TEXT\] INDEX[ES\]] | Schema | List indexes in the database, either all or filtered on fulltext, lookup, point, range, or text indexes. diff --git a/modules/ROOT/pages/query-tuning/indexes.adoc b/modules/ROOT/pages/query-tuning/indexes.adoc index 4557ba658..98a621786 100644 --- a/modules/ROOT/pages/query-tuning/indexes.adoc +++ b/modules/ROOT/pages/query-tuning/indexes.adoc @@ -642,7 +642,7 @@ Total database accesses: 2, total allocated memory: 184 A query containing equality comparisons for all the properties of a composite index will automatically be backed by the same index. However, the query does not need to have equality on all properties. It can have ranges and existence predicates as well. -But in these cases rewrites might happen depending on which properties have which predicates, see xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. +But in these cases rewrites might happen depending on which properties have which predicates, see xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. //// [source, cypher, role=test-setup] @@ -730,7 +730,7 @@ Total database accesses: 2, total allocated memory: 184 Composite indexes are also automatically used for inequality (range) comparisons of indexed properties in the `WHERE` clause. Equality or list membership check predicates may precede the range predicate. -However, predicates after the range predicate may be rewritten as an existence check predicate and a filter as described in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. +However, predicates after the range predicate may be rewritten as an existence check predicate and a filter as described in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. //// [source, cypher, role=test-setup] @@ -1019,7 +1019,7 @@ CREATE RANGE INDEX node_text_firstname_surname FOR (p:Person) ON (p.firstname, p The `STARTS WITH` predicate on `person.firstname` in the following query will use the `Person(firstname,surname)` index, if it exists. Any (non-existence check) predicate on `person.surname` will be rewritten as existence check with a filter. However, if the predicate on `person.firstname` is a equality check then a `STARTS WITH` on `person.surname` would also use the index (without rewrites). -More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. +More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. .Query [source, cypher] @@ -1103,7 +1103,7 @@ Text indexes only index String values and therefore do not find other values. The `ENDS WITH` predicate on `r.metIn` in the following query uses the `KNOWS(metIn, lastMetIn)` index, if it exists. However, it is rewritten as existence check and a filter due to the index not supporting actual suffix searches for composite indexes, this is still faster than not using an index in the first place. Any (non-existence check) predicate on `KNOWS.lastMetIn` is also rewritten as existence check with a filter. -More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. +More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. //// [source, cypher, role=test-setup] @@ -1190,7 +1190,7 @@ Text indexes only index String values and therefore do not find other values. The `CONTAINS` predicate on `person.country` in the following query will use the `Person(country,age)` index, if it exists. However, it will be rewritten as existence check and a filter due to the index not supporting actual suffix searches for composite indexes, this is still faster than not using an index in the first place. Any (non-existence check) predicate on `person.age` will also be rewritten as existence check with a filter. -More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#administration-indexes-single-vs-composite-index[composite index limitations]. +More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. .Query [source, cypher] diff --git a/modules/ROOT/pages/syntax/spatial.adoc b/modules/ROOT/pages/syntax/spatial.adoc index 56d58452b..b5101392d 100644 --- a/modules/ROOT/pages/syntax/spatial.adoc +++ b/modules/ROOT/pages/syntax/spatial.adoc @@ -247,7 +247,7 @@ RETURN == Point index // POINT INDEX new in Neo4j 5.0 -If there is a xref::indexes-for-search-performance.adoc#administration-indexes-create-a-node-point-index[index] on a particular `:Label(property)` combination, and a spatial point is assigned to that property on a node with that label, the node will be indexed in a point index. +If there is a xref::indexes-for-search-performance.adoc#indexes-create-a-node-point-index[index] on a particular `:Label(property)` combination, and a spatial point is assigned to that property on a node with that label, the node will be indexed in a point index. For point indexing, Neo4j uses space filling curves in 2D or 3D over an underlying generalized B+Tree. Points will be stored in up to four different trees, one for each of the xref::syntax/spatial.adoc#cypher-spatial-crs[four coordinate reference systems]. From 7403f71e1c1b71de2df31724f976620f3771c7bd Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Thu, 13 Apr 2023 15:08:01 +0200 Subject: [PATCH 173/383] [Testing] Correct query plan for some operators (rollback) (#506) To a good extent, this is a rollback of #395. This investigation started with the query plan of `CacheProperties` being wrong since... well, there was no `CacheProperties` operator in the plan. The testing was happy that way, which was odd. It turned out that it was because of the example query in the `Eager` operator, which effectively _cleared_ the database, so that all examples afterwards ran on an empty db. `CacheProperties` comes after `Eager` in the operators page, so that the `CacheProperties` example ran on an empty db. Summary of changes: - Changed `Eager` example so that it deletes only _two_ nodes, rather than _all_ nodes. The query plan is essentially unchanged, other than indexes being used. Most notably, `Eager` is still triggered, presumably as the db doesn't know upfront how many nodes have that property value anyway. - Fixing the above triggered a bunch of failures (for `CacheProperties`, `EagerAggregation`, `Distinct`) , which are essentially the plans changed in #395. I've restored them as they were before, but not altered them otherwise. - Given proper names to indexes/constraints, describing what they _index_ rather than what they _test_. - Moved the setup queries _inside_ the sections they belong to, rather than _before_ the respective openings. - Removed a setup block that dropped and recreated the exact same index. - The `Delete` example was acting on `(me:Person {name: 'me'})`, which has relationships attached to it, so an error is raised. Or rather, no failure used to happen because, previously, the `Eager` example cleared the whole db, so `DELETE me` was deleting a non-existent node, and now the `Eager` example `DETACH DELETE`s the `me` node, so that it keeps not existing. However, if for whatever reason we changed some of the examples involved and it turned out that `me` existed with relationships (as the page setup creates), then we'd get a failure. So I've changed the `DELETE` example to act on `you`, rather than `me`, as `you` never exists. There's also some perverse joy involved with pretending to obliterate the reader, so I like it better. --- .../ROOT/pages/execution-plans/operators.adoc | 263 +++++++++--------- 1 file changed, 132 insertions(+), 131 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index f0350279d..87596dcec 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -80,8 +80,6 @@ CREATE (me:Person {name: 'me'}), ---- //// -// --- scan and seek operators --- - [[query-plan-all-nodes-scan]] == All Nodes Scan @@ -126,17 +124,19 @@ Total database accesses: 36, total allocated memory: 184 ---- ====== + + +[[query-plan-directed-relationship-index-scan]] +== Directed Relationship Index Scan +// DirectedRelationshipIndexScan + //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX relprop_index_range FOR ()-[r:WORKS_IN]->() ON (r.title) +CREATE RANGE INDEX range_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) ---- //// -[[query-plan-directed-relationship-index-scan]] -== Directed Relationship Index Scan -// DirectedRelationshipIndexScan - The `DirectedRelationshipIndexScan` operator examines all values stored in an index, returning all relationships and their start and end nodes with a particular relationship type and a specified property. @@ -399,17 +399,18 @@ Total database accesses: 1, total allocated memory: 184 ====== + +[[query-plan-directed-relationship-index-contains-scan]] +== Directed Relationship Index Contains Scan +// DirectedRelationshipIndexContainsScan + //// [source, cypher, role=test-setup] ---- -CREATE TEXT INDEX relprop_index_contains FOR ()-[r:WORKS_IN]->() ON (r.title) +CREATE TEXT INDEX text_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) ---- //// -[[query-plan-directed-relationship-index-contains-scan]] -== Directed Relationship Index Contains Scan -// DirectedRelationshipIndexContainsScan - The `DirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific string; for example, in queries including `CONTAINS`. Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a type scan using `DirectedRelationshipTypeScan`, and a property store filter. @@ -585,18 +586,18 @@ Total database accesses: 9, total allocated memory: 184 ====== + +[[query-plan-directed-relationship-index-seek-by-range]] +== Directed Relationship Index Seek By Range +// DirectedRelationshipIndexSeekByRange + //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX relprop_index_100 FOR ()-[r:WORKS_IN]->() ON (r.duration) +CREATE RANGE INDEX range_worksin_duration FOR ()-[r:WORKS_IN]->() ON (r.duration) ---- //// - -[[query-plan-directed-relationship-index-seek-by-range]] -== Directed Relationship Index Seek By Range -// DirectedRelationshipIndexSeekByRange - The `DirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix string. `DirectedRelationshipIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. @@ -695,10 +696,6 @@ Total database accesses: 76, total allocated memory: 184 The `UnionNodeByLabelsScan` operator fetches all nodes that have at least one of the provided labels from the node label index. ====== -//// -CREATE LOOKUP INDEX lookup_index_name FOR (n) ON EACH labels(n) -//// - .Query [source,cypher] ---- @@ -739,9 +736,6 @@ Total database accesses: 13, total allocated memory: 184 The `IntersectionNodeByLabelsScan` operator fetches all nodes that have all of the provided labels from the node label index. ====== -//// -CREATE LOOKUP INDEX lookup_index_name FOR (n) ON EACH labels(n) -//// .Query [source,cypher] @@ -816,6 +810,7 @@ Total database accesses: 28, total allocated memory: 184 [[query-plan-undirected-all-relationships-scan]] == Undirected All Relationships Scan == The `UndirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. + ====== .Query [source,cypher] @@ -1104,17 +1099,18 @@ Total database accesses: 15, total allocated memory: 184 ====== + +[[query-plan-node-index-seek]] +== Node Index Seek +// NodeIndexSeek + //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX index_node_seek FOR (l:Location) ON (l.name) +CREATE RANGE INDEX range_location_name FOR (l:Location) ON (l.name) ---- //// -[[query-plan-node-index-seek]] -== Node Index Seek -// NodeIndexSeek - The `NodeIndexSeek` operator finds nodes using an index seek. The node variable and the index used are shown in the arguments of the operator. If the index is a unique index, the operator is instead called xref::execution-plans/operators.adoc#query-plan-node-unique-index-seek[NodeUniqueIndexSeek]. @@ -1155,17 +1151,18 @@ Total database accesses: 2, total allocated memory: 184 ====== + +[[query-plan-node-unique-index-seek]] +== Node Unique Index Seek +// NodeUniqueIndexSeek + //// [source, cypher, role=test-setup] ---- -CREATE CONSTRAINT constraint_Team_name IF NOT EXISTS FOR (t:Team) REQUIRE (t.name) IS UNIQUE +CREATE CONSTRAINT team_name IF NOT EXISTS FOR (t:Team) REQUIRE (t.name) IS UNIQUE ---- //// -[[query-plan-node-unique-index-seek]] -== Node Unique Index Seek -// NodeUniqueIndexSeek - The `NodeUniqueIndexSeek` operator finds nodes using an index seek within a unique index. The node variable and the index used are shown in the arguments of the operator. If the index is not unique, the operator is instead called xref::execution-plans/operators.adoc#query-plan-node-index-seek[NodeIndexSeek]. @@ -1207,6 +1204,12 @@ Total database accesses: 1, total allocated memory: 184 ---- ====== + + +[[query-plan-multi-node-index-seek]] +== Multi Node Index Seek +// MultiNodeIndexSeek + //// [source, cypher, role=test-setup] ---- @@ -1214,10 +1217,6 @@ CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name) ---- //// -[[query-plan-multi-node-index-seek]] -== Multi Node Index Seek -// MultiNodeIndexSeek - The `MultiNodeIndexSeek` operator finds nodes using multiple index seeks. It supports using multiple distinct indexes for different nodes in the query. The node variables and the indexes used are shown in the arguments of the operator. @@ -1265,17 +1264,18 @@ Total database accesses: 0, total allocated memory: 184 ====== + +[[query-plan-asserting-multi-node-index-seek]] +== Asserting Multi Node Index Seek +// AssertingMultiNodeIndexSeek + //// [source, cypher, role=test-setup] ---- -CREATE CONSTRAINT constraint_Team_id IF NOT EXISTS FOR (t:Team) REQUIRE (t.id) IS UNIQUE +CREATE CONSTRAINT team_id IF NOT EXISTS FOR (t:Team) REQUIRE (t.id) IS UNIQUE ---- //// -[[query-plan-asserting-multi-node-index-seek]] -== Asserting Multi Node Index Seek -// AssertingMultiNodeIndexSeek - The `AssertingMultiNodeIndexSeek` operator is used to ensure that no property uniqueness constraints are violated. The example looks for the presence of a team with the supplied name and id, and if one does not exist, it will be created. Owing to the existence of two property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. @@ -1319,18 +1319,12 @@ Total database accesses: 4, total allocated memory: 184 ====== -//// -[source, cypher, role=test-setup] ----- -DROP INDEX index_node_seek; -CREATE RANGE INDEX index_range_node_location FOR (l:Location) ON (l.name) ----- -//// [[query-plan-node-index-seek-by-range]] == Node Index Seek By Range // NodeIndexSeekByRange + The `NodeIndexSeekByRange` operator finds nodes using an index seek where the value of the property matches a given prefix string. `NodeIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. If the index is a unique index, the operator is instead called `NodeUniqueIndexSeekByRange`. @@ -1417,17 +1411,19 @@ Total database accesses: 1, total allocated memory: 184 ---- ====== + + +[[query-plan-node-index-contains-scan]] +== Node Index Contains Scan == +// NodeIndexContainsScan + //// [source, cypher, role=test-setup] ---- -CREATE TEXT INDEX index_node_contains FOR (l:Location) ON (l.name) +CREATE TEXT INDEX text_location_name FOR (l:Location) ON (l.name) ---- //// -[[query-plan-node-index-contains-scan]] -== Node Index Contains Scan == -// NodeIndexContainsScan - The `NodeIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific string; for example, in queries including `CONTAINS`. Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. @@ -3453,9 +3449,9 @@ Alternatively, the records to be updated can be returned, followed by an update [source, cypher] ---- PROFILE -MATCH (a), (b) +MATCH (a:Person {name: 'me'}), (b:Person {name: 'Bob'}) DETACH DELETE a, b -MERGE () +MERGE (:Person {name: 'me'}) ---- .Query Plan @@ -3467,39 +3463,34 @@ Runtime PIPELINED Runtime version {neo4j-version-minor} -Batch size 1024 - -+-------------------+----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------+----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | | 15129 | 0 | 0 | | | | | -| | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +EmptyResult | 1 | | 15129 | 0 | 0 | | | | | -| | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +Apply | 2 | | 15129 | 15129 | 0 | | | | | -| |\ +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| | +Merge | 3 | CREATE (anon_0) | 15129 | 15129 | 1 | | | | | -| | | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| | +AllNodesScan | 4 | anon_0 | 1860867 | 15128 | 30257 | 24680 | 347967/0 | 1820.510 | Fused in Pipeline 5 | -| | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +Eager | 5 | read/delete conflict for variable: anon_0, read/delete conflict for variable: a, | 15129 | 15129 | 0 | 243608 | 0/0 | 0.182 | In Pipeline 4 | -| | | | read/delete conflict for variable: b | | | | | | | | -| | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +DetachDelete | 6 | b | 15129 | 15129 | 122 | | | | | -| | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +DetachDelete | 7 | a | 15129 | 15129 | 1 | | | | | -| | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +Eager | 8 | read/delete conflict for variable: b | 15129 | 15129 | 0 | 243608 | 0/0 | 8.515 | Fused in Pipeline 3 | -| | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +CartesianProduct | 9 | | 15129 | 15129 | 0 | 10712 | 0/0 | 0.342 | In Pipeline 2 | -| |\ +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| | +AllNodesScan | 10 | b | 123 | 123 | 124 | 136 | 23/0 | 0.243 | In Pipeline 1 | -| | +----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +AllNodesScan | 11 | a | 123 | 123 | 124 | 120 | 23/0 | 0.344 | In Pipeline 0 | -+-------------------+----+----------------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 30629, total allocated memory: 260488 +Batch size 128 ++---------------------+----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 1 | 0 | 0 | | | | | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 1 | 0 | 0 | | | | | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 2 | | 1 | 1 | 0 | | | | | +| |\ +----+------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Merge | 3 | CREATE (anon_0:Person {name: $autostring_2}) | 1 | 1 | 3 | | | | | +| | | +----+------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeek | 4 | RANGE INDEX anon_0:Person(name) WHERE name = $autostring_2 | 1 | 0 | 1 | 3176 | 1/0 | 0.407 | Fused in Pipeline 3 | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Eager | 5 | read/delete conflict for variable: anon_0 | 1 | 1 | 0 | 104 | 0/0 | 0.020 | In Pipeline 2 | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +DetachDelete | 6 | b | 1 | 1 | 4 | | | | | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DetachDelete | 7 | a | 1 | 1 | 5 | | | | | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Eager | 8 | read/delete conflict for variable: b | 1 | 1 | 0 | 104 | 1/0 | 0.183 | Fused in Pipeline 1 | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +MultiNodeIndexSeek | 9 | RANGE INDEX a:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 120 | 1/1 | 0.224 | In Pipeline 0 | +| | | RANGE INDEX b:Person(name) WHERE name = $autostring_1 | | | | | | | | ++---------------------+----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 17, total allocated memory: 3320 ---- ====== @@ -3537,19 +3528,23 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------+----+-----------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+----+-----------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | location, people | 0 | 0 | 0 | | 0/0 | 0.013 | In Pipeline 1 | -| | +----+-----------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +EagerAggregation | 1 | l.name AS location, collect(p.name) AS people | 0 | 0 | 0 | 488 | | | | -| | +----+-----------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 2 | l:Location AND p:Person | 0 | 0 | 0 | | | | | -| | +----+-----------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipTypeScan | 3 | (p)-[anon_0:WORKS_IN]->(l) | 3 | 0 | 0 | 120 | 0/0 | 0.121 | Fused in Pipeline 0 | -+-------------------------------+----+-----------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------+----+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | location, people | 4 | 6 | 0 | | 0/0 | 0.022 | In Pipeline 1 | +| | +----+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | cache[l.name] AS location, collect(p.name) AS people | 4 | 6 | 30 | 2584 | | | | +| | +----+------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | p:Person | 15 | 15 | 30 | | | | | +| | +----+------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | 3 | (l)<-[anon_0:WORKS_IN]-(p) | 15 | 15 | 26 | | | | | +| | +----+------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +CacheProperties | 4 | cache[l.name] | 10 | 10 | 20 | | | | | +| | +----+------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 5 | l:Location | 10 | 10 | 11 | 120 | 4/0 | 0.813 | Fused in Pipeline 0 | ++-------------------+----+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 672 +Total database accesses: 117, total allocated memory: 2664 ---- ====== @@ -3710,7 +3705,7 @@ This may lead to increased memory pressure in the system. ---- PROFILE MATCH (l:Location)<-[:WORKS_IN]-(p:Person) -RETURN DISTINCT l +RETURN DISTINCT p ---- .Query Plan @@ -3724,19 +3719,21 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | l | 0 | 0 | 0 | | | | | -| | +----+----------------------------+----------------+------+---------+----------------+ | | | -| +Distinct | 1 | l | 0 | 0 | 0 | 224 | | | | -| | +----+----------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 2 | l:Location AND p:Person | 0 | 0 | 0 | | | | | -| | +----+----------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipTypeScan | 3 | (p)-[anon_0:WORKS_IN]->(l) | 3 | 0 | 0 | 120 | 0/0 | 0.112 | Fused in Pipeline 0 | -+-------------------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | p | 14 | 14 | 28 | | | | | +| | +----+-----------------------+----------------+------+---------+----------------+ | | | +| +Distinct | 1 | p | 14 | 14 | 0 | 352 | | | | +| | +----+-----------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | p:Person | 15 | 15 | 30 | | | | | +| | +----+-----------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | 3 | (l)<-[r:WORKS_IN]-(p) | 15 | 15 | 26 | | | | | +| | +----+-----------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 4 | l:Location | 10 | 10 | 11 | 120 | 4/0 | 0.287 | Fused in Pipeline 0 | ++------------------+----+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 288 +Total database accesses: 95, total allocated memory: 432 ---- ====== @@ -4611,19 +4608,23 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------+----+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+----+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | location, name | 0 | 0 | 0 | | | | | -| | +----+------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | 1 | l.name AS location, p.name AS name | 0 | 0 | 0 | | | | | -| | +----+------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 2 | l:Location AND p:Person | 0 | 0 | 0 | | | | | -| | +----+------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipTypeScan | 3 | (p)-[anon_0:WORKS_IN]->(l) | 3 | 0 | 0 | 120 | 0/0 | 0.094 | Fused in Pipeline 0 | -+-------------------------------+----+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | location, name | 13 | 13 | 0 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[l.name] AS location, p.name AS name | 13 | 13 | 26 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | p:Person | 13 | 13 | 26 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | 3 | (l)<-[anon_0:WORKS_IN]-(p) | 13 | 13 | 24 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +CacheProperties | 4 | cache[l.name] | 10 | 10 | 20 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 5 | l:Location | 10 | 10 | 11 | 120 | 4/0 | 0.344 | Fused in Pipeline 0 | ++------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 184 +Total database accesses: 107, total allocated memory: 200 ---- ====== @@ -4691,8 +4692,8 @@ The `Delete` operator is used to delete a node or a relationship. [source, cypher] ---- PROFILE -MATCH (me:Person {name: 'me'}) -DELETE me +MATCH (you:Person {name: 'you'}) +DELETE you ---- .Query Plan @@ -4713,9 +4714,9 @@ Batch size 128 | | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | | +EmptyResult | 1 | | 0 | 0 | 0 | | | | | | | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Delete | 2 | me | 0 | 0 | 0 | | | | | +| +Delete | 2 | you | 0 | 0 | 0 | | | | | | | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | 3 | RANGE INDEX me:Person(name) WHERE name = $autostring_0 | 0 | 0 | 1 | 120 | 1/0 | 0.330 | Fused in Pipeline 0 | +| +NodeIndexSeek | 3 | RANGE INDEX you:Person(name) WHERE name = $autostring_0 | 0 | 0 | 1 | 120 | 1/0 | 0.330 | Fused in Pipeline 0 | +-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 13, total allocated memory: 216 From 22711ef03a2fbba645de91704ab6a5d808dada46 Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Thu, 13 Apr 2023 15:26:28 +0200 Subject: [PATCH 174/383] BFS doesn't have an upper bound (#508) re-opening of https://github.com/neo4j/docs-cypher/pull/461 --- modules/ROOT/pages/execution-plans/operators.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 87596dcec..bb4e40ede 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -2727,7 +2727,6 @@ This is only used in cases where the individual paths are not of interest. This kind of expand is only planned when: * The individual paths are not of interest. -* The relationships have an upper bound. * The lower bound is either `0` or `1` (default). This operator guarantees that all the end nodes produced are unique. From 3103ad41947342702bd1833aeb98522377aa4164 Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Thu, 13 Apr 2023 16:06:30 +0200 Subject: [PATCH 175/383] Add docs explaining MERGE + relationship uniqueness constraints (#507) re-opening of https://github.com/neo4j/docs-cypher/pull/460 --------- --- modules/ROOT/pages/clauses/merge.adoc | 41 +++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index 1e05639eb..7d3ae8005 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -415,12 +415,12 @@ This is in contrast to the example shown above in xref::clauses/merge.adoc#merge [[query-merge-using-unique-constraints]] -== Using property uniqueness constraints with `MERGE` +== Using node property uniqueness constraints with `MERGE` Cypher prevents getting conflicting results from `MERGE` when using patterns that involve property uniqueness constraints. In this case, there must be at most one node that matches that pattern. -For example, given two property uniqueness constraints on `:Person(id)` and `:Person(ssn)`, a query such as `MERGE (n:Person {id: 12, ssn: 437})` will fail, if there are two different nodes (one with `id` 12 and one with `ssn` 437), or if there is only one node with only one of the properties. +For example, given two property node uniqueness constraints on `:Person(id)` and `:Person(ssn)`, a query such as `MERGE (n:Person {id: 12, ssn: 437})` will fail, if there are two different nodes (one with `id` 12 and one with `ssn` 437), or if there is only one node with only one of the properties. In other words, there must be exactly one node that matches the pattern, or no matching nodes. Note that the following examples assume the existence of property uniqueness constraints that have been created using: @@ -433,9 +433,9 @@ CREATE CONSTRAINT FOR (n:Person) REQUIRE n.role IS UNIQUE; [[merge-merge-using-unique-constraints-creates-a-new-node-if-no-node-is-found]] -=== Merge using property uniqueness constraints creates a new node if no node is found +=== Merge node using property uniqueness constraints creates a new node if no node is found -Given the property uniqueness constraint on the `name` property for all `Person` nodes, the below query will create a new `Person` with the `name` property `'Laurence Fishburne'`. +Given the node property uniqueness constraint on the `name` property for all `Person` nodes, the below query will create a new `Person` with the `name` property `'Laurence Fishburne'`. If a `'Laurence Fishburne'` node had already existed, `MERGE` would match the existing node instead. .Query @@ -454,7 +454,7 @@ RETURN laurence.name [[merge-merge-using-unique-constraints-matches-an-existing-node]] -=== Merge using property uniqueness constraints matches an existing node +=== Merge using node property uniqueness constraints matches an existing node Given property uniqueness constraint on the `name` property for all `Person` nodes, the below query will match the pre-existing `Person` node with the `name` property `'Oliver Stone'`. @@ -526,6 +526,37 @@ While there is a matching unique `Person` node with the name `'Oliver Stone'`, t Node already exists with label `Person` and property `name` = 'Oliver Stone' ---- +[[query-merge-using-relationship-unique-constraints]] +== Using relationship property uniqueness constraints with `MERGE` + +All that has been said above about node uniqueness constraints also applies to relationship uniqueness constraints. +However, for relationship uniqueness constraints there are some additional things to consider. + +For example, if there exists a relationship uniqueness constraint on `()-[:ACTED_IN(year)]-()`, then the following query, in which not all nodes of the pattern are bound, would fail: + +.Query +[source, cypher, role=test-fail] +---- +MERGE (charlie:Person {name: 'Charlie Sheen'})-[r:ACTED_IN {year: 1987}]->(wallStreet:Movie {title: 'Wall Street'}) +RETURN charlie.name, type(r), wallStreet.title +---- + +This is due to the all-or-nothing semantics of `MERGE`, which causes the query to fail if there exists a relationship with the given `year` property but there is no match for the full pattern. +In this example, since no match was found for the pattern, `MERGE` will try to create the full pattern including a relationship with `{year: 1987}`, which will lead to constraint violation error. + +Therefore, it is advised - especially when relationship uniqueness constraints exist - to always use bound nodes in the `MERGE` pattern. +The following would, therefore, be a more appropriate composition of the query: + +.Query +[source, cypher] +---- +MATCH + (charlie:Person {name: 'Charlie Sheen'}), + (wallStreet:Movie {title: 'Wall Street'}) +MERGE (charlie)-[r:ACTED_IN {year: 1987}]->(wallStreet) +RETURN charlie.name, type(r), wallStreet.title +---- + [[merge-using-map-parameters-with-merge]] === Using map parameters with `MERGE` From 473cec782262b97a1afe33d5153ca1a09a3c39ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:29:10 +0200 Subject: [PATCH 176/383] Fix dbms admin images (#510) --- ...rivileges_grant_and_deny_syntax_dbms_privileges.svg | 10 +++++++++- modules/ROOT/images/privileges_hierarchy_dbms.svg | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.svg b/modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.svg index b929a580d..ef7252df3 100644 --- a/modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.svg +++ b/modules/ROOT/images/privileges_grant_and_deny_syntax_dbms_privileges.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + diff --git a/modules/ROOT/images/privileges_hierarchy_dbms.svg b/modules/ROOT/images/privileges_hierarchy_dbms.svg index 705c3fd5d..977c8d072 100644 --- a/modules/ROOT/images/privileges_hierarchy_dbms.svg +++ b/modules/ROOT/images/privileges_hierarchy_dbms.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + From 94802d8666ef62152f79459b777e4d6201e50e57 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Tue, 18 Apr 2023 08:45:25 +0200 Subject: [PATCH 177/383] Re-add relationship constraint documentation (#490) Companion PR to https://github.com/neo4j/docs-cheat-sheet/pull/69 --- modules/ROOT/pages/constraints/examples.adoc | 454 +++++++++--------- modules/ROOT/pages/constraints/index.adoc | 23 +- modules/ROOT/pages/constraints/syntax.adoc | 67 +-- ...ions-additions-removals-compatibility.adoc | 114 ++--- .../ROOT/pages/execution-plans/operators.adoc | 4 +- .../ROOT/pages/introduction/cypher_neo4j.adoc | 12 +- .../introduction/neo4j-databases-graphs.adoc | 12 +- modules/ROOT/pages/keyword-glossary.adoc | 18 +- 8 files changed, 288 insertions(+), 416 deletions(-) diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc index 6fab58884..beaf0db61 100644 --- a/modules/ROOT/pages/constraints/examples.adoc +++ b/modules/ROOT/pages/constraints/examples.adoc @@ -47,13 +47,11 @@ FOR (book:Book) REQUIRE book.isbn IS UNIQUE Added 1 constraint. ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== -The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. +The detailed statistics view currently says `Unique constraints added: 1`. +It will be updated to say `Node property uniqueness constraints added: 1` in Neo4j version 6.0. ==== -//// ====== @@ -62,7 +60,7 @@ The statistics will be updated to say `Node uniqueness constraints` in Neo4j ver [[constraints-create-a-node-uniqueness-constraint-if-not-exist]] === Handling existing constraints when creating a constraint -Creating an already existing constraint will fail. +Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and no constraint is created if any other constraint with the given name or another node property uniqueness constraint on the same schema already exists. @@ -85,13 +83,11 @@ Assuming no constraint with the given name or other node property uniqueness con Added 1 constraint. ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== -The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. +The detailed statistics view currently says `Unique constraints added: 1`. +It will be updated to say `Node property uniqueness constraints added: 1` in Neo4j version 6.0. ==== -//// ====== @@ -130,13 +126,11 @@ OPTIONS { Added 1 constraint. ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== -The statistics will be updated to say `Node uniqueness constraints` in Neo4j version 6.0. +The detailed statistics view currently says `Unique constraints added: 1`. +It will be updated to say `Node property uniqueness constraints added: 1` in Neo4j version 6.0. ==== -//// ====== @@ -175,13 +169,10 @@ Constraint already exists: Constraint( id=4, name='preExisting_book_published', type='UNIQUENESS', schema=(:Book {published}), ownedIndex=3 ) ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== -The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6.0. +The constraint type will be updated to say `NODE PROPERTY UNIQUENESS` in Neo4j version 6.0. ==== -//// ====== @@ -307,13 +298,10 @@ Unable to create Constraint( name='book_title', type='UNIQUENESS', schema=(:Book Both Node(0) and Node(1) have the label `Book` and property `title` = 'Moby Dick' ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== -The constraint type will be updated to say `NODE_UNIQUENESS` in Neo4j version 6.0. +The constraint type will be updated to say `NODE PROPERTY UNIQUENESS` in Neo4j version 6.0. ==== -//// ====== @@ -331,9 +319,6 @@ WHERE book1.title = book2.title AND NOT book1 = book2 RETURN book1, book2 ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints -TODO: Remove 'test-skip' message on queries when feature is introduced [[constraints-examples-relationship-uniqueness]] == Relationship property uniqueness constraints @@ -348,7 +333,6 @@ A relationship property uniqueness constraint ensures that all relationships wit * xref::constraints/examples.adoc#constraints-create-a-relationship-that-violates-a-uniqueness-constraint[] * xref::constraints/examples.adoc#constraints-fail-to-create-a-uniqueness-constraint-due-to-conflicting-relationships[] - [discrete] [[constraints-create-a-relationship-uniqueness-constraints]] === Create a relationship property uniqueness constraint @@ -360,21 +344,24 @@ When creating a property uniqueness constraint, a name can be provided. ====== .Query -[source, cypher, role=test-skip] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name -FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +CREATE CONSTRAINT sequels +FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.order, sequel.seriesTitle) IS UNIQUE ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Relationship uniqueness constraints added: 1 +Added 1 constraint. ---- +[NOTE] +==== +The detailed statistics view currently says `Relationship uniqueness constraints added: 1`. +It will be updated to say `Relationship property uniqueness constraints added: 1` in Neo4j version 6.0. +==== + ====== @@ -391,23 +378,26 @@ This will ensure that no error is thrown and no constraint is created if any oth ====== .Query -[source, cypher, role=test-skip] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name IF NOT EXISTS -FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +CREATE CONSTRAINT sequels IF NOT EXISTS +FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.order) IS UNIQUE ---- -Assuming no constraint with the given name or other relationship property uniqueness constraint on the same schema exists: +Assuming a constraint with the given name already exists: .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Relationship uniqueness constraints added: 1 +(no changes, no records) ---- +[NOTE] +==== +The detailed statistics view currently says `Relationship uniqueness constraints added: 1`. +It will be updated to say `Relationship property uniqueness constraints added: 1` in Neo4j version 6.0. +==== + ====== @@ -430,24 +420,27 @@ The only valid value for the index provider is: ====== .Query -[source, cypher, role=test-skip] +[source, cypher] ---- -CREATE CONSTRAINT constraint_with_options -FOR ()-[friend:FRIENDS_WITH]-() REQUIRE (friend.nickname, friend.since) IS UNIQUE +CREATE CONSTRAINT rel_constraint_with_options +FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.order, sequel.seriesTitle, sequel.number) IS UNIQUE OPTIONS { - indexProvider: 'range-1.0', + indexProvider: 'range-1.0' } ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Relationship uniqueness constraints added: 1 +Added 1 constraint. ---- +[NOTE] +==== +The detailed statistics view currently says `Relationship uniqueness constraints added: 1`. +It will be updated to say `Relationship property uniqueness constraints added: 1` in Neo4j version 6.0. +==== + ====== There are no valid index configuration values for the constraint-backing range indexes. @@ -461,15 +454,12 @@ There are no valid index configuration values for the constraint-backing range i .+CREATE CONSTRAINT+ ====== -Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type, when that constraint already exists. - -// Set-up to get expected behavior: -// CREATE CONSTRAINT preExistingUnique FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +Create a property uniqueness constraint on the properties `order` and `seriesTitle` on relationships with the `SEQUEL_OF` relationship type, when that constraint already exists. .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +CREATE CONSTRAINT sequel_order_seriestitle FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.order, sequel.seriesTitle) IS UNIQUE ---- In this case, the constraint cannot be created because it already exists. @@ -478,9 +468,14 @@ In this case, the constraint cannot be created because it already exists. [source, error] ---- Constraint already exists: -Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', schema=()-[:FRIENDS_WITH {nickname}]-(), ownedIndex=3 ) +Constraint( id=13, name='sequels', type='RELATIONSHIP UNIQUENESS', schema=()-[:SEQUEL_OF {order, seriesTitle}]-(), ownedIndex=12 ) ---- +[NOTE] +==== +The constraint type will be updated to say `RELATIONSHIP PROPERTY UNIQUENESS` in Neo4j version 6.0. +==== + ====== @@ -492,15 +487,19 @@ Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', sche .+CREATE CONSTRAINT+ ====== -Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type, when an index already exists on that relationship type and property combination. +Create a property uniqueness constraint on the property `order` on relationships with the `SEQUEL_OF` relationship type, when an index already exists on that relationship type and property combination. -// Set-up to get expected behavior: -// CREATE INDEX FOR ()-[friend:FRIENDS_WITH]-() ON (friend.nickname) +//// +[source, cypher, role=test-setup] +---- +CREATE INDEX sequel_order FOR ()-[sequel:SEQUEL_OF]-() ON (sequel.order) +---- +//// .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +CREATE CONSTRAINT sequel_series_title FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.order) IS UNIQUE ---- In this case, the constraint cannot be created because there already exists an index covering that schema. @@ -508,7 +507,7 @@ In this case, the constraint cannot be created because there already exists an i .Error message [source, error] ---- -There already exists an index ()-[:FRIENDS_WITH {nickname}]-(). +There already exists an index ()-[:SEQUEL_OF {order}]-(). A constraint cannot be created until the index has been dropped. ---- @@ -523,27 +522,18 @@ A constraint cannot be created until the index has been dropped. .+CREATE RELATIONSHIP+ ====== -Create a `FRIENDS_WITH` relationship with an `nickname` that is not already in the database. - -// Set-up to get expected behavior: -// CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +Create a `SEQUEL_OF` relationship with values for properties `order` and `seriesTitle` that are not already in the database. .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -CREATE (:Person {name: 'Josefin'})-[:FRIENDS_WITH {nickname: 'Mimi'}]->(:Person {name: 'Emilia'}) +CREATE (:Book {title: 'Spirit Walker'})-[:SEQUEL_OF {order: 1, seriesTitle: 'Chronicles of Ancient Darkness'}]->(:Book {title: 'Wolf Brother'}) ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Nodes created: 2 -Relationships created: 1 -Properties set: 3 -Labels added: 2 +Added 2 labels, created 2 nodes, set 4 properties, created 1 relationship. ---- ====== @@ -557,17 +547,13 @@ Labels added: 2 .+CREATE RELATIONSHIP+ ====== -Create a `FRIENDS_WITH` relationship with an `nickname` that is already used in the database. - -// Set-up to get expected behavior: -// CREATE CONSTRAINT FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE -// CREATE (:Person {name: 'Emma'}), (:Person {name: 'Josefin'})-[:FRIENDS_WITH {nickname: 'Mimi'}]->(:Person {name: 'Emilia'}) +Create a `SEQUEL_OF` relationship with values for properties `order` and `seriesTitle` that are already used in the database. .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -MATCH (emma:Person {name: 'Emma'}), (emilia:Person {name: 'Emilia'}) -CREATE (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) +MATCH (wolfBrother:Book {title: 'Wolf Brother'}), (spiritWalker:Book {title: 'Spirit Walker'}) +CREATE (spiritWalker)-[:SEQUEL_OF {order: 1, seriesTitle: 'Chronicles of Ancient Darkness'}]->(wolfBrother) ---- In this case, the relationship is not created in the graph. @@ -575,7 +561,7 @@ In this case, the relationship is not created in the graph. .Error message [source, error] ---- -Relationship(0) already exists with type `FRIENDS_WITH` and property `nickname` = 'Mimi' +Relationship(0) already exists with type `SEQUEL_OF` and properties `order` = 1, `seriesTitle` = 'Chronicles of Ancient Darkness' ---- ====== @@ -589,16 +575,20 @@ Relationship(0) already exists with type `FRIENDS_WITH` and property `nickname` .+CREATE CONSTRAINT+ ====== -Create a property uniqueness constraint on the property `nickname` on relationships with the `FRIENDS_WITH` relationship type when there are two relationships with the same `nickname`. +Create a property uniqueness constraint on the property `seriesTitle` on relationships with the `SEQUEL_OF` relationship type, when two relationships with the same `seriesTitle` already exist. -// Set-up to get expected behavior: -// CREATE (emma:Person {name: 'Emma'}), (josefin:Person {name: 'Josefin'}), (emilia:Person {name: 'Emilia'}) -// CREATE (josefin)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia), (emma)-[:FRIENDS_WITH {nickname: 'Mimi'}]->(emilia) +//// +[source, cypher, role=test-setup] +---- +MATCH (spiritWalker:Book {title: 'Spirit Walker'}) +CREATE (:Book {title: 'Soul Eater'})-[:SEQUEL_OF {order: 2, seriesTitle: 'Chronicles of Ancient Darkness'}]->(spiritWalker) +---- +//// .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT friends FOR ()-[friend:FRIENDS_WITH]-() REQUIRE friend.nickname IS UNIQUE +CREATE CONSTRAINT series_title FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.seriesTitle) IS UNIQUE ---- In this case, the constraint cannot be created because it is violated by existing data. @@ -607,12 +597,25 @@ Either use xref::indexes-for-search-performance.adoc[] instead, or remove the of .Error message [source, error] ---- -Unable to create Constraint( name='friends', type='RELATIONSHIP_UNIQUENESS', schema=()-[:FRIENDS_WITH {nickname}]-() ): -Both Relationship(0) and Relationship(1) have the type `FRIENDS_WITH` and property `nickname` = 'Mimi' +Unable to create Constraint( name='series_title', type='RELATIONSHIP UNIQUENESS', schema=()-[:SEQUEL_OF {seriesTitle}]-() ): +Both Relationship(0) and Relationship(1) have the type `SEQUEL_OF` and property `seriesTitle` = 'Chronicles of Ancient Darkness' ---- ====== -//// + +The constraint creation fails on the first offending relationships that are found. +This does not guarantee that there are no other offending relationships in the data. +Therefore, all the data should be checked and cleaned up before re-attempting the constraint creation. + +This is an example `MATCH` query to find all offending relationships for the constraint above: + +.Query +[source, cypher] +---- +MATCH ()-[knows1:KNOWS]->(), ()-[knows2:KNOWS]->() +WHERE knows1.level = knows2.level AND NOT knows1 = knows2 +RETURN knows1, knows2 +---- [role=enterprise-edition] [[constraints-examples-node-property-existence]] @@ -652,14 +655,11 @@ FOR (author:Author) REQUIRE author.name IS NOT NULL Added 1 constraint. ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== -The statistics for property existence constraints will be split between nodes and relationships in Neo4j version 6.0. -For the node property existence constraints, they will say `Node property existence constraints`. +The detailed statistics view for property existence constraints, `Property existence constraints added: 1`, will be split between nodes and relationships in Neo4j version 6.0. +For the node property existence constraints, they will say `Node property existence constraints added: 1`. ==== -//// ====== @@ -679,7 +679,7 @@ This will ensure that no error is thrown and no constraint is created if any oth //// [source, cypher, role=test-setup] ---- -CREATE CONSTRAINT author_pseudonym +CREATE CONSTRAINT author_pseudonym FOR (author:Author) REQUIRE author.pseudonym IS UNIQUE ---- //// @@ -891,14 +891,11 @@ FOR ()-[wrote:WROTE]-() REQUIRE wrote.year IS NOT NULL Added 1 constraint. ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== -The statistics for property existence constraints will be split between nodes and relationships in Neo4j version 6.0. -For the relationship property existence constraints, they will say `Relationship property existence constraints`. +The detailed statistics view for property existence constraints, `Property existence constraints added: 1`, will be split between nodes and relationships in Neo4j version 6.0. +For the relationship property existence constraints, they will say `Relationship property existence constraints added: 1`. ==== -//// ====== @@ -918,7 +915,7 @@ This will ensure that no error is thrown and no constraint is created if any oth .Query [source, cypher] ---- -CREATE CONSTRAINT wrote_year IF NOT EXISTS +CREATE CONSTRAINT wrote_year IF NOT EXISTS FOR ()-[wrote:WROTE]-() REQUIRE wrote.year IS NOT NULL ---- @@ -1278,7 +1275,7 @@ There already exists an index called 'citizenship'. .+CREATE NODE+ ====== -Create an `Actor` node with a `firstname` and `surname` property. +Create an `Actor` node with `firstname` and `surname` properties. .Query [source, cypher] @@ -1399,10 +1396,7 @@ WHERE actor.born IS NULL RETURN actor, 'non-existing' AS reason ---- -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [role=enterprise-edition] -TODO: Remove role=skip-test from queries once feature is introduced [[constraints-examples-relationship-key]] == Relationship key constraints @@ -1419,7 +1413,6 @@ It also ensures that all properties in the set are present. * xref::constraints/examples.adoc#constraints-removing-a-relationship-key-constrained-property[] * xref::constraints/examples.adoc#constraints-fail-to-create-a-relationship-key-constraint-due-to-existing-relationship[] - [discrete] [[constraints-create-a-relationship-key-constraint]] === Create a relationship key constraint @@ -1431,19 +1424,16 @@ When creating a relationship key constraint, a name can be provided. ====== .Query -[source, cypher, role=test-skip] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name -FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY +CREATE CONSTRAINT knows_since_how +FOR ()-[knows:KNOWS]-() REQUIRE (knows.since, knows.how) IS RELATIONSHIP KEY ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Relationship key constraints added: 1 +Added 1 constraint. ---- ====== @@ -1461,24 +1451,19 @@ This will ensure that no error is thrown and no constraint is created if any oth .+CREATE CONSTRAINT+ ====== -// Set-up to get expected behavior: -// CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY - .Query -[source, cypher, role=test-skip] +[source, cypher] ---- -CREATE CONSTRAINT constraint_name IF NOT EXISTS -FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS RELATIONSHIP KEY +CREATE CONSTRAINT knows IF NOT EXISTS +FOR ()-[knows:KNOWS]-() REQUIRE (knows.since, knows.how) IS RELATIONSHIP KEY ---- -Assuming a relationship key constraint on `()-[:ROAD {startPoint, endPoint}]-()` already existed: +Assuming a relationship key constraint on `()-[:KNOWS {since, how}]-()` already existed: .Result [queryresult] ---- -+--------------------------------------------+ -| No data returned, and nothing was changed. | -+--------------------------------------------+ +(no changes, no records) ---- ====== @@ -1501,10 +1486,10 @@ The only valid value for the index provider is: ====== .Query -[source, cypher, role=test-skip] +[source, cypher] ---- -CREATE CONSTRAINT constraint_with_provider -FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY +CREATE CONSTRAINT rel_constraint_with_provider +FOR ()-[knows:KNOWS]-() REQUIRE (knows.since) IS REL KEY OPTIONS { indexProvider: 'range-1.0' } @@ -1513,10 +1498,7 @@ OPTIONS { .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Relationship key constraints added: 1 +Added 1 constraint. ---- ====== @@ -1532,15 +1514,19 @@ There is no valid index configuration values for the constraint-backing range in .+CREATE CONSTRAINT+ ====== -Create a relationship key constraint on the properties `startPoint` and `endPoint` on relationships with the `ROAD` relationship type, when a property uniqueness constraint already exists on the same relationship type and property combination. +Create a relationship key constraint on the property `how` on relationships with the `KNOWS` relationship type, when a property uniqueness constraint already exists on the same relationship type and property combination. -// Set-up to get expected behavior: -// CREATE CONSTRAINT preExistingUnique FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS UNIQUE +//// +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT preExisting_how FOR ()-[knows:KNOWS]-() REQUIRE (knows.how) IS UNIQUE +---- +//// .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY +CREATE CONSTRAINT knows_how FOR ()-[knows:KNOWS]-() REQUIRE (knows.how) IS REL KEY ---- In this case, the constraint cannot be created because there already exists a conflicting constraint on that relationship type and property combination. @@ -1549,9 +1535,14 @@ In this case, the constraint cannot be created because there already exists a co [source, error] ---- Constraint already exists: -Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', schema=()-[:ROAD {startPoint, endPoint}]-(), ownedIndex=3 ) +Constraint( id=34, name='preExisting_how', type='RELATIONSHIP UNIQUENESS', schema=()-[:KNOWS {how}]-(), ownedIndex=33 ) ---- +[NOTE] +==== +The constraint type for relationship property uniqueness constraints will be updated to say `RELATIONSHIP PROPERTY UNIQUENESS` in Neo4j version 6.0. +==== + ====== @@ -1563,16 +1554,20 @@ Constraint( id=4, name='preExistingUnique', type='RELATIONSHIP_UNIQUENESS', sche .+CREATE CONSTRAINT+ ====== -Create a named relationship key constraint on the property `coordinates` on relationships with the `INTERSECTION` relationship type, when an index already exists with the given name. +Create a named relationship key constraint on the property `level` on relationships with the `KNOWS` relationship type, when an index already exists with the given name. -// Set-up to get expected behavior: -// CREATE INDEX intersections FOR ()-[intersect:Roundabout]-() ON (intersect.coordinates) +//// +[source, cypher, role=test-setup] +---- +CREATE INDEX knows FOR ()-[know:KNOW]-() ON (know.levels) +---- +//// .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT intersections -FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY +CREATE CONSTRAINT knows +FOR ()-[knows:KNOWS]-() REQUIRE (knows.level) IS REL KEY ---- In this case, the constraint cannot be created because there already exists an index with the given name. @@ -1580,7 +1575,7 @@ In this case, the constraint cannot be created because there already exists an i .Error message [source, error] ---- -There already exists an index called 'intersections'. +There already exists an index called 'knows'. ---- ====== @@ -1594,27 +1589,18 @@ There already exists an index called 'intersections'. .+CREATE RELATIONSHIP+ ====== -Create a `ROAD` relationship with both a `startPoint` and `endPoint` property. - -// Set-up to get expected behavior: -// CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY -// CREATE (:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) +Create a `KNOWS` relationship with both `since` and `how` properties and a relationship key constraint on `:KNOWS(since, how)`. .Query -[source, cypher, role=test-skip] +[source, cypher] ---- -MATCH (a:Intersection {name: 'a'}), (b:Intersection {name: 'b'}) -CREATE (a)-[:ROAD {startPoint: a.coordinates, endPoint: b.coordinates}]->(b) +CREATE (:Actor {firstname: 'Jensen', surname: 'Ackles'})-[:KNOWS {since: 2008, how: 'coworkers'}]->(:Actor {firstname: 'Misha', surname: 'Collins'}) ---- .Result [queryresult] ---- -+-------------------+ -| No data returned. | -+-------------------+ -Relationships created: 1 -Properties set: 2 +Added 2 labels, created 2 nodes, set 6 properties, created 1 relationship. ---- ====== @@ -1628,17 +1614,13 @@ Properties set: 2 .+CREATE RELATIONSHIP+ ====== -Trying to create a `INTERSECTION` relationship without a `coordinates` property, given a relationship key constraint on `:INTERSECTION(coordinates)`, will fail. - -// Set-up to get expected behavior: -// CREATE CONSTRAINT FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY -// CREATE (:Road {name: 'a'}), (:Road {name: 'b'}) +Trying to create a `KNOWS` relationship without a `since` property, given a relationship key constraint on `:KNOWS(since, how)`, will fail. .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -MATCH (a:Road {name: 'a'}), (b:Road {name: 'b'}) -CREATE (a)-[:INTERSECTION]->(b) +MATCH (jensen:Actor {firstname: 'Jensen', surname: 'Ackles'}), (misha:Actor {firstname: 'Misha', surname: 'Collins'}) +CREATE (misha)-[:KNOWS {how: 'coworkers'}]->(jensen) ---- In this case, the relationship is not created in the graph. @@ -1646,7 +1628,7 @@ In this case, the relationship is not created in the graph. .Error message [source, error] ---- -Relationship(0) with type `INTERSECTION` must have the property `coordinates` +Relationship(0) already exists with type `KNOWS` and property `how` = 'coworkers' ---- ====== @@ -1660,17 +1642,12 @@ Relationship(0) with type `INTERSECTION` must have the property `coordinates` .+REMOVE PROPERTY+ ====== -Trying to remove the `endPoint` property from an existing relationship `ROAD`, given a `RELATIONSHIP KEY` constraint on `:ROAD(startPoint, endPoint)`. - -// Set-up to get expected behavior: -// CREATE CONSTRAINT FOR ()-[r:ROAD]-() REQUIRE (r.startPoint, r.endPoint) IS REL KEY -// CREATE (a:Intersection {name: 'a', coordinates: point({x: 1, y:2})}), (b:Intersection {name: 'b', coordinates: point({x: 2, y:5})}) -// CREATE (a)-[:ROAD {startPoint: a.coordinates, endPoint: b.coordinates}]->(b) +Trying to remove the `since` property from an existing relationship `KNOWS`, given a `RELATIONSHIP KEY` constraint on `:KNOWS(since, how)`. .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -MATCH ()-[r:ROAD {startPoint: point({x: 1, y:2}), endPoint: point({x: 2, y:5})}]->() REMOVE r.endPoint +MATCH ()-[knows:KNOWS {since: 2008, how: 'coworkers'}]->() REMOVE knows.since ---- In this case, the property is not removed. @@ -1678,7 +1655,7 @@ In this case, the property is not removed. .Error message [source, error] ---- -Relationship(0) with type `ROAD` must have the properties (`startPoint`, `endPoint`) +Relationship(0) with type `KNOWS` must have the properties (`since`, `how`) ---- ====== @@ -1692,17 +1669,21 @@ Relationship(0) with type `ROAD` must have the properties (`startPoint`, `endPoi .+CREATE CONSTRAINT+ ====== -Trying to create a relationship key constraint on the property `coordinates` on relationships with the `INTERSECTION` relationship type will fail when two relationships with identical `coordinates` already exists in the database. +Trying to create a relationship key constraint on the property `level` on relationships with the `KNOWS` relationship type will fail when two relationships with identical `level` property values already exist in the database. -// Set-up to get expected behavior: -// CREATE (a:Road {name: 'a'}), (b:Road {name: 'b'}) -// CREATE (a)-[:INTERSECTION {coordinates: point({x:1, y:2})}]->(b) -// CREATE (a)<-[:INTERSECTION {coordinates: point({x:1, y:2})}]-(b) +//// +[source, cypher, role=test-setup] +---- +MATCH (jensen:Actor {firstname: 'Jensen', surname: 'Ackles'})-[knows:KNOWS {since: 2008, how: 'coworkers'}]->(:Actor {firstname: 'Misha', surname: 'Collins'}) +SET knows.level = 10 +CREATE (jensen)-[:KNOWS {since: 2005, how: 'costars', level: 10}]->(:Actor {firstname: 'Jared', surname: 'Padalecki'}) +---- +//// .Query -[source, cypher, role=test-skip] +[source, cypher, role=test-fail] ---- -CREATE CONSTRAINT intersectionConstraint FOR ()-[r:INTERSECTION]-() REQUIRE (r.coordinates) IS REL KEY +CREATE CONSTRAINT knows_level FOR ()-[knows:KNOWS]-() REQUIRE (knows.level) IS REL KEY ---- In this case, the relationship key constraint cannot be created because it is violated by existing data. @@ -1711,12 +1692,31 @@ Either use xref::indexes-for-search-performance.adoc[] instead, or remove the of .Error message [source, error] ---- -Unable to create Constraint( name='intersectionConstraint', type='RELATIONSHIP KEY', schema=()-[:INTERSECTION {coordinates}]-() ): -Both Relationship(0) and Relationship(1) have the type `INTERSECTION` and property `coordinates` = {geometry: {type: "Point", coordinates: [1.0, 2.0], crs: {type: link, properties: {href: "http://spatialreference.org/ref/sr-org/7203/", code: 7203}}}} +Unable to create Constraint( name='knows_level', type='RELATIONSHIP KEY', schema=()-[:KNOWS {level}]-() ): +Both Relationship(0) and Relationship(1) have the type `KNOWS` and property `level` = 10 ---- ====== -//// + +The constraint creation fails on the first offending relationships that are found. +This does not guarantee that there are no other offending relationships in the data. +Therefore, all the data should be checked and cleaned up before re-attempting the constraint creation. + +This is an example `MATCH` query to find all offending relationships for the constraint above: + +.Query +[source, cypher] +---- +MATCH ()-[knows1:KNOWS]->(), ()-[knows2:KNOWS]->() +WHERE knows1.level = knows2.level AND NOT knows1 = knows2 +UNWIND [knows1, knows2] AS knows +RETURN knows, 'non-unique' AS reason +UNION +MATCH ()-[knows:KNOWS]->() +WHERE knows.level IS NULL +RETURN knows, 'non-existing' AS reason +---- + [[constraints-examples-drop-constraint]] == Drop a constraint by name @@ -1730,9 +1730,7 @@ Both Relationship(0) and Relationship(1) have the type `INTERSECTION` and proper === Drop a constraint A constraint can be dropped using the name with the `DROP CONSTRAINT constraint_name` command. -It is the same command for uniqueness, property existence, and node key constraints. -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//It is the same command for uniqueness, property existence, and node/relationship key constraints. +It is the same command for uniqueness, property existence, and node/relationship key constraints. The name of the constraint can be found using the xref::constraints/syntax.adoc#constraints-syntax-list[`SHOW CONSTRAINTS` command], given in the output column `name`. @@ -1759,9 +1757,7 @@ Removed 1 constraint. === Drop a non-existing constraint If it is uncertain if any constraint with a given name exists and you want to drop it if it does but not get an error should it not, use `IF EXISTS`. -It is the same command for uniqueness, property existence, and node constraints. -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//It is the same command for uniqueness, property existence, and node/relationship key constraints. +It is the same command for uniqueness, property existence, and node/relationship key constraints. .+DROP CONSTRAINT+ ====== @@ -1813,44 +1809,48 @@ SHOW CONSTRAINTS [queryresult] ---- -╒════╤════════════════════════════╤═════════════════════════════════╤══════════════╤═══════════════╤═══════════════════════╤════════════════════════════╕ -│"id"│"name" │"type" │"entityType" │"labelsOrTypes"│"properties" │"ownedIndex" │ -╞════╪════════════════════════════╪═════════════════════════════════╪══════════════╪═══════════════╪═══════════════════════╪════════════════════════════╡ -│16 │"actor_fullname" │"NODE_KEY" │"NODE" │["Actor"] │["firstname","surname"]│"actor_fullname" │ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│10 │"author_name" │"NODE_PROPERTY_EXISTENCE" │"NODE" │["Author"] │["name"] │null │ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│12 │"author_pseudonym" │"UNIQUENESS" │"NODE" │["Author"] │["pseudonym"] │"author_pseudonym" │ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│4 │"book_isbn2" │"UNIQUENESS" │"NODE" │["Book"] │["isbn2"] │"book_isbn2" │ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│6 │"constraint_with_options" │"UNIQUENESS" │"NODE" │["Book"] │["prop1","prop2"] │"constraint_with_options" │ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│18 │"constraint_with_provider" │"NODE_KEY" │"NODE" │["Actor"] │["surname"] │"constraint_with_provider" │ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│20 │"preExisting_actor_name_age"│"UNIQUENESS" │"NODE" │["Actor"] │["firstname","age"] │"preExisting_actor_name_age"│ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│8 │"preExisting_book_published"│"UNIQUENESS" │"NODE" │["Book"] │["published"] │"preExisting_book_published"│ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│14 │"wrote_locations" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["location"] │null │ -├────┼────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼───────────────────────┼────────────────────────────┤ -│13 │"wrote_year" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["year"] │null │ -└────┴────────────────────────────┴─────────────────────────────────┴──────────────┴───────────────┴───────────────────────┴────────────────────────────┘ -10 rows +╒════╤══════════════════════════════╤═════════════════════════════════╤══════════════╤═══════════════╤════════════════════════════════╤══════════════════════════════╕ +│"id"│"name" │"type" │"entityType" │"labelsOrTypes"│"properties" │"ownedIndex" │ +╞════╪══════════════════════════════╪═════════════════════════════════╪══════════════╪═══════════════╪════════════════════════════════╪══════════════════════════════╡ +│23 │"actor_fullname" │"NODE_KEY" │"NODE" │["Actor"] │["firstname","surname"] │"actor_fullname" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│16 │"author_name" │"NODE_PROPERTY_EXISTENCE" │"NODE" │["Author"] │["name"] │null │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│19 │"author_pseudonym" │"UNIQUENESS" │"NODE" │["Author"] │["pseudonym"] │"author_pseudonym" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│6 │"book_isbn2" │"UNIQUENESS" │"NODE" │["Book"] │["isbn2"] │"book_isbn2" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│8 │"constraint_with_options" │"UNIQUENESS" │"NODE" │["Book"] │["prop1","prop2"] │"constraint_with_options" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│25 │"constraint_with_provider" │"NODE_KEY" │"NODE" │["Actor"] │["surname"] │"constraint_with_provider" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│30 │"knows_since_how" │"RELATIONSHIP_KEY" │"RELATIONSHIP"│["KNOWS"] │["since","how"] │"knows_since_how" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│27 │"preExisting_actor_name_age" │"UNIQUENESS" │"NODE" │["Actor"] │["firstname","age"] │"preExisting_actor_name_age" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│10 │"preExisting_book_published" │"UNIQUENESS" │"NODE" │["Book"] │["published"] │"preExisting_book_published" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│34 │"preExisting_how" │"RELATIONSHIP_UNIQUENESS" │"RELATIONSHIP"│["KNOWS"] │["how"] │"preExisting_how" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│15 │"rel_constraint_with_options" │"RELATIONSHIP_UNIQUENESS" │"RELATIONSHIP"│["SEQUEL_OF"] │["order","seriesTitle","number"]│"rel_constraint_with_options" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│32 │"rel_constraint_with_provider"│"RELATIONSHIP_KEY" │"RELATIONSHIP"│["KNOWS"] │["since"] │"rel_constraint_with_provider"│ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│13 │"sequels" │"RELATIONSHIP_UNIQUENESS" │"RELATIONSHIP"│["SEQUEL_OF"] │["order","seriesTitle"] │"sequels" │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│21 │"wrote_locations" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["location"] │null │ +├────┼──────────────────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────────────────────────┼──────────────────────────────┤ +│20 │"wrote_year" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["year"] │null │ +└────┴──────────────────────────────┴─────────────────────────────────┴──────────────┴───────────────┴────────────────────────────────┴──────────────────────────────┘ +15 rows ---- -//// -TODO: Update the above table of constraints once the relationship key and uniqueness constraints are no longer behind a feature flag. -//// - -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [NOTE] ==== The `type` column returns `UNIQUENESS` for the node property uniqueness constraint and `RELATIONSHIP_UNIQUENESS` for the relationship property uniqueness constraint. -The `type` for node property uniqueness constraint will be updated to `NODE_UNIQUENESS` in Neo4j version 6.0. +This will be updated in Neo4j version 6.0. +Node property uniqueness constraints will be updated to `NODE_PROPERTY_UNIQUENESS` and relationship property uniqueness constraints to `RELATIONSHIP_PROPERTY_UNIQUENESS`. ==== -//// ====== @@ -1884,9 +1884,9 @@ To get all columns, use `+SHOW INDEXES YIELD * WHERE ...+`. ╒════╤═════════════════╤═════════════════════════════════╤══════════════╤═══════════════╤════════════╤════════════╕ │"id"│"name" │"type" │"entityType" │"labelsOrTypes"│"properties"│"ownedIndex"│ ╞════╪═════════════════╪═════════════════════════════════╪══════════════╪═══════════════╪════════════╪════════════╡ -│14 │"wrote_locations"│"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["location"]│null │ +│21 │"wrote_locations"│"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["location"]│null │ ├────┼─────────────────┼─────────────────────────────────┼──────────────┼───────────────┼────────────┼────────────┤ -│13 │"wrote_year" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["year"] │null │ +│20 │"wrote_year" │"RELATIONSHIP_PROPERTY_EXISTENCE"│"RELATIONSHIP"│["WROTE"] │["year"] │null │ └────┴─────────────────┴─────────────────────────────────┴──────────────┴───────────────┴────────────┴────────────┘ 2 rows ---- diff --git a/modules/ROOT/pages/constraints/index.adoc b/modules/ROOT/pages/constraints/index.adoc index e356d748a..ac1ab9687 100644 --- a/modules/ROOT/pages/constraints/index.adoc +++ b/modules/ROOT/pages/constraints/index.adoc @@ -18,13 +18,10 @@ Unique node property constraints, or node property uniqueness constraints, ensur For property uniqueness constraints on multiple properties, the combination of the property values is unique. Node property uniqueness constraints do not require all nodes to have a unique value for the properties listed (nodes without all properties are not subject to this rule). -// TODO: Re-add this part when adding back relationship key and uniqueness constraints -//// *Unique relationship property constraints*:: Unique relationship property constraints, or relationship property uniqueness constraints, ensure that property values are unique for all relationships with a specific type. For property uniqueness constraints on multiple properties, the combination of the property values is unique. Relationship property uniqueness constraints do not require all relationships to have a unique value for the properties listed (relationships without all properties are not subject to this rule). -//// *Node property existence constraints* label:enterprise-edition[]:: Node property existence constraints ensure that a property exists for all nodes with a specific label. @@ -50,8 +47,6 @@ Queries attempting to do any of the following will fail: * Remove one of the mandatory properties. * Update the properties so that the combination of property values is no longer unique. -// TODO: Re-add this part when adding back relationship key and uniqueness constraints -//// *Relationship key constraints* label:enterprise-edition[]:: Relationship key constraints ensure that, for a given type and set of properties: + @@ -65,14 +60,11 @@ Queries attempting to do any of the following will fail: * Create new relationships without all the properties or where the combination of property values is not unique. * Remove one of the mandatory properties. * Update the properties so that the combination of property values is no longer unique. -//// [NOTE] ==== -Node key constraints, node property existence constraints, and relationship property existence constraints are only available in Neo4j Enterprise Edition. -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//Node key constraints, relationship key constraints, node property existence constraints, and relationship property existence constraints are only available in Neo4j Enterprise Edition. +Node key constraints, relationship key constraints, node property existence constraints, and relationship property existence constraints are not available in Neo4j Community Edition. Databases containing one of these constraint types cannot be opened using Neo4j Community Edition. ==== @@ -81,26 +73,15 @@ Databases containing one of these constraint types cannot be opened using Neo4j Creating a constraint has the following implications on indexes: -* Adding a node key or property uniqueness constraint on a single property also adds an index on that property, and therefore, an index of the same index type, label, and property combination cannot be added separately. -* Adding a node key or property uniqueness constraint for a set of properties also adds an index on those properties, and therefore, an index of the same index type, label, and properties combination cannot be added separately. -* Cypher will use these indexes for lookups just like other indexes. - Refer to xref::indexes-for-search-performance.adoc[] for more details on indexes. -* If a node key or property uniqueness constraint is dropped and the backing index is still required, the index need to be created explicitly. - -//// -TODO: Switch the part above to the one below when adding back relationship key and uniqueness constraints * Adding a node key, relationship key, or property uniqueness constraint on a single property also adds an index on that property, and therefore, an index of the same index type, label/relationship type, and property combination cannot be added separately. * Adding a node key, relationship key, or property uniqueness constraint for a set of properties also adds an index on those properties, and therefore, an index of the same index type, label/relationship type, and properties combination cannot be added separately. * Cypher will use these indexes for lookups just like other indexes. Refer to xref::indexes-for-search-performance.adoc[] for more details on indexes. * If a node key, relationship key, or property uniqueness constraint is dropped and the backing index is still required, the index need to be created explicitly. -//// Additionally, the following is true for constraints: -* A given label can have multiple constraints, and uniqueness and property existence constraints can be combined on the same property. -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//* A given label or relationship type can have multiple constraints, and uniqueness and property existence constraints can be combined on the same property. +* A given label or relationship type can have multiple constraints, and uniqueness and property existence constraints can be combined on the same property. * Adding constraints is an atomic operation that can take a while -- all existing data has to be scanned before Neo4j DBMS can turn the constraint 'on'. * Best practice is to give the constraint a name when it is created. If the constraint is not explicitly named, it will get an auto-generated name. diff --git a/modules/ROOT/pages/constraints/syntax.adoc b/modules/ROOT/pages/constraints/syntax.adoc index 76909c2fe..75cf3d014 100644 --- a/modules/ROOT/pages/constraints/syntax.adoc +++ b/modules/ROOT/pages/constraints/syntax.adoc @@ -56,8 +56,6 @@ REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS [NODE] UNIQUE Index provider can be specified using the `OPTIONS` clause. -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [[constraints-syntax-create-rel-unique]] [discrete] === Create a relationship property uniqueness constraint @@ -81,7 +79,6 @@ REQUIRE (r.propertyName_1, ..., r.propertyName_n) IS [REL[ATIONSHIP]] UNIQUE ---- Index provider can be specified using the `OPTIONS` clause. -//// [[constraints-syntax-create-node-exists]] @@ -149,8 +146,6 @@ REQUIRE (n.propertyName_1, ..., n.propertyName_n) IS [NODE] KEY Index provider can be specified using the `OPTIONS` clause. -//// -TODO: Re-add this part when adding back relationship key and uniqueness constraints [[constraints-syntax-create-rel-key]] [discrete] === Create a relationship key constraint label:enterprise-edition[] @@ -174,7 +169,6 @@ REQUIRE (r.propertyName_1, ..., r.propertyName_n) IS [REL[ATIONSHIP]] KEY ---- Index provider can be specified using the `OPTIONS` clause. -//// [[constraints-syntax-drop]] @@ -208,21 +202,6 @@ Listing constraints requires the xref::administration/access-control/database-ad The simple version of the command allows for a `WHERE` clause and will give back the default set of output columns: -[source, syntax, role="noheader", indent=0] ----- -SHOW [ - ALL - |UNIQUE[NESS] - |NODE [PROPERTY] EXIST[ENCE] - |REL[ATIONSHIP] [PROPERTY] EXIST[ENCE] - |[PROPERTY] EXIST[ENCE] - |NODE KEY -] CONSTRAINT[S] - [WHERE expression] ----- - -//// -TODO: Switch the syntax above to the one below when adding back relationship key and uniqueness constraints [source, syntax, role="noheader", indent=0] ---- SHOW [ @@ -239,27 +218,9 @@ SHOW [ ] CONSTRAINT[S] [WHERE expression] ---- -//// To get the full set of output columns, a yield clause is needed: -[source, syntax, role="noheader", indent=0] ----- -SHOW [ - ALL - |UNIQUE[NESS] - |NODE [PROPERTY] EXIST[ENCE] - |REL[ATIONSHIP] [PROPERTY] EXIST[ENCE] - |[PROPERTY] EXIST[ENCE] - |NODE KEY -] CONSTRAINT[S] -YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] - [WHERE expression] - [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ----- - -//// -TODO: Switch the syntax above to the one below when adding back relationship key and uniqueness constraints [source, syntax, role="noheader", indent=0] ---- SHOW [ @@ -278,7 +239,6 @@ YIELD { * | field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- -//// The type filtering keywords filters the returned constraints on constraint type: @@ -293,18 +253,14 @@ The type filtering keywords filters the returned constraints on constraint type: | Returns all constraints, no filtering on constraint type. This is the default if none is given. -// TODO: Re-add these parts when adding back relationship key and uniqueness constraints -//|NODE UNIQUE[NESS] -//| Returns the node property uniqueness constraints. +|NODE UNIQUE[NESS] +| Returns the node property uniqueness constraints. -//|REL[ATIONSHIP] UNIQUE[NESS] -//| Returns the relationship property uniqueness constraints. +|REL[ATIONSHIP] UNIQUE[NESS] +| Returns the relationship property uniqueness constraints. |UNIQUE[NESS] -| Returns all property uniqueness constraints. -// TODO: Switch the part above to the one below when adding back relationship key and uniqueness constraints -//|UNIQUE[NESS] -//| Returns all property uniqueness constraints, for both nodes and relationships. +| Returns all property uniqueness constraints, for both nodes and relationships. |NODE [PROPERTY] EXIST[ENCE] | Returns the node property existence constraints. @@ -318,12 +274,11 @@ This is the default if none is given. |NODE KEY | Returns the node key constraints. -// TODO: Re-add these parts when adding back relationship key and uniqueness constraints -//|REL[ATIONSHIP] KEY -//| Returns the relationship key constraints. +|REL[ATIONSHIP] KEY +| Returns the relationship key constraints. -//|KEY -//| Returns all node and relationship key constraints. +|KEY +| Returns all node and relationship key constraints. |=== @@ -344,9 +299,7 @@ The returned columns from the show command is: | STRING | type -| The ConstraintType of this constraint (`UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, or `NODE_KEY`). label:default-output[] -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//| The ConstraintType of this constraint (`UNIQUENESS` (node uniqueness), `RELATIONSHIP_UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, `NODE_KEY`, or `RELATIONSHIP_KEY`). label:default-output[] +| The ConstraintType of this constraint (`UNIQUENESS` (node uniqueness), `RELATIONSHIP_UNIQUENESS`, `NODE_PROPERTY_EXISTENCE`, `RELATIONSHIP_PROPERTY_EXISTENCE`, `NODE_KEY`, or `RELATIONSHIP_KEY`). label:default-output[] | STRING | entityType diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 15348e1c3..939578d1a 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -36,6 +36,7 @@ a| The Cypher query option `connectComponentsPlanner` is deprecated and will be removed without a replacement. The product's default behavior of using a cost-based IDP search algorithm when combining sub-plans will be kept. + |=== @@ -54,8 +55,44 @@ label:updated[] ALTER DATABASE ... [WAIT [n [SEC[OND[S]]]]\|NOWAIT] ---- a| -New sub-clause `WAIT` for `ALTER DATABASE`. -This enables adding a waiting clause to specify a time limit in which the command must be completed and returned. +New sub-clause `WAIT` for `ALTER DATABASE`. +This enables adding a waiting clause to specify a time limit in which the command must be completed and returned. + +a| +label:functionality[] +label:new[] +[source, cypher, role="noheader"] +---- +CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS UNIQUE + +CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS RELATIONSHIP KEY +---- +a| + +Added relationship xref:constraints/syntax.adoc#constraints-syntax-create-rel-key[key] and xref:constraints/syntax.adoc#constraints-syntax-create-rel-unique[uniqueness] constraints. + +a| +label:functionality[] +label:new[] +[source, cypher, role="noheader"] +---- +SHOW NODE UNIQUE[NESS] CONSTRAINTS + +SHOW REL[ATIONSHIP] UNIQUE[NESS] CONSTRAINTS + +SHOW UNIQUE[NESS] CONSTRAINTS + +SHOW REL[ATIONSHIP] KEY CONSTRAINTS + +SHOW KEY CONSTRAINTS +---- +a| + +Added filtering for the new constraint types to `SHOW CONSTRAINTS`. +Includes filtering for the node part, relationship part, or both parts of each type (`NODE KEY` filtering already exists previously). + +The existing `UNIQUENESS` filter will now return both node and relationship property uniqueness constraints. + |=== [[cypher-deprecations-additions-removals-5.6]] @@ -164,32 +201,6 @@ RETURN 'val' as one, 'val' as two [[cypher-deprecations-additions-removals-5.3]] == Version 5.3 -// TODO: Re-add this part in whatever version (not 5.3...) the relationship key and uniqueness constraints are added back in -//// -=== Deprecated features - -[cols="2", options="header"] -|=== -| Feature -| Details - -a| -//not sure what category this should be, it is more information about a coming breaking change than actual deprecation -label:returnValues[] -label:deprecated[] -[source, cypher, role="noheader"] ----- -SHOW NODE UNIQUENESS CONSTRAINTS YIELD type ----- -a| - -The current constraint type for node property uniqueness constraints, `UNIQUENESS`, will be updated to `NODE_UNIQUENESS` in Neo4j version 6.0. - -This will also be reflected in updates to some error messages and query statistics. - -|=== -//// - === Updated features [cols="2", options="header"] @@ -252,53 +263,6 @@ The property uniqueness constraint type filter now allow both `UNIQUE` and `UNIQ |=== -// TODO: Re-add this part in whatever version (not 5.3...) the relationship key and uniqueness constraints are added back in -//// -=== New features - -[cols="2", options="header"] -|=== -| Feature -| Details - -a| -label:functionality[] -label:new[] -[source, cypher, role="noheader"] ----- -CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS UNIQUE - -CREATE CONSTRAINT name FOR ()-[r:TYPE]-() REQUIRE r.prop IS RELATIONSHIP KEY ----- -a| - -Added relationship xref:constraints/syntax.adoc#constraints-syntax-create-rel-key[key] and xref:constraints/syntax.adoc#constraints-syntax-create-rel-unique[uniqueness] constraints. - -a| -label:functionality[] -label:new[] -[source, cypher, role="noheader"] ----- -SHOW NODE UNIQUE[NESS] CONSTRAINTS - -SHOW REL[ATIONSHIP] UNIQUE[NESS] CONSTRAINTS - -SHOW UNIQUE[NESS] CONSTRAINTS - -SHOW REL[ATIONSHIP] KEY CONSTRAINTS - -SHOW KEY CONSTRAINTS ----- -a| - -Added filtering for the new constraint types to `SHOW CONSTRAINTS`. -Includes filtering for the node part, relationship part, or both parts of each type (`NODE KEY` filtering already exists previously). - -The existing `UNIQUENESS` filter will now return both node and relationship property uniqueness constraints. - -|=== -//// - [[cypher-deprecations-additions-removals-5.2]] == Version 5.2 diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index bb4e40ede..5cd266dbd 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -5022,9 +5022,7 @@ This constraint can have any of the available constraint types: * Property uniqueness constraints * Property existence constraints label:enterprise-edition[] -* Node key constraints label:enterprise-edition[] -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//* Node or relationship key constraints label:enterprise-edition[] +* Node or relationship key constraints label:enterprise-edition[] The following query will create a property uniqueness constraint with the name `uniqueness` on the `name` property of nodes with the `Country` label. diff --git a/modules/ROOT/pages/introduction/cypher_neo4j.adoc b/modules/ROOT/pages/introduction/cypher_neo4j.adoc index 02a0ba62a..10ee3b16f 100644 --- a/modules/ROOT/pages/introduction/cypher_neo4j.adoc +++ b/modules/ROOT/pages/introduction/cypher_neo4j.adoc @@ -32,22 +32,12 @@ a| All constraints: xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], -xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], and -xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints]. -//// -TODO: Switch the part above to the one below when adding back relationship key and uniqueness constraints -All constraints: -xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], -xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship property uniqueness constraints], xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints], and xref::constraints/examples.adoc#constraints-examples-relationship-key[relationship key constraints]. -//// a| -Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints]. -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node] and xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship] property uniqueness constraints. +Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node] and xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship] property uniqueness constraints. |=== diff --git a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc index 5432dbbca..3cda77131 100644 --- a/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc +++ b/modules/ROOT/pages/introduction/neo4j-databases-graphs.adoc @@ -78,22 +78,12 @@ a| All constraints: xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], -xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], and -xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints]. -//// -TODO: Switch the part above to the one below when adding back relationship key and uniqueness constraints -All constraints: -xref::constraints/examples.adoc#constraints-examples-node-property-existence[node existence constraints], -xref::constraints/examples.adoc#constraints-examples-relationship-property-existence[relationship existence constraints], xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints], xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship property uniqueness constraints], xref::constraints/examples.adoc#constraints-examples-node-key[node key constraints], and xref::constraints/examples.adoc#constraints-examples-relationship-key[relationship key constraints]. -//// a| -Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node property uniqueness constraints]. -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node] and xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship] property uniqueness constraints. +Only xref::constraints/examples.adoc#constraints-examples-node-uniqueness[node] and xref::constraints/examples.adoc#constraints-examples-relationship-uniqueness[relationship] property uniqueness constraints. |=== diff --git a/modules/ROOT/pages/keyword-glossary.adoc b/modules/ROOT/pages/keyword-glossary.adoc index 2b43d610d..c95f60ddd 100644 --- a/modules/ROOT/pages/keyword-glossary.adoc +++ b/modules/ROOT/pages/keyword-glossary.adoc @@ -54,19 +54,17 @@ Typically used when modifying or importing large amounts of data. | Schema | Create a constraint that ensures all nodes with a particular label have all the specified properties and that the combination of property values is unique; i.e. ensures existence and uniqueness. -// TODO: Re-add this part when adding back relationship key and uniqueness constraints -//| xref::constraints/syntax.adoc#constraints-syntax-create-rel-key[CREATE CONSTRAINT [rel_key\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] KEY [OPTIONS {optionKey: optionValue[, ...\]}\]] -//| Schema -//| Create a constraint that ensures all relationships with a particular type have all the specified properties and that the combination of property values is unique; i.e. ensures existence and uniqueness. +| xref::constraints/syntax.adoc#constraints-syntax-create-rel-key[CREATE CONSTRAINT [rel_key\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] KEY [OPTIONS {optionKey: optionValue[, ...\]}\]] +| Schema +| Create a constraint that ensures all relationships with a particular type have all the specified properties and that the combination of property values is unique; i.e. ensures existence and uniqueness. | xref::constraints/syntax.adoc#constraints-syntax-create-node-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR (n:Label) REQUIRE (n.prop1[, ..., n.propN\]) IS [NODE\] UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema | Create a constraint that ensures the uniqueness of the combination of node label and property values for a particular property key combination across all nodes. -// TODO: Re-add this part when adding back relationship key and uniqueness constraints -//| xref::constraints/syntax.adoc#constraints-syntax-create-rel-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] -//| Schema -//| Create a constraint that ensures the uniqueness of the combination of relationship type and property values for a particular property key combination across all relationships. +| xref::constraints/syntax.adoc#constraints-syntax-create-rel-unique[CREATE CONSTRAINT [uniqueness\] [IF NOT EXISTS\] FOR ()-"["r:REL_TYPE"\]"-() REQUIRE (r.prop1[, ..., r.propN\]) IS [REL[ATIONSHIP\]\] UNIQUE [OPTIONS {optionKey: optionValue[, ...\]}\]] +| Schema +| Create a constraint that ensures the uniqueness of the combination of relationship type and property values for a particular property key combination across all relationships. | xref::indexes-for-full-text-search.adoc[CREATE FULLTEXT INDEX [name\] [IF NOT EXISTS\] FOR (n:Label["\|" ... "\|" LabelN\]) ON EACH "[" n.property[, ..., n.propertyN\] "\]" [OPTIONS {optionKey: optionValue[, ...\]}\]] | Schema @@ -178,9 +176,7 @@ Either the pattern already exists, or it needs to be created. | Writing | Update labels on nodes and properties on nodes and relationships. -| xref::constraints/syntax.adoc#constraints-syntax-list[SHOW [ALL\|UNIQUE[NESS\]\|NODE [PROPERTY\] EXIST[ENCE\]\|REL[ATIONSHIP\] [PROPERTY\] EXIST[ENCE\]\|[PROPERTY\] EXIST[ENCE\]\|NODE KEY\] CONSTRAINT[S\]] -// TODO: Switch the row above to the one below when adding back relationship key and uniqueness constraints -//| xref::constraints/syntax.adoc#constraints-syntax-list[SHOW [ALL\|NODE UNIQUE[NESS\]\|REL[ATIONSHIP\] UNIQUE[NESS\]\|UNIQUE[NESS\]\|NODE [PROPERTY\] EXIST[ENCE\]\|REL[ATIONSHIP\] [PROPERTY\] EXIST[ENCE\]\|[PROPERTY\] EXIST[ENCE\]\|NODE KEY\|REL[ATIONSHIP\] KEY\|KEY\] CONSTRAINT[S\]] +| xref::constraints/syntax.adoc#constraints-syntax-list[SHOW [ALL\|NODE UNIQUE[NESS\]\|REL[ATIONSHIP\] UNIQUE[NESS\]\|UNIQUE[NESS\]\|NODE [PROPERTY\] EXIST[ENCE\]\|REL[ATIONSHIP\] [PROPERTY\] EXIST[ENCE\]\|[PROPERTY\] EXIST[ENCE\]\|NODE KEY\|REL[ATIONSHIP\] KEY\|KEY\] CONSTRAINT[S\]] | Schema | List constraints in the database, either all or filtered on type. From c180585deee1962b73514594745f75c504109e97 Mon Sep 17 00:00:00 2001 From: Gem Lamont <106068376+gem-neo4j@users.noreply.github.com> Date: Wed, 19 Apr 2023 11:08:44 +0200 Subject: [PATCH 178/383] Add a clearer deprecation message for replacing the user of paths in the size() function. (#515) Size() cannot accept a path anymore (as of 4.0), but can take pattern comprehension and could now be replaced with 5.0 feature, COUNT. This PR makes this replacement clearer to the users. --- ...precations-additions-removals-compatibility.adoc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 939578d1a..22cd2acdc 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -3271,7 +3271,18 @@ label:restricted[] size() ---- a| -No longer works for paths. Only works for strings, lists and pattern expressions. See xref:functions/scalar.adoc#functions-size[size()] for more details. +Only works for strings, lists and pattern comprehensions, and no longer works for paths. +For versions above 5.0, use a `COUNT` expression instead: +[source, cypher, role="noheader"] +---- +RETURN COUNT { (a)-[]->(b) } +---- +For versions below 5.0, use a pattern comprehension instead: +[source, cypher, role="noheader"] +---- +RETURN size([ (a)-[]->(b) \| a ]) +---- +See xref:functions/scalar.adoc#functions-size[size()] and xref:syntax/expressions.adoc#count-subqueries[Count Subqueries] for more details. |=== === Updated features From 1332ffcf561557c1357de80bfd50e93dd64832a7 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Wed, 19 Apr 2023 14:57:51 +0100 Subject: [PATCH 179/383] Add edit url (#501) --- preview.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/preview.yml b/preview.yml index 667419199..2976e6f10 100644 --- a/preview.yml +++ b/preview.yml @@ -7,6 +7,7 @@ content: sources: - url: ./ branches: ['HEAD'] + edit_url: https://github.com/neo4j/docs-cypher/tree/{refname}/{path} exclude: - '!**/_includes/*' - '!**/readme.adoc' From 44e7b1e5f848b0a71b7bb59f2138112dc54b8e88 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Wed, 19 Apr 2023 15:03:15 +0100 Subject: [PATCH 180/383] Update readme (#498) The main purpose of this PR is to test and investigate build triggers --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index c7c7470b0..ed671a939 100644 --- a/README.adoc +++ b/README.adoc @@ -70,4 +70,4 @@ There are a few edge cases where we might want to work only on the current branc ** Open a draft PR against `dev`, with a label for the specific version it is targeting - this can be shared & reviewed in draft mode, but will not be mergeable until it is pointed at the next release. ** Create a feature branch from `dev`, to be merged into `dev` when appropriate. * When a new version is ready to published, the `5.x` branch will get a git tag, named with the exact version (for example, **5.1.0**), signifying that this point-in-time marks the completion of the work for that minor release. -* The `dev` branch, with all merged content for the next release, will then be merged +* Updates merged into the `dev` branch for the next release are cherry-picked into the `5.x` branch. From 895993c52144af5db322a12dd88a58e59b6f7ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 21 Apr 2023 15:18:33 +0200 Subject: [PATCH 181/383] Add 5.8 to antora.yml (#520) --- antora.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antora.yml b/antora.yml index 47c896cc7..b8e8b46f9 100644 --- a/antora.yml +++ b/antora.yml @@ -7,5 +7,5 @@ nav: asciidoc: attributes: neo4j-version: '5' - neo4j-version-minor: '5.7' - neo4j-version-exact: '5.7.0' + neo4j-version-minor: '5.8' + neo4j-version-exact: '5.8.0' From 0c5ec22426bddd7fa0bb0bb7c39deb7004712859 Mon Sep 17 00:00:00 2001 From: Gustav Hedengran Date: Mon, 24 Apr 2023 16:47:46 +0200 Subject: [PATCH 182/383] Add some initial documentation for query caches (#521) Co-authored-by: Reneta Popova Co-authored-by: Satia Herfert --- modules/ROOT/content-nav.adoc | 3 ++ modules/ROOT/pages/query-caches/index.adoc | 42 +++++++++++++++++++ .../query-caches/unified-query-caches.adoc | 37 ++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 modules/ROOT/pages/query-caches/index.adoc create mode 100644 modules/ROOT/pages/query-caches/unified-query-caches.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 5aa713e0a..10d1ccb19 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -85,6 +85,9 @@ ** xref:execution-plans/operators.adoc[] ** xref:execution-plans/shortestpath-planning.adoc[] +* xref:query-caches/index.adoc[] +** xref:query-caches/unified-query-caches.adoc[] + * xref:administration/index.adoc[] ** xref:administration/databases.adoc[] ** xref:administration/aliases.adoc[] diff --git a/modules/ROOT/pages/query-caches/index.adoc b/modules/ROOT/pages/query-caches/index.adoc new file mode 100644 index 000000000..b92b2973c --- /dev/null +++ b/modules/ROOT/pages/query-caches/index.adoc @@ -0,0 +1,42 @@ +:description: Different caches to speed up query planning. +[[query-caches]] += Query caches + +From Neo4j 5.7 onwards, you can use several different caches to speed up Cypher query planning. + +Out of the box, the set of query caches is _per database_. +That means that a new set of caches is initialized for each new database. + +The maximum number of entries per cache is configured using `server.memory.query_cache.per_db_cache_num_entries`. +It determines the cache size only when `server.memory.query_cache.sharing_enabled` is set to `false`. + +Query caches may consume a lot of memory, especially when running many active databases. +To tackle this and improve predictability on memory consumption, you can configure the DBMS to use only one set of caches for all databases. +For more information, see xref:query-caches/unified-query-caches.adoc[Unifying query caches]. + +[[configure-caches]] +== Configure caches + +The following is a summary of the query cache configurations. +For more information, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings/[Operations Manual -> Configuration settings]. + +.Query cache configurations +[options="header", width="100%", cols="4m,3a,1m"] +|=== +| Setting +| Description +| Default + +| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings/#config_server.memory.query_cache.sharing_enabled[server.memory.query_cache.sharing_enabled] +| label:enterprise-edition[Enterprise only] Enable sharing cache space between different databases. With this option turned on, databases will share cache space, but not cache entries. +| false + +| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings/#config_server.memory.query_cache.shared_cache_num_entries[server.memory.query_cache.shared_cache_num_entries] +|label:enterprise-edition[Enterprise only] The number of cached queries for all databases. This setting is only deciding cache size when `server.memory.query_cache.sharing_enabled` is set to `true`. +| 1000 + +| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings/#config_server.memory.query_cache.per_db_cache_num_entries[server.memory.query_cache.per_db_cache_num_entries] +| The number of cached queries per database. +This setting is only deciding cache size when `server.memory.query_cache.sharing_enabled` is set to `false`. +| 1000 +|=== \ No newline at end of file diff --git a/modules/ROOT/pages/query-caches/unified-query-caches.adoc b/modules/ROOT/pages/query-caches/unified-query-caches.adoc new file mode 100644 index 000000000..63a35c4e2 --- /dev/null +++ b/modules/ROOT/pages/query-caches/unified-query-caches.adoc @@ -0,0 +1,37 @@ +:description: How to enable sharing of cache space and unifying query caches across databases. +[role=enterprise-edition] +[[unifying-query-caches]] += Unifying query caches + +To enable the unified query caches, set the option `server.memory.query_cache.sharing_enabled=true`. + +.Unified query cache configurations +[options="header", width="100%", cols="4m,3a,1m"] +|=== +| Setting +| Description +| Default + +| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings/#config_server.memory.query_cache.sharing_enabled[server.memory.query_cache.sharing_enabled] +| label:enterprise-edition[Enterprise only] Enable sharing cache space between different databases. With this option turned on, databases will share cache space, +but not cache entries. +| false + +| link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/configuration-settings/#config_server.memory.query_cache.shared_cache_num_entries[server.memory.query_cache.shared_cache_num_entries] +| label:enterprise-edition[Enterprise only] The number of cached queries for all databases. This setting is only deciding cache size when +`server.memory.query_cache.sharing_enabled` is set to `true`. +| 1000 +|=== + +When this feature is enabled, all databases use only one set of query caches. +A database may store and retrieve entries from the shared cache, but it may not retrieve entries produced by another database. + +[IMPORTANT] +==== +While databases use the same set of caches, a database may not observe entries originating from other databases. +==== + +The database may, however, evict entries from other databases as necessary, according to the constrained cache size and cache eviction policy. +In essence, databases may compete for cache space, but may not observe each other's entries. + +When this option is turned on, the cache space available to all databases is configured using the setting `server.memory.query_cache.shared_cache_num_entries`. From 80be7d4c0303c05060d9a01e2b1b3ebb88d4ce05 Mon Sep 17 00:00:00 2001 From: Gustav Hedengran Date: Tue, 25 Apr 2023 13:45:51 +0200 Subject: [PATCH 183/383] Fix Query caches page (#524) --- modules/ROOT/pages/query-caches/index.adoc | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/ROOT/pages/query-caches/index.adoc b/modules/ROOT/pages/query-caches/index.adoc index b92b2973c..3bfa26adb 100644 --- a/modules/ROOT/pages/query-caches/index.adoc +++ b/modules/ROOT/pages/query-caches/index.adoc @@ -2,8 +2,6 @@ [[query-caches]] = Query caches -From Neo4j 5.7 onwards, you can use several different caches to speed up Cypher query planning. - Out of the box, the set of query caches is _per database_. That means that a new set of caches is initialized for each new database. From 434cd9ccfaf31b1c1ce8b6a6995c7feb411782f2 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Fri, 28 Apr 2023 09:22:50 +0200 Subject: [PATCH 184/383] Document new statistics columns for `SHOW INDEXES` (#512) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also added to the cheat sheet in: https://github.com/neo4j/docs-cheat-sheet/pull/79 --------- Co-authored-by: Lasse Heemann Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- ...ions-additions-removals-compatibility.adoc | 27 +++++++ .../pages/indexes-for-search-performance.adoc | 74 ++++++++++++------- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 22cd2acdc..a39863b38 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -10,6 +10,33 @@ New features are added to the language continuously, and occasionally, some feat This section lists all of the features that have been removed, deprecated, added, or extended in different Cypher versions. Replacement syntax for deprecated and removed features are also indicated. +[[cypher-deprecations-additions-removals-5.8]] +== Version 5.8 + +=== Updated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +SHOW INDEXES +---- +a| +Introduced `lastRead`, `readCount`, and `trackedSince` columns. +Both `lastRead` and `readCount` are returned by default. + +The `lastRead` column returns the last time the index was used for reading. +The `readCount` column returns the number of read queries that have been issued to this index. +The `trackedSince` column returns the time when usage statistics tracking started for this index. + +|=== + [[cypher-deprecations-additions-removals-5.7]] == Version 5.7 diff --git a/modules/ROOT/pages/indexes-for-search-performance.adoc b/modules/ROOT/pages/indexes-for-search-performance.adoc index a3e1bbd01..11beac547 100644 --- a/modules/ROOT/pages/indexes-for-search-performance.adoc +++ b/modules/ROOT/pages/indexes-for-search-performance.adoc @@ -55,6 +55,7 @@ Using the keyword `IF NOT EXISTS` makes the command idempotent, and no error wil For a brief overview of the syntax, for all the index commands, see xref::indexes-for-search-performance.adoc#indexes-syntax[]. + [[indexes-create-indexes]] == +CREATE INDEX+ @@ -662,6 +663,22 @@ This command will produce a table with the following columns: | The name of the constraint the index is associated with or `null` if the index is not associated with any constraint. label:default-output[] | `STRING` +// New in 5.8 +| `lastRead` +| The last time the index was used for reading. +Returns `null` if the index has not been read since `trackedSince`, or if the statistics are not tracked. label:default-output[] +| `DATETIME` + +// New in 5.8 +| `readCount` +| The number of read queries that have been issued to this index since `trackedSince`, or `null` if the statistics are not tracked. label:default-output[] +| `INTEGER` + +// New in 5.8 +| `trackedSince` +| The time when usage statistics tracking started for this index, or `null` if the statistics are not tracked. +| `DATETIME` + | `options` | The options passed to `CREATE` command. | `MAP` @@ -710,29 +727,30 @@ SHOW INDEXES // SHOW INDEXES default outputs // 4.4: id, name, state, populationPercent, uniqueness, type, entityType, labelsOrTypes, properties, indexProvider // 5.0: id, name, state, populationPercent, type, entityType, labelsOrTypes, properties, indexProvider, owningConstraint +// 5.8: id, name, state, populationPercent, type, entityType, labelsOrTypes, properties, indexProvider, owningConstraint, lastRead, readCount .Result [queryresult] ---- -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | -| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | -| 13 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | -| 14 | "indexOnBooks" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Label1"] | ["prop1"] | "text-2.0" | NULL | -| 11 | "node_label_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "NODE" | NULL | NULL | "token-lookup-1.0" | NULL | -| 8 | "node_point_index_name" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Person"] | ["sublocation"] | "point-1.0" | NULL | -| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | -| 5 | "node_text_index_nickname" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["nickname"] | "text-2.0" | NULL | -| 10 | "point_index_with_config" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop2"] | "point-1.0" | NULL | -| 9 | "rel_point_index_name" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["intersection"] | "point-1.0" | NULL | -| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | -| 6 | "rel_text_index_name" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["KNOWS"] | ["interest"] | "text-2.0" | NULL | -| 12 | "rel_type_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "RELATIONSHIP" | NULL | NULL | "token-lookup-1.0" | NULL | -| 7 | "text_index_with_indexprovider" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "text-2.0" | NULL | -| 15 | "uniqueBookIsbn" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["isbn"] | "range-1.0" | "uniqueBookIsbn" | -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | NULL | 0 | +| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | 2023-03-13T11:41:44.537Z | 1 | +| 13 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | 2023-04-10T15:41:44.537Z | 2 | +| 14 | "indexOnBooks" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Label1"] | ["prop1"] | "text-2.0" | NULL | NULL | 0 | +| 11 | "node_label_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "NODE" | NULL | NULL | "token-lookup-1.0" | NULL | 2023-04-13T08:11:15.537Z | 10 | +| 8 | "node_point_index_name" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Person"] | ["sublocation"] | "point-1.0" | NULL | 2023-04-05T16:21:44.692Z | 1 | +| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | 2022-12-30T02:01:44.537Z | 6 | +| 5 | "node_text_index_nickname" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["nickname"] | "text-2.0" | NULL | 2023-04-13T11:41:44.537Z | 2 | +| 10 | "point_index_with_config" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop2"] | "point-1.0" | NULL | NULL | 0 | +| 9 | "rel_point_index_name" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["intersection"] | "point-1.0" | NULL | 2023-03-03T13:37:42.537Z | 2 | +| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | 2023-04-12T10:41:44.692Z | 5 | +| 6 | "rel_text_index_name" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["KNOWS"] | ["interest"] | "text-2.0" | NULL | 2023-04-01T10:40:44.537Z | 3 | +| 12 | "rel_type_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "RELATIONSHIP" | NULL | NULL | "token-lookup-1.0" | NULL | 2023-04-12T21:41:44.537Z | 7 | +| 7 | "text_index_with_indexprovider" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "text-2.0" | NULL | NULL | 0 | +| 15 | "uniqueBookIsbn" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["isbn"] | "range-1.0" | "uniqueBookIsbn" | 2023-04-13T11:41:44.692Z | 6 | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 15 rows ---- @@ -762,15 +780,15 @@ SHOW RANGE INDEXES WHERE owningConstraint IS NULL .Result [queryresult] ---- -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | -| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | -| 13 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | -| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | -| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | -+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | NULL | 0 | +| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | 2023-03-13T11:41:44.537Z | 1 | +| 13 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | 2023-04-10T15:41:44.537Z | 2 | +| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | 2022-12-30T02:01:44.537Z | 6 | +| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | 2023-04-12T10:41:44.692Z | 5 | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 5 rows ---- From 86c5c025db90062dd49f471f584d914ff48bb9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastien=20Lou=C3=ABrat?= Date: Fri, 28 Apr 2023 11:13:20 +0100 Subject: [PATCH 185/383] Add documentation for `AssertSameRelationship` logical plan --- .../execution-plans/operator-summary.adoc | 8 +- .../ROOT/pages/execution-plans/operators.adoc | 92 +++++++++++++++---- 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/modules/ROOT/pages/execution-plans/operator-summary.adoc b/modules/ROOT/pages/execution-plans/operator-summary.adoc index b7336675b..a76f74f4f 100644 --- a/modules/ROOT/pages/execution-plans/operator-summary.adoc +++ b/modules/ROOT/pages/execution-plans/operator-summary.adoc @@ -53,7 +53,13 @@ Tests for the absence of a pattern predicate. | | xref::execution-plans/operators.adoc#query-plan-assert-same-node[AssertSameNode] -| Used to ensure that no property uniqueness constraints are violated. +| Ensures that no node property uniqueness constraints are violated. +| +| +| + +| xref::execution-plans/operators.adoc#query-plan-assert-same-relationship[AssertSameRelationship] +| Ensures that no relationship property uniqueness constraints are violated. | | | diff --git a/modules/ROOT/pages/execution-plans/operators.adoc b/modules/ROOT/pages/execution-plans/operators.adoc index 5cd266dbd..88bdb919f 100644 --- a/modules/ROOT/pages/execution-plans/operators.adoc +++ b/modules/ROOT/pages/execution-plans/operators.adoc @@ -49,21 +49,21 @@ CREATE (me:Person {name: 'me'}), (executive:Team {name: 'Team Executive'}), (remoting:Team {name: 'Team Remoting'}), (other:Team {name: 'Other'}), - (me)-[:WORKS_IN {duration: 190, title: 'senior sales engineer'}]->(london), - (bob)-[:WORKS_IN {duration: 187, title: 'junior developer'}]->(london), - (andy)-[:WORKS_IN {duration: 150, title: ''}]->(london), - (mattias)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(london), - (lovis)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(sf), - (pontus)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(malmo), - (max)-[:WORKS_IN {duration: 230, title: 'field engineer'}]->(newyork), - (konstantin)-[:WORKS_IN {duration: 230, title: 'frontend developer'}]->(london), - (stefan)-[:WORKS_IN {duration: 230, title: 'chief architect'}]->(london), - (stefan)-[:WORKS_IN {duration: 230, title: 'language architect'}]->(berlin), - (mats)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(malmo), - (petra)-[:WORKS_IN {duration: 230, title: 'language architect'}]->(london), - (craig)-[:WORKS_IN {duration: 230, title: 'senior developer'}]->(malmo), - (steven)-[:WORKS_IN {duration: 230, title: 'junior developer'}]->(malmo), - (chris)-[:WORKS_IN {duration: 230, title: 'field engineer'}]->(madrid), + (me)-[:WORKS_IN {id: 0, duration: 190, title: 'senior sales engineer', badgeNumber: 4332}]->(london), + (bob)-[:WORKS_IN {id: 1, duration: 187, title: 'junior developer', badgeNumber: 3293}]->(london), + (andy)-[:WORKS_IN {id: 2, duration: 150, title: ''}]->(london), + (mattias)-[:WORKS_IN {id: 3, duration: 230, title: 'senior developer', badgeNumber: 7627}]->(london), + (lovis)-[:WORKS_IN {id: 4, duration: 230, title: 'junior developer'}]->(sf), + (pontus)-[:WORKS_IN {id: 5, duration: 230, title: 'junior developer', badgeNumber: 9596}]->(malmo), + (max)-[:WORKS_IN {id: 6, duration: 230, title: 'field engineer', badgeNumber: 4416}]->(newyork), + (konstantin)-[:WORKS_IN {id: 7, duration: 230, title: 'frontend developer'}]->(london), + (stefan)-[:WORKS_IN {id: 8, duration: 230, title: 'chief architect', badgeNumber: 2951}]->(london), + (stefan)-[:WORKS_IN {id: 9, duration: 230, title: 'language architect', badgeNumber: 7092}]->(berlin), + (mats)-[:WORKS_IN {id: 10, duration: 230, title: 'senior developer', badgeNumber: 1402}]->(malmo), + (petra)-[:WORKS_IN {id: 11, duration: 230, title: 'language architect'}]->(london), + (craig)-[:WORKS_IN {id: 12, duration: 230, title: 'senior developer'}]->(malmo), + (steven)-[:WORKS_IN {id: 13, duration: 230, title: 'junior developer', badgeNumber: 1515}]->(malmo), + (chris)-[:WORKS_IN {id: 14, duration: 230, title: 'field engineer', badgeNumber: 6828}]->(madrid), (london)-[:IN]->(england), (me)-[:FRIENDS_WITH]->(andy), (andy)-[:FRIENDS_WITH]->(bob), @@ -77,6 +77,9 @@ CREATE (me:Person {name: 'me'}), (bob)-[:FRIENDS_WITH]->(andy), (steven)-[:FRIENDS_WITH]->(mats), (mattias)-[:FRIENDS_WITH]->(me); + +CREATE CONSTRAINT constraint_WORKS_IN_id_unique IF NOT EXISTS FOR ()-[r:WORKS_IN]-() REQUIRE (r.id) IS UNIQUE; +CREATE CONSTRAINT constraint_WORKS_IN_badgeNumber_unique IF NOT EXISTS FOR ()-[r:WORKS_IN]-() REQUIRE (r.badgeNumber) IS UNIQUE; ---- //// @@ -2772,9 +2775,9 @@ Total database accesses: 1, total allocated memory: 200 == Assert Same Node // AssertSameNode -The `AssertSameNode` operator is used to ensure that no property uniqueness constraints are violated in the slotted and interpreted runtime. -The example looks for the presence of a team with the supplied name and id, and if one does not exist, it will be created. -Owing to the existence of two property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. +The `AssertSameNode` operator is used to ensure that no node property uniqueness constraints are violated in the slotted and interpreted runtime. +The example looks for the presence of a team node with the supplied name and id, and if one does not exist, it will be created. +Owing to the existence of two node property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. .AssertSameNode @@ -2819,6 +2822,59 @@ Total database accesses: 2, total allocated memory: 64 ====== +[[query-plan-assert-same-relationship]] +== Assert Same Relationship +// AssertSameRelationship + +The `AssertSameRelationship` operator is used to ensure that no relationship property uniqueness constraints are violated in the slotted and interpreted runtime. +The example looks for the presence of a `WORKS_IN` relationship with the supplied `id` and `badgeNumber`. +If it can't be found, then it will be created. +Owing to the existence of two property uniqueness constraints on `:WORKS_IN(id)` and `:WORKS_IN(badgeNumber)`, any relationship that would be found by the `DirectedRelationshipUniqueIndexSeek` operator must be the very same relationship or the constraints would be violated. + + +.AssertSameRelationship +====== + +.Query +[source, cypher] +---- +PROFILE +CYPHER runtime=slotted +MERGE (person)-[work:WORKS_IN {id: 0, badgeNumber: 4332}]->(location) +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime SLOTTED + +Runtime version {neo4j-version-minor} + ++-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | 0 | | 1 | 0 | 0 | 0/0 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +EmptyResult | 1 | | 1 | 0 | 0 | 0/0 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +Merge | 2 | CREATE (person), (location), (person)-[work:WORKS_IN {id: $autoint_0, badgeNumber: $autoint_1}]->(lo | 1 | 1 | 0 | 0/0 | +| | | | cation) | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +AssertSameRelationship | 3 | work | 0 | 1 | 0 | 0/0 | +| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| | +DirectedRelationshipUniqueIndexSeek(Locking) | 4 | RANGE INDEX (person)-[work:WORKS_IN(badgeNumber)]->(location) WHERE badgeNumber = $autoint_1 | 1 | 1 | 1 | 0/1 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +DirectedRelationshipUniqueIndexSeek(Locking) | 5 | RANGE INDEX (person)-[work:WORKS_IN(id)]->(location) WHERE id = $autoint_0 | 1 | 1 | 1 | 1/1 | ++-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ + +Total database accesses: 2, total allocated memory: 64 +---- + +====== + + // DropResult -- removed in 4.3 From 8dcc17ff1a2b4ae519c91e0366a2006cbb0255c8 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Fri, 28 Apr 2023 17:54:18 +0200 Subject: [PATCH 186/383] [TESTING] Ensure string type results are surrounded by quotes (#527) Testing infrastructure now checks that strings are ... really strings! Meaning they need to be surrounded by some form of quotes (either single or double). Couple of other mistakes fixed along the way. --- modules/ROOT/pages/administration/aliases.adoc | 4 ++-- modules/ROOT/pages/administration/databases.adoc | 4 ++-- modules/ROOT/pages/clauses/return.adoc | 12 ++++++------ modules/ROOT/pages/clauses/set.adoc | 2 +- modules/ROOT/pages/functions/predicate.adoc | 16 ++++++++-------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/modules/ROOT/pages/administration/aliases.adoc b/modules/ROOT/pages/administration/aliases.adoc index 4104f5431..4cbd6a009 100644 --- a/modules/ROOT/pages/administration/aliases.adoc +++ b/modules/ROOT/pages/administration/aliases.adoc @@ -329,8 +329,8 @@ SHOW ALIASES FOR DATABASE | +name+ | +database+ | +location+ | +url+ | +user+ | +"films"+ | +"movies"+ | +"local"+ | ++ | ++ -| +"library.romance"+ | +romance-books"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ -| +"library.sci-fi"+ | +sci-fi-books"+ | +"local"+ | ++ | ++ +| +"library.romance"+ | +"romance-books""+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ +| +"library.sci-fi"+ | +"sci-fi-books"+ | +"local"+ | ++ | ++ | +"motion pictures"+ | +"movies"+ | +"local"+ | ++ | ++ | +"movie scripts"+ | +"scripts"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ 5+d|Rows: 5 diff --git a/modules/ROOT/pages/administration/databases.adoc b/modules/ROOT/pages/administration/databases.adoc index d2ffa03e1..62203e85d 100644 --- a/modules/ROOT/pages/administration/databases.adoc +++ b/modules/ROOT/pages/administration/databases.adoc @@ -288,7 +288,7 @@ SHOW DATABASES |=== | +name+ | +type+ | +aliases+ | +access+ | +address+ | +role+ | +writer+ | +requestedStatus+ | +currentStatus+ | +statusMessage+ | +default+ | +home+ | +constituents+ -| +"movies"+ | +standard+ | +["films","motion pictures"]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +false+ | +false+ | +[]+ +| +"movies"+ | +"standard"+ | +["films","motion pictures"]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +false+ | +false+ | +[]+ | +"neo4j"+ | +"standard"+ | +[]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +true+ | +true+ | +[]+ | +"system"+ | +"system"+ | +[]+ | +"read-write"+ | +"localhost:7687"+ | +"primary"+ | +true+ | +"online"+ | +"online"+ | +""+ | +false+ | +false+ | +[]+ 13+d|Rows: 3 @@ -630,7 +630,7 @@ SHOW DATABASES YIELD name, type, access, role, writer, constituents | +"movies"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ | +"neo4j"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ | +"romance"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ -| +"sci-fi-books"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ +| +"sci-fi"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ | +"system"+ | +"system"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ | +"topology-example"+ | +"standard"+ | +"read-write"+ | +"primary"+ | +true+ | +[]+ 6+d|Rows: 8 diff --git a/modules/ROOT/pages/clauses/return.adoc b/modules/ROOT/pages/clauses/return.adoc index 12d27de2e..6395e4261 100644 --- a/modules/ROOT/pages/clauses/return.adoc +++ b/modules/ROOT/pages/clauses/return.adoc @@ -5,7 +5,7 @@ [[return-introduction]] == Introduction -The `RETURN` clause defines the parts of a pattern (nodes, relationships, and/or properties) to be included in the query result. +The `RETURN` clause defines the parts of a pattern (nodes, relationships, and/or properties) to be included in the query result. [[return-example-graph]] == Example graph @@ -62,8 +62,8 @@ RETURN type(r) .Result [role="queryresult",options="header,footer",cols="1*(m) RETURN * ---- -This returns the two nodes, and the two possible paths between them. +This returns the two nodes, and the two possible paths between them. .Result [role="queryresult",options="header,footer",cols="4*+ | +36+ +| ++ | +"36"+ 2+d|Rows: 1 + Properties set: 1 |=== diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 3a5cdc006..156f66ffd 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -3,7 +3,7 @@ [[query-functions-predicate]] = Predicate functions -== Introduction +== Introduction Predicates are boolean functions that return `true` or `false` for a given set of non-`null` input. They are most commonly used to filter out paths in the `WHERE` part of a query. @@ -308,12 +308,12 @@ This query returns every `Person` node in the graph with a set `nationality` pro .Result [role="queryresult",options="header,footer",cols="2* Date: Fri, 28 Apr 2023 18:12:37 +0200 Subject: [PATCH 187/383] [TESTING] Fix string formatting. --- modules/ROOT/pages/administration/aliases.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/administration/aliases.adoc b/modules/ROOT/pages/administration/aliases.adoc index 4cbd6a009..55d09ecaa 100644 --- a/modules/ROOT/pages/administration/aliases.adoc +++ b/modules/ROOT/pages/administration/aliases.adoc @@ -372,7 +372,7 @@ SHOW ALIAS library.romance FOR DATABASES |=== | +name+ | +database+ | +location+ | +url+ | +user+ -| +"library.romance"+ | +romance-books"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ +| +"library.romance"+ | +"romance-books"+ | +"remote"+ | +"neo4j+s://location:7687"+ | +"alice"+ 5+d|Rows: 1 From 794aa245a180f0a3021a656bf7391f4ad697ecda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 2 May 2023 15:11:41 +0200 Subject: [PATCH 188/383] Add cheat sheet link to Aura intro page (#528) --- modules/ROOT/pages/introduction/cypher_aura.adoc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/ROOT/pages/introduction/cypher_aura.adoc b/modules/ROOT/pages/introduction/cypher_aura.adoc index ad865f2b7..c1cd41633 100644 --- a/modules/ROOT/pages/introduction/cypher_aura.adoc +++ b/modules/ROOT/pages/introduction/cypher_aura.adoc @@ -44,13 +44,10 @@ The Cypher Manual uses two different labels to differentiate this distinction: | label:aura-db-enterprise[AuraDB Enterprise] | Cypher feature only available on AuraDB Enterprise. |=== -//// -TODO: remove comment blocks once Aura Cheat Sheet has been published. - == Aura and the Cypher Cheat Sheet Each different tier of Aura has a customized version of the Cypher Cheat Sheet which only shows the features of Cypher available for the chosen tier. -The Aura Cheat Sheet can be accessed here: //Add url when available +The Cypher Cheat Sheet can be accessed link:{neo4j-docs-base-uri}/cypher-cheat-sheet/{page-version}/auradb-enterprise/[here]. +You can select your desired Aura tier and Neo4j version by using the dropdown menus provided. Note that the default tier is AuraDB Enterprise. -//// From 32aaabe0865ea8ae33cc7e7771823e1953b42ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 2 May 2023 19:42:33 +0200 Subject: [PATCH 189/383] Add 5.7 CALL IN TXs features to additions page (#526) Trello Card: https://trello.com/c/01mZK1Gb/4923-need-disclaimer-introduced-in-57-for-https-neo4jcom-docs-cypher-manual-current-clauses-call-subquery-txserrorbehaviour --- ...ions-additions-removals-compatibility.adoc | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index a39863b38..bcb395018 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -40,6 +40,39 @@ The `trackedSince` column returns the time when usage statistics tracking starte [[cypher-deprecations-additions-removals-5.7]] == Version 5.7 +=== New features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:new[] +[source, syntax] +---- +CALL { + +} IN TRANSACTIONS [ OF ROWS ] + [ ON ERROR CONTINUE / BREAK / FAIL ] + [ REPORT STATUS AS ] +---- + +a| +New fine-grained control mechanism to control how an inner transaction impacts subsequent inner and/or outer transactions. + +* `ON ERROR CONTINUE` - will ignore errors and continue with the execution of subsequent inner transactions when one of them fails. + +* `ON ERROR BREAK` - will ignore an error and stop the execution of subsequent inner transactions. + +* `ON ERROR FAIL` - will fail in case of an error. + +* `REPORT STATUS AS ` - reports the execution status of the inner transaction (a map value including the fields `started` `committed`, `transactionId`, and `errorMessage`). This flag is disallowed for `ON ERROR FAIL`. + +|=== + + === Deprecated features [cols="2", options="header"] From 4437e81d3293c112f39fd079de545676d6e14e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 3 May 2023 09:58:13 +0200 Subject: [PATCH 190/383] Small editorial change to introduction (#533) --- modules/ROOT/pages/introduction/cypher_neo4j.adoc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/ROOT/pages/introduction/cypher_neo4j.adoc b/modules/ROOT/pages/introduction/cypher_neo4j.adoc index 10ee3b16f..37a8eea13 100644 --- a/modules/ROOT/pages/introduction/cypher_neo4j.adoc +++ b/modules/ROOT/pages/introduction/cypher_neo4j.adoc @@ -55,11 +55,8 @@ Normally there is only one graph within each database, and many administrative c Cypher queries executed in a session may declare which graph they apply to, or use a default, given by the session. Composite databases can contain multiple graphs, by means of aliases to other databases. Queries submitted to composite databases may refer to multiple graphs within the same query. - For more information, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases/[Operations manual -> Composite databases]. -Composite databases can contain multiple graphs, by means of aliases to other databases. Queries submitted to composite databases may refer to multiple graphs within the same query. For more information, see Operations manual → Composite databases. - *Database*:: A database is a storage and retrieval mechanism for collecting data in a defined space on disk and in memory. [[built-in-databases]] @@ -78,7 +75,7 @@ For more information about the _system_ database, see the sections on xref::admi === Query considerations Most of the time Cypher queries are reading or updating queries, which are run against a graph. -There are also administrative commands that apply to a database, or to the entire DBMS. +There are also xref:administration/index.adoc[administrative commands] that apply to a database, or to the entire DBMS. Administrative commands cannot be run in a session connected to a normal user database, but instead need to be run within a session connected to the `system` database. Administrative commands execute on the `system` database. If an administrative command is submitted to a user database, it is rerouted to the system database. From 163611c08773ec0c1a5025d2d65113fa59624ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 3 May 2023 13:46:34 +0200 Subject: [PATCH 191/383] New values and types chapter (#516) - Creates stand-alone chapter for Values and types, including a new page for casting data values - Fixes all the affected xref links. Companion PRs - Operations Manual: https://github.com/neo4j/docs-operations/pull/760 - Cypher Cheat Sheet: https://github.com/neo4j/docs-cheat-sheet/pull/80 - APOC: https://github.com/neo4j/docs-apoc/pull/138 --------- --- modules/ROOT/content-nav.adoc | 16 +- modules/ROOT/images/graph5.svg | 149 ----- modules/ROOT/images/graph6.svg | 92 --- ...values_and_types_converting_data_graph.svg | 1 + .../images/values_and_types_lists_graph.svg | 1 + .../images/values_and_types_maps_graph.svg | 1 + modules/ROOT/pages/clauses/order-by.adoc | 2 +- modules/ROOT/pages/clauses/where.adoc | 2 +- ...ions-additions-removals-compatibility.adoc | 16 +- modules/ROOT/pages/functions/aggregating.adoc | 8 +- modules/ROOT/pages/functions/index.adoc | 6 +- modules/ROOT/pages/functions/list.adoc | 2 +- .../pages/functions/temporal/duration.adoc | 6 +- .../ROOT/pages/functions/temporal/index.adoc | 92 +-- modules/ROOT/pages/syntax/index.adoc | 76 +-- modules/ROOT/pages/syntax/lists.adoc | 307 ---------- modules/ROOT/pages/syntax/maps.adoc | 145 ----- modules/ROOT/pages/syntax/operators.adoc | 50 +- modules/ROOT/pages/syntax/spatial.adoc | 267 --------- .../pages/values-and-types/casting-data.adoc | 171 ++++++ .../ROOT/pages/values-and-types/index.adoc | 16 + .../ROOT/pages/values-and-types/lists.adoc | 379 ++++++++++++ modules/ROOT/pages/values-and-types/maps.adoc | 156 +++++ .../property-structural-composite.adoc} | 56 +- .../ROOT/pages/values-and-types/spatial.adoc | 273 +++++++++ .../temporal.adoc | 541 +++++++++--------- .../working-with-null.adoc | 78 ++- 27 files changed, 1462 insertions(+), 1447 deletions(-) delete mode 100644 modules/ROOT/images/graph5.svg delete mode 100644 modules/ROOT/images/graph6.svg create mode 100644 modules/ROOT/images/values_and_types_converting_data_graph.svg create mode 100644 modules/ROOT/images/values_and_types_lists_graph.svg create mode 100644 modules/ROOT/images/values_and_types_maps_graph.svg delete mode 100644 modules/ROOT/pages/syntax/lists.adoc delete mode 100644 modules/ROOT/pages/syntax/maps.adoc delete mode 100644 modules/ROOT/pages/syntax/spatial.adoc create mode 100644 modules/ROOT/pages/values-and-types/casting-data.adoc create mode 100644 modules/ROOT/pages/values-and-types/index.adoc create mode 100644 modules/ROOT/pages/values-and-types/lists.adoc create mode 100644 modules/ROOT/pages/values-and-types/maps.adoc rename modules/ROOT/pages/{syntax/values.adoc => values-and-types/property-structural-composite.adoc} (68%) create mode 100644 modules/ROOT/pages/values-and-types/spatial.adoc rename modules/ROOT/pages/{syntax => values-and-types}/temporal.adoc (94%) rename modules/ROOT/pages/{syntax => values-and-types}/working-with-null.adoc (52%) diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 10d1ccb19..a68f7dfc3 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -5,7 +5,6 @@ ** xref:introduction/cypher_aura.adoc[] * xref:syntax/index.adoc[] -** xref:syntax/values.adoc[] ** xref:syntax/naming.adoc[] ** xref:syntax/expressions.adoc[] ** xref:syntax/variables.adoc[] @@ -14,11 +13,6 @@ ** xref:syntax/operators.adoc[] ** xref:syntax/comments.adoc[] ** xref:syntax/patterns.adoc[] -** xref:syntax/temporal.adoc[] -** xref:syntax/spatial.adoc[] -** xref:syntax/lists.adoc[] -** xref:syntax/maps.adoc[] -** xref:syntax/working-with-null.adoc[] * xref:clauses/index.adoc[] ** xref:clauses/clause_composition.adoc[] @@ -48,6 +42,16 @@ ** xref:clauses/transaction-clauses.adoc#query-listing-transactions[SHOW TRANSACTIONS] ** xref:clauses/transaction-clauses.adoc#query-terminate-transactions[TERMINATE TRANSACTIONS] +* xref:values-and-types/index.adoc[] +** xref:values-and-types/property-structural-composite.adoc[] +** xref:values-and-types/temporal.adoc[] +** xref:values-and-types/spatial.adoc[] +** xref:values-and-types/working-with-null.adoc[] +** xref:values-and-types/lists.adoc[] +** xref:values-and-types/maps.adoc[] +** xref:values-and-types/casting-data.adoc[] + + * xref:functions/index.adoc[] ** xref:functions/predicate.adoc[] ** xref:functions/scalar.adoc[] diff --git a/modules/ROOT/images/graph5.svg b/modules/ROOT/images/graph5.svg deleted file mode 100644 index bdd240952..000000000 --- a/modules/ROOT/images/graph5.svg +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - -L - - - -N0 - -Person - -name = 'Keanu Reeves' - - - -N8 - -Movie - -title = 'The Matrix Resurrections' -released = 2021 - - - -N0->N8 - - -ACTED_IN - - - -N7 - -Movie - -title = 'The Devils Advocate' -released = 1997 - - - -N0->N7 - - -ACTED_IN - - - -N4 - -Movie - -title = 'The Matrix Reloaded' -released = 2003 - - - -N0->N4 - - -ACTED_IN - - - -N3 - -Movie - -title = 'The Matrix Revolutions' -released = 2003 - - - -N0->N3 - - -ACTED_IN - - - -N5 - -Movie - -title = 'The Replacements' -released = 2000 - - - -N0->N5 - - -ACTED_IN - - - -N6 - -Movie - -title = 'The Matrix' -released = 1999 - - - -N0->N6 - - -ACTED_IN - - - -N2 - -Movie - -title = 'Somethings Gotta Give' -released = 2003 - - - -N0->N2 - - -ACTED_IN - - - -N1 - -Movie - -title = 'Johnny Mnemonic' -released = 1995 - - - -N0->N1 - - -ACTED_IN - - - diff --git a/modules/ROOT/images/graph6.svg b/modules/ROOT/images/graph6.svg deleted file mode 100644 index 0fd4be2d0..000000000 --- a/modules/ROOT/images/graph6.svg +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - -L - - - -N0 - -Person - -realName = 'Carlos Irwin Estévez' -name = 'Charlie Sheen' - - - -N4 - -Movie - -year = 1979 -title = 'Apocalypse Now' - - - -N0->N4 - - -ACTED_IN - - - -N3 - -Movie - -year = 1984 -title = 'Red Dawn' - - - -N0->N3 - - -ACTED_IN - - - -N2 - -Movie - -year = 1987 -title = 'Wall Street' - - - -N0->N2 - - -ACTED_IN - - - -N1 - -Person - -name = 'Martin Sheen' - - - -N1->N4 - - -ACTED_IN - - - -N1->N2 - - -ACTED_IN - - - diff --git a/modules/ROOT/images/values_and_types_converting_data_graph.svg b/modules/ROOT/images/values_and_types_converting_data_graph.svg new file mode 100644 index 000000000..97ce2ecff --- /dev/null +++ b/modules/ROOT/images/values_and_types_converting_data_graph.svg @@ -0,0 +1 @@ +KNOWSsince:1999Personname:'Keanu Reeves'age:58active:truePersonname:'Carrie-Anne Moss'age:55active:true \ No newline at end of file diff --git a/modules/ROOT/images/values_and_types_lists_graph.svg b/modules/ROOT/images/values_and_types_lists_graph.svg new file mode 100644 index 000000000..8c71adee4 --- /dev/null +++ b/modules/ROOT/images/values_and_types_lists_graph.svg @@ -0,0 +1 @@ +ACTED_INACTED_INACTED_INACTED_INACTED_INACTED_INACTED_INMovietitle:'The Matrix Reloaded'released:2003Movietitle:'Johnny Mnemonic'released:1995Personname:'Keanu Reeves'Movietitle:'The Matrix'released:1999Movietitle:'The Matrix Revolutions'released:2003Movietitle:'The Matrix Resurrections'released:2021Movietitle:'The Replacements'released:2000Movietitle:'The Devil's Advocate'released:1997 \ No newline at end of file diff --git a/modules/ROOT/images/values_and_types_maps_graph.svg b/modules/ROOT/images/values_and_types_maps_graph.svg new file mode 100644 index 000000000..9d0bef557 --- /dev/null +++ b/modules/ROOT/images/values_and_types_maps_graph.svg @@ -0,0 +1 @@ +ACTED_INACTED_INACTED_INACTED_INACTED_INACTED_INACTED_INACTED_INACTED_INMovietitle:'The Matrix Reloaded'released:2003Personname:'Keanu Reeves'nationality:'Canadian'Movietitle:'The Matrix'released:1999Movietitle:'The Matrix Revolutions'released:2003Movietitle:'The Matrix Resurrections'released:2021Movietitle:'The Devil's Advocate'released:1997Personname:'Carrie-Anne Moss' \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/order-by.adoc b/modules/ROOT/pages/clauses/order-by.adoc index 7a5d28780..60c7694f9 100644 --- a/modules/ROOT/pages/clauses/order-by.adoc +++ b/modules/ROOT/pages/clauses/order-by.adoc @@ -43,7 +43,7 @@ CREATE [NOTE] ==== Strings that contain special characters can have inconsistent or non-deterministic ordering in Neo4j. -For details, see xref::syntax/values.adoc#property-types-sip-note[Sorting of special characters]. +For details, see xref::values-and-types/property-structural-composite.adoc#property-types-sip-note[Sorting of special characters]. ==== [[order-nodes-by-property]] diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index ee2ecad3d..d8b4d1b07 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -84,7 +84,7 @@ RETURN [(a)-->(b WHERE b:Person) | b.name] AS friends === Boolean operations The following boolean operators can be used with the `WHERE` clause: `AND`, `OR`, `XOR`, and `NOT`. -For more information on how operators work with `null`, see the chapter on xref::syntax/working-with-null.adoc[Working with null]. +For more information on how operators work with `null`, see the chapter on xref::values-and-types/working-with-null.adoc[Working with null]. .Query [source, cypher] diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index bcb395018..d1549c094 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -3116,7 +3116,7 @@ label:removed[] extract() ---- a| -Replaced by xref:syntax/lists.adoc#cypher-list-comprehension[list comprehension]. +Replaced by xref:values-and-types/lists.adoc#cypher-list-comprehension[list comprehension]. a| label:function[] @@ -3126,7 +3126,7 @@ label:removed[] filter() ---- a| -Replaced by xref:syntax/lists.adoc#cypher-list-comprehension[list comprehension]. +Replaced by xref:values-and-types/lists.adoc#cypher-list-comprehension[list comprehension]. a| label:functionality[] @@ -3490,7 +3490,7 @@ label:deprecated[] extract() ---- a| -Replaced by xref:syntax/lists.adoc#cypher-list-comprehension[list comprehension]. +Replaced by xref:values-and-types/lists.adoc#cypher-list-comprehension[list comprehension]. a| label:function[] @@ -3500,7 +3500,7 @@ label:deprecated[] filter() ---- a| -Replaced by xref:syntax/lists.adoc#cypher-list-comprehension[list comprehension]. +Replaced by xref:values-and-types/lists.adoc#cypher-list-comprehension[list comprehension]. |=== @@ -3509,11 +3509,11 @@ Replaced by xref:syntax/lists.adoc#cypher-list-comprehension[list comprehension] [options="header"] |=== | Feature | Type | Change | Details -| xref:syntax/spatial.adoc[Spatial point types] | Functionality | Amendment | A point -- irrespective of which Coordinate Reference System is used -- can be stored as a property and is able to be backed by an index. Prior to this, a point was a virtual property only. +| xref:values-and-types/spatial.adoc[Spatial point types] | Functionality | Amendment | A point -- irrespective of which Coordinate Reference System is used -- can be stored as a property and is able to be backed by an index. Prior to this, a point was a virtual property only. | xref:functions/spatial.adoc#functions-point-cartesian-3d[point() - Cartesian 3D] | Function | Added | | xref:functions/spatial.adoc#functions-point-wgs84-3d[point() - WGS 84 3D] | Function | Added | | xref:functions/scalar.adoc#functions-randomuuid[randomUUID()] | Function | Added | -| xref:syntax/temporal.adoc[Temporal types] | Functionality | Added | Supports storing, indexing and working with the following temporal types: Date, Time, LocalTime, DateTime, LocalDateTime and Duration. +| xref:values-and-types/temporal.adoc[Temporal types] | Functionality | Added | Supports storing, indexing and working with the following temporal types: Date, Time, LocalTime, DateTime, LocalDateTime and Duration. | xref:functions/temporal/index.adoc[Temporal functions] | Functionality | Added | Functions allowing for the creation and manipulation of values for each temporal type -- _Date_, _Time_, _LocalTime_, _DateTime_, _LocalDateTime_ and _Duration_. | xref:syntax/operators.adoc#query-operators-temporal[Temporal operators] | Functionality | Added | Operators allowing for the manipulation of values for each temporal type -- _Date_, _Time_, _LocalTime_, _DateTime_, _LocalDateTime_ and _Duration_. | xref:functions/string.adoc#functions-tostring[toString()] | Function | Extended | Now also allows temporal values as input (i.e. values of type _Date_, _Time_, _LocalTime_, _DateTime_, _LocalDateTime_ or _Duration_). @@ -3564,8 +3564,8 @@ An example of this is `CALL db.index.explicit.searchNodes('my_index','email:me*' | `lower()` | Function | Deprecated | Replaced by xref:functions/string.adoc#functions-tolower[toLower()] | `upper()` | Function | Deprecated | Replaced by xref:functions/string.adoc#functions-toupper[toUpper()] | xref:functions/scalar.adoc#functions-toboolean[toBoolean()] | Function | Added | -| xref:syntax/maps.adoc#cypher-map-projection[Map projection] | Syntax | Added | -| xref:syntax/lists.adoc#cypher-pattern-comprehension[Pattern comprehension] | Syntax | Added | +| xref:values-and-types/maps.adoc#cypher-map-projection[Map projection] | Syntax | Added | +| xref:values-and-types/lists.adoc#cypher-pattern-comprehension[Pattern comprehension] | Syntax | Added | | link:/docs/java-reference/5/extending-neo4j/functions#extending-neo4j-functions[User-defined functions] | Functionality | Added | | xref:clauses/call.adoc[CALL\...YIELD\...WHERE] | Clause | Extended | Records returned by `YIELD` may be filtered further using `WHERE` |=== diff --git a/modules/ROOT/pages/functions/aggregating.adoc b/modules/ROOT/pages/functions/aggregating.adoc index 846dd23a9..757d2be43 100644 --- a/modules/ROOT/pages/functions/aggregating.adoc +++ b/modules/ROOT/pages/functions/aggregating.adoc @@ -438,7 +438,7 @@ max(expression) |=== -| A xref::syntax/values.adoc#property-types[property type], or a list, depending on the values returned by `expression`. +| A xref::values-and-types/property-structural-composite.adoc#property-types[property type], or a list, depending on the values returned by `expression`. |=== @@ -449,7 +449,7 @@ max(expression) | Name | Description | `expression` -| An expression returning a set containing any combination of xref::syntax/values.adoc#property-types[property types] and lists thereof. +| An expression returning a set containing any combination of xref::values-and-types/property-structural-composite.adoc#property-types[property types] and lists thereof. |=== @@ -557,7 +557,7 @@ min(expression) |=== -| A xref::syntax/values.adoc#property-types[property type], or a list, depending on the values returned by `expression`. +| A xref::values-and-types/property-structural-composite.adoc#property-types[property type], or a list, depending on the values returned by `expression`. |=== @@ -569,7 +569,7 @@ min(expression) | Name | Description | `expression` -| An expression returning a set containing any combination of xref::syntax/values.adoc#property-types[property types] and lists thereof. +| An expression returning a set containing any combination of xref::values-and-types/property-structural-composite.adoc#property-types[property types] and lists thereof. |=== diff --git a/modules/ROOT/pages/functions/index.adoc b/modules/ROOT/pages/functions/index.adoc index c905117b5..b026db48a 100644 --- a/modules/ROOT/pages/functions/index.adoc +++ b/modules/ROOT/pages/functions/index.adoc @@ -248,7 +248,7 @@ These functions take multiple values as arguments, and calculate and return an a **xref::functions/list.adoc[List functions]** These functions return lists of other values. -Further details and examples of lists may be found in xref::syntax/lists.adoc[Lists]. +Further details and examples of lists may be found in xref::values-and-types/lists.adoc[Lists]. [options="header"] |=== @@ -535,7 +535,7 @@ These functions are used to manipulate strings or to create a string representat [[header-query-functions-temporal-instant-types]] **xref::functions/temporal/index.adoc[Temporal instant types functions]** -Values of the xref::syntax/temporal.adoc[temporal types] -- _Date_, _Time_, _LocalTime_, _DateTime_, and _LocalDateTime_ -- can be created manipulated using the following functions: +Values of the xref::values-and-types/temporal.adoc[temporal types] -- _Date_, _Time_, _LocalTime_, _DateTime_, and _LocalDateTime_ -- can be created manipulated using the following functions: [options="header"] |=== @@ -655,7 +655,7 @@ Values of the xref::syntax/temporal.adoc[temporal types] -- _Date_, _Time_, _Loc [[header-query-functions-temporal-duration]] **xref::functions/temporal/duration.adoc[Temporal duration functions]** -Duration values of the xref::syntax/temporal.adoc[temporal types] can be created manipulated using the following functions: +Duration values of the xref::values-and-types/temporal.adoc[temporal types] can be created manipulated using the following functions: [options="header"] |=== diff --git a/modules/ROOT/pages/functions/list.adoc b/modules/ROOT/pages/functions/list.adoc index 155362df0..e1659fe5e 100644 --- a/modules/ROOT/pages/functions/list.adoc +++ b/modules/ROOT/pages/functions/list.adoc @@ -8,7 +8,7 @@ List functions return lists of things -- nodes in a path, and so on. -- -Further details and examples of lists may be found in xref::syntax/lists.adoc[Lists] and xref::syntax/operators.adoc#query-operators-list[List operators]. +Further details and examples of lists may be found in xref::values-and-types/lists.adoc[Lists] and xref::syntax/operators.adoc#query-operators-list[List operators]. == Example graph diff --git a/modules/ROOT/pages/functions/temporal/duration.adoc b/modules/ROOT/pages/functions/temporal/duration.adoc index 46702737b..792daaacc 100644 --- a/modules/ROOT/pages/functions/temporal/duration.adoc +++ b/modules/ROOT/pages/functions/temporal/duration.adoc @@ -10,7 +10,7 @@ Cypher provides functions allowing for the creation and manipulation of values f [NOTE] ==== -See also xref::syntax/temporal.adoc[Temporal (Date/Time) values] and xref::syntax/operators.adoc#query-operators-temporal[Temporal operators]. +See also xref::values-and-types/temporal.adoc[Temporal (Date/Time) values] and xref::syntax/operators.adoc#query-operators-temporal[Temporal operators]. ==== duration(): @@ -19,7 +19,7 @@ duration(): * xref::functions/temporal/duration.adoc#functions-duration-create-string[Creating a _Duration_ from a string] * xref::functions/temporal/duration.adoc#functions-duration-computing[Computing the _Duration_ between two temporal instants] -Information regarding specifying and accessing components of a _Duration_ value can be found xref::syntax/temporal.adoc#cypher-temporal-durations[here]. +Information regarding specifying and accessing components of a _Duration_ value can be found xref::values-and-types/temporal.adoc#cypher-temporal-durations[here]. [[functions-duration]] == Creating a _Duration_ from duration components @@ -180,7 +180,7 @@ duration(temporalAmount) |=== -| `temporalAmount` must comply with either the xref::syntax/temporal.adoc#cypher-temporal-specifying-durations[unit based form or date-and-time based form defined for _Durations_]. +| `temporalAmount` must comply with either the xref::values-and-types/temporal.adoc#cypher-temporal-specifying-durations[unit based form or date-and-time based form defined for _Durations_]. |=== diff --git a/modules/ROOT/pages/functions/temporal/index.adoc b/modules/ROOT/pages/functions/temporal/index.adoc index da6ec36ea..19db53173 100644 --- a/modules/ROOT/pages/functions/temporal/index.adoc +++ b/modules/ROOT/pages/functions/temporal/index.adoc @@ -10,7 +10,7 @@ Cypher provides functions allowing for the creation and manipulation of values f [NOTE] ==== -See also xref::syntax/temporal.adoc[Temporal (Date/Time) values] and xref::syntax/operators.adoc#query-operators-temporal[Temporal operators]. +See also xref::values-and-types/temporal.adoc[Temporal (Date/Time) values] and xref::syntax/operators.adoc#query-operators-temporal[Temporal operators]. ==== @@ -351,7 +351,7 @@ date([{timezone}]) | | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -441,7 +441,7 @@ date.transaction([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -498,7 +498,7 @@ date.statement([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -554,7 +554,7 @@ date.realtime([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -634,7 +634,7 @@ date({year [, month, day]}) | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `month` | An integer between `1` and `12` that specifies the month. @@ -715,7 +715,7 @@ date({year [, week, dayOfWeek]}) | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `week` | An integer between `1` and `53` that specifies the week. @@ -796,7 +796,7 @@ date({year [, quarter, dayOfQuarter]}) | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `quarter` | An integer between `1` and `4` that specifies the quarter. @@ -877,7 +877,7 @@ date({year [, ordinalDay]}) | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `ordinalDay` | An integer between `1` and `366` that specifies the ordinal day of the year. @@ -958,7 +958,7 @@ date(temporalValue) |=== -| `temporalValue` must comply with the format defined for xref::syntax/temporal.adoc#cypher-temporal-specify-date[dates]. +| `temporalValue` must comply with the format defined for xref::values-and-types/temporal.adoc#cypher-temporal-specify-date[dates]. | `temporalValue` must denote a valid date; i.e. a `temporalValue` denoting `30 February 2001` is invalid. | `date(null)` returns `null`. @@ -1035,7 +1035,7 @@ date({date [, year, month, day, week, dayOfWeek, quarter, dayOfQuarter, ordinalD | A _Date_ value. | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `month` | An integer between `1` and `12` that specifies the month. @@ -1146,7 +1146,7 @@ date.truncate(unit [, temporalInstantValue [, mapOfComponents ] ]) |=== | Any component that is provided in `mapOfComponents` must be less significant than `unit`; i.e. if `unit` string is `'day'`, `mapOfComponents` cannot contain information pertaining to a _month_. -| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::syntax/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. +| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::values-and-types/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. | If `mapOfComponents` is not provided, all components of the returned value which are less significant than `unit` will be set to their default values. | If `temporalInstantValue` is not provided, it will be set to the current date, i.e. `date.truncate(unit)` is equivalent of `date.truncate(unit, date())`. @@ -1242,7 +1242,7 @@ datetime([{timezone}]) | | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -1333,7 +1333,7 @@ datetime.transaction([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -1411,7 +1411,7 @@ datetime.statement([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -1467,7 +1467,7 @@ datetime.realtime([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -1525,7 +1525,7 @@ datetime({year [, month, day, hour, minute, second, millisecond, microsecond, na | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `month` | An integer between `1` and `12` that specifies the month. @@ -1644,7 +1644,7 @@ datetime({year [, week, dayOfWeek, hour, minute, second, millisecond, microsecon | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `week` | An integer between `1` and `53` that specifies the week. @@ -1761,7 +1761,7 @@ datetime({year [, quarter, dayOfQuarter, hour, minute, second, millisecond, micr | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `quarter` | An integer between `1` and `4` that specifies the quarter. @@ -1872,7 +1872,7 @@ datetime({year [, ordinalDay, hour, minute, second, millisecond, microsecond, na | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `ordinalDay` | An integer between `1` and `366` that specifies the ordinal day of the year. @@ -1984,7 +1984,7 @@ datetime(temporalValue) |=== -| `temporalValue` must comply with the format defined for xref::syntax/temporal.adoc#cypher-temporal-specify-date[dates], xref::syntax/temporal.adoc#cypher-temporal-specify-time[times] and xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zones]. +| `temporalValue` must comply with the format defined for xref::values-and-types/temporal.adoc#cypher-temporal-specify-date[dates], xref::values-and-types/temporal.adoc#cypher-temporal-specify-time[times] and xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zones]. | The _timezone_ component will default to the configured default time zone if it is omitted. | `temporalValue` must denote a valid date and time; i.e. a `temporalValue` denoting `30 February 2001` is invalid. | `datetime(null)` returns null. @@ -2072,7 +2072,7 @@ datetime({datetime [, year, ..., timezone]}) | datetime({date [, year, ..., time | A _Time_ value. | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `month` | An integer between `1` and `12` that specifies the month. @@ -2401,7 +2401,7 @@ During truncation, a time zone can be attached or overridden using the key `time | If `temporalInstantValue` is one of _Time_, _DateTime_ -- a value with a time zone -- and the time zone is overridden, no time conversion occurs. | If `temporalInstantValue` is one of _LocalDateTime_, _Date_ -- a value without a time zone -- and the time zone is not overridden, the configured default time zone will be used. | Any component that is provided in `mapOfComponents` must be less significant than `unit`; i.e. if `unit` is `'day'`, `mapOfComponents` cannot contain information pertaining to a _month_. -| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::syntax/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. +| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::values-and-types/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. | If `mapOfComponents` is not provided, all components of the returned value which are less significant than `unit` will be set to their default values. | If `temporalInstantValue` is not provided, it will be set to the current date, time and timezone, i.e. `datetime.truncate(unit)` is equivalent of `datetime.truncate(unit, datetime())`. @@ -2493,7 +2493,7 @@ localdatetime([{timezone}]) | | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -2584,7 +2584,7 @@ localdatetime.transaction([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -2641,7 +2641,7 @@ localdatetime.statement([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -2697,7 +2697,7 @@ localdatetime.realtime([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -2777,7 +2777,7 @@ localdatetime({year [, month, day, hour, minute, second, millisecond, microsecon | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `month` | An integer between `1` and `12` that specifies the month. @@ -2879,7 +2879,7 @@ localdatetime({year [, week, dayOfWeek, hour, minute, second, millisecond, micro | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `week` | An integer between `1` and `53` that specifies the week. @@ -2981,7 +2981,7 @@ localdatetime({year [, quarter, dayOfQuarter, hour, minute, second, millisecond, | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `quarter` | An integer between `1` and `4` that specifies the quarter. @@ -3083,7 +3083,7 @@ localdatetime({year [, ordinalDay, hour, minute, second, millisecond, microsecon | | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `ordinalDay` | An integer between `1` and `366` that specifies the ordinal day of the year. @@ -3186,7 +3186,7 @@ localdatetime(temporalValue) |=== -| `temporalValue` must comply with the format defined for xref::syntax/temporal.adoc#cypher-temporal-specify-date[dates] and xref::syntax/temporal.adoc#cypher-temporal-specify-time[times]. +| `temporalValue` must comply with the format defined for xref::values-and-types/temporal.adoc#cypher-temporal-specify-date[dates] and xref::values-and-types/temporal.adoc#cypher-temporal-specify-time[times]. | `temporalValue` must denote a valid date and time; i.e. a `temporalValue` denoting `30 February 2001` is invalid. | `localdatetime(null)` returns null. @@ -3264,7 +3264,7 @@ localdatetime({datetime [, year, ..., nanosecond]}) | localdatetime({date [, yea | A _Time_ value. | `year` -| An expression consisting of at xref::syntax/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. +| An expression consisting of at xref::values-and-types/temporal.adoc#cypher-temporal-year[least four digits] that specifies the year. | `month` | An integer between `1` and `12` that specifies the month. @@ -3481,7 +3481,7 @@ localdatetime.truncate(unit [, temporalInstantValue [, mapOfComponents ] ]) | `temporalInstantValue` cannot be a _Date_ value if `unit` is one of: `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`. | Any component that is provided in `mapOfComponents` must be less significant than `unit`; i.e. if `unit` is `'day'`, `mapOfComponents` cannot contain information pertaining to a _month_. -| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::syntax/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. +| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::values-and-types/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. | If `mapOfComponents` is not provided, all components of the returned value which are less significant than `unit` will be set to their default values. | If `temporalInstantValue` is not provided, it will be set to the current date and time, i.e. `localdatetime.truncate(unit)` is equivalent of `localdatetime.truncate(unit, localdatetime())`. @@ -3569,7 +3569,7 @@ localtime([{timezone}]) | | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -3660,7 +3660,7 @@ localtime.transaction([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -3717,7 +3717,7 @@ localtime.statement([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -3795,7 +3795,7 @@ localtime.realtime([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -3951,7 +3951,7 @@ localtime(temporalValue) |=== -| `temporalValue` must comply with the format defined for xref::syntax/temporal.adoc#cypher-temporal-specify-time[times]. +| `temporalValue` must comply with the format defined for xref::values-and-types/temporal.adoc#cypher-temporal-specify-time[times]. | `temporalValue` must denote a valid time; i.e. a `temporalValue` denoting `13:46:64` is invalid. | `localtime(null)` returns null. @@ -4129,7 +4129,7 @@ a| Truncating time to day -- i.e. `unit` is `'day'` -- is supported, and yields midnight at the start of the day (`00:00`), regardless of the value of `temporalInstantValue`. However, the time zone of `temporalInstantValue` is retained. | Any component that is provided in `mapOfComponents` must be less significant than `unit`; i.e. if `unit` is `'second'`, `mapOfComponents` cannot contain information pertaining to a _minute_. -| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::syntax/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. +| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::values-and-types/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. | If `mapOfComponents` is not provided, all components of the returned value which are less significant than `unit` will be set to their default values. | If `temporalInstantValue` is not provided, it will be set to the current time, i.e. `localtime.truncate(unit)` is equivalent of `localtime.truncate(unit, localtime())`. @@ -4213,7 +4213,7 @@ time([{timezone}]) | | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -4303,7 +4303,7 @@ time.transaction([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -4357,7 +4357,7 @@ time.statement([{timezone}]) |=== | Name | Description -| `timezone` | A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| `timezone` | A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -4435,7 +4435,7 @@ time.realtime([{timezone}]) | Name | Description | `timezone` -| A string expression that represents the xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. +| A string expression that represents the xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zone]. |=== @@ -4600,7 +4600,7 @@ time(temporalValue) |=== -| `temporalValue` must comply with the format defined for xref::syntax/temporal.adoc#cypher-temporal-specify-time[times] and xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[time zones]. +| `temporalValue` must comply with the format defined for xref::values-and-types/temporal.adoc#cypher-temporal-specify-time[times] and xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[time zones]. | The _timezone_ component will default to the configured default time zone if it is omitted. | `temporalValue` must denote a valid time; i.e. a `temporalValue` denoting `15:67` is invalid. | `time(null)` returns `null`. @@ -4797,7 +4797,7 @@ During truncation, a time zone can be attached or overridden using the key `time | If `temporalInstantValue` is one of _Time_, _DateTime_ -- a value with a time zone -- and the time zone is overridden, no time conversion occurs. | If `temporalInstantValue` is one of _LocalTime_, _LocalDateTime_, _Date_ -- a value without a time zone -- and the time zone is not overridden, the configured default time zone will be used. | Any component that is provided in `mapOfComponents` must be less significant than `unit`; i.e. if `unit` is `'second'`, `mapOfComponents` cannot contain information pertaining to a _minute_. -| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::syntax/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. +| Any component that is not contained in `mapOfComponents` and which is less significant than `unit` will be set to its xref::values-and-types/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[minimal value]. | If `mapOfComponents` is not provided, all components of the returned value which are less significant than `unit` will be set to their default values. | If `temporalInstantValue` is not provided, it will be set to the current time and timezone, i.e. `time.truncate(unit)` is equivalent of `time.truncate(unit, time())`. |=== diff --git a/modules/ROOT/pages/syntax/index.adoc b/modules/ROOT/pages/syntax/index.adoc index 19e536fb7..de361bde2 100644 --- a/modules/ROOT/pages/syntax/index.adoc +++ b/modules/ROOT/pages/syntax/index.adoc @@ -3,7 +3,7 @@ [[query-syntax]] = Syntax -* xref::syntax/values.adoc[Values and types] +* xref::values-and-types/property-structural-composite.adoc[Values and types] * xref::syntax/naming.adoc[Naming rules and recommendations] * xref::syntax/expressions.adoc[Expressions] ** xref::syntax/expressions.adoc#cypher-expressions-general[Expressions in general] @@ -48,40 +48,40 @@ ** xref::syntax/patterns.adoc#cypher-pattern-relationship[Patterns for relationships] ** xref::syntax/patterns.adoc#cypher-pattern-varlength[Variable-length pattern matching] ** xref::syntax/patterns.adoc#cypher-pattern-path-variables[Assigning to path variables] -* xref::syntax/temporal.adoc[Temporal (Date/Time) values] - ** xref::syntax/temporal.adoc#cypher-temporal-timezones[Time zones] - ** xref::syntax/temporal.adoc#cypher-temporal-instants[Temporal instants] - *** xref::syntax/temporal.adoc#cypher-temporal-specifying-temporal-instants[Specifying temporal instants] - **** xref::syntax/temporal.adoc#cypher-temporal-specify-date[Specifying dates] - **** xref::syntax/temporal.adoc#cypher-temporal-specify-time[Specifying times] - **** xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[Specifying time zones] - **** xref::syntax/temporal.adoc#cypher-temporal-specify-instant-examples[Examples] - *** xref::syntax/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[Accessing components of temporal instants] - ** xref::syntax/temporal.adoc#cypher-temporal-durations[Durations] - *** xref::syntax/temporal.adoc#cypher-temporal-specifying-durations[Specifying durations] - **** xref::syntax/temporal.adoc#cypher-temporal-specify-duration-examples[Examples] - *** xref::syntax/temporal.adoc#cypher-temporal-accessing-components-durations[Accessing components of durations] - ** xref::syntax/temporal.adoc#cypher-temporal-examples[Examples] - ** xref::syntax/temporal.adoc#cypher-temporal-index[Temporal indexing] -* xref::syntax/spatial.adoc[Spatial values] - ** xref::syntax/spatial.adoc#spatial-values-introduction[Introduction] - ** xref::syntax/spatial.adoc#spatial-values-crs[Coordinate Reference Systems] - *** xref::syntax/spatial.adoc#spatial-values-crs-geographic[Geographic coordinate reference systems] - *** xref::syntax/spatial.adoc#spatial-values-crs-cartesian[Cartesian coordinate reference systems] - ** xref::syntax/spatial.adoc#spatial-values-spatial-instants[Spatial instants] - *** xref::syntax/spatial.adoc#spatial-values-spatial-instants-creating-points[Creating points] - *** xref::syntax/spatial.adoc#spatial-values-spatial-instants-accessing-components[Accessing components of points] - ** xref::syntax/spatial.adoc#spatial-values-point-index[Point index] -* xref::syntax/lists.adoc[Lists] - ** xref::syntax/lists.adoc#cypher-lists-general[Lists in general] - ** xref::syntax/lists.adoc#cypher-list-comprehension[List comprehension] - ** xref::syntax/lists.adoc#cypher-pattern-comprehension[Pattern comprehension] -* xref::syntax/maps.adoc[Maps] - ** xref::syntax/maps.adoc#cypher-literal-maps[Literal maps] - ** xref::syntax/maps.adoc#cypher-map-projection[Map projection] -* xref::syntax/working-with-null.adoc[Working with `null`] - ** xref::syntax/working-with-null.adoc#cypher-null-intro[Introduction to `null` in Cypher] - ** xref::syntax/working-with-null.adoc#cypher-null-logical-operators[Logical operations with `null`] - ** xref::syntax/working-with-null.adoc#cypher-null-bracket-operator[The `[\]` operator and `null`] - ** xref::syntax/working-with-null.adoc#cypher-null-in-operator[The `IN` operator and `null`] - ** xref::syntax/working-with-null.adoc#cypher-expressions-and-null[Expressions that return `null`] +* xref::values-and-types/temporal.adoc[Temporal (Date/Time) values] + ** xref::values-and-types/temporal.adoc#cypher-temporal-timezones[Time zones] + ** xref::values-and-types/temporal.adoc#cypher-temporal-instants[Temporal instants] + *** xref::values-and-types/temporal.adoc#cypher-temporal-specifying-temporal-instants[Specifying temporal instants] + **** xref::values-and-types/temporal.adoc#cypher-temporal-specify-date[Specifying dates] + **** xref::values-and-types/temporal.adoc#cypher-temporal-specify-time[Specifying times] + **** xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[Specifying time zones] + **** xref::values-and-types/temporal.adoc#cypher-temporal-specify-instant-examples[Examples] + *** xref::values-and-types/temporal.adoc#cypher-temporal-accessing-components-temporal-instants[Accessing components of temporal instants] + ** xref::values-and-types/temporal.adoc#cypher-temporal-durations[Durations] + *** xref::values-and-types/temporal.adoc#cypher-temporal-specifying-durations[Specifying durations] + **** xref::values-and-types/temporal.adoc#cypher-temporal-specify-duration-examples[Examples] + *** xref::values-and-types/temporal.adoc#cypher-temporal-accessing-components-durations[Accessing components of durations] + ** xref::values-and-types/temporal.adoc#cypher-temporal-examples[Examples] + ** xref::values-and-types/temporal.adoc#cypher-temporal-index[Temporal indexing] +* xref::values-and-types/spatial.adoc[Spatial values] + ** xref::values-and-types/spatial.adoc#spatial-values-introduction[Introduction] + ** xref::values-and-types/spatial.adoc#spatial-values-crs[Coordinate Reference Systems] + *** xref::values-and-types/spatial.adoc#spatial-values-crs-geographic[Geographic coordinate reference systems] + *** xref::values-and-types/spatial.adoc#spatial-values-crs-cartesian[Cartesian coordinate reference systems] + ** xref::values-and-types/spatial.adoc#spatial-values-spatial-instants[Spatial instants] + *** xref::values-and-types/spatial.adoc#spatial-values-spatial-instants-creating-points[Creating points] + *** xref::values-and-types/spatial.adoc#spatial-values-spatial-instants-accessing-components[Accessing components of points] + ** xref::values-and-types/spatial.adoc#spatial-values-point-index[Point index] +* xref::values-and-types/lists.adoc[Lists] + ** xref::values-and-types/lists.adoc#cypher-lists-general[Lists in general] + ** xref::values-and-types/lists.adoc#cypher-list-comprehension[List comprehension] + ** xref::values-and-types/lists.adoc#cypher-pattern-comprehension[Pattern comprehension] +* xref::values-and-types/maps.adoc[Maps] + ** xref::values-and-types/maps.adoc#cypher-literal-maps[Literal maps] + ** xref::values-and-types/maps.adoc#cypher-map-projection[Map projection] +* xref::values-and-types/working-with-null.adoc[Working with `null`] + ** xref::values-and-types/working-with-null.adoc#cypher-null-intro[Introduction to `null` in Cypher] + ** xref::values-and-types/working-with-null.adoc#cypher-null-logical-operators[Logical operations with `null`] + ** xref::values-and-types/working-with-null.adoc#cypher-null-bracket-operator[The `[\]` operator and `null`] + ** xref::values-and-types/working-with-null.adoc#cypher-null-in-operator[The `IN` operator and `null`] + ** xref::values-and-types/working-with-null.adoc#cypher-expressions-and-null[Expressions that return `null`] diff --git a/modules/ROOT/pages/syntax/lists.adoc b/modules/ROOT/pages/syntax/lists.adoc deleted file mode 100644 index 0db547521..000000000 --- a/modules/ROOT/pages/syntax/lists.adoc +++ /dev/null @@ -1,307 +0,0 @@ -:description: Cypher has comprehensive support for lists. - -[[cypher-lists]] -= Lists - -[abstract] --- -Cypher has comprehensive support for lists. --- - -[NOTE] -==== -Information regarding operators, such as list concatenation (`+`), element existence checking (`IN`), and access (`[]`) can be found xref::syntax/operators.adoc#query-operators-list[here]. -The behavior of the `IN` and `[]` operators with respect to `null` is detailed xref::syntax/working-with-null.adoc[here]. -==== - - -[[cypher-lists-general]] -== Lists in general - -A literal list is created by using brackets and separating the elements in the list with commas. - -.Query -[source, cypher] ----- -RETURN [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] AS list ----- - -.Result -[role="queryresult",options="header,footer",cols="1*+ -1+d|Rows: 1 -|=== - -.Query -[source, cypher] ----- -RETURN range(0, 10)[5..15] ----- - -.Result -[role="queryresult",options="header,footer",cols="1*(johnnymnemonic), - (keanu)-[:ACTED_IN]->(somethingsgottagive), - (keanu)-[:ACTED_IN]->(thematrixrevolutions), - (keanu)-[:ACTED_IN]->(thematrixreloaded), - (keanu)-[:ACTED_IN]->(thereplacements), - (keanu)-[:ACTED_IN]->(thematrix), - (keanu)-[:ACTED_IN]->(thedevilsadvocate), - (keanu)-[:ACTED_IN]->(matrix4) ----- -//// - -.Query -[source, cypher] ----- -MATCH (a:Person {name: 'Keanu Reeves'}) -RETURN [(a)-->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.released] AS years ----- - -.Result -[role="queryresult",options="header,footer",cols="1*(b:Movie) | b.released] AS years -UNWIND years AS year -WITH year ORDER BY year -RETURN COLLECT(year) AS sorted_years ----- - -.Result -[role="queryresult",options="header,footer",cols="1*` will be returned. - -//// -[source, cypher, role=test-setup] ----- -CREATE - (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), - (martin:Person {name: 'Martin Sheen'}), - (wallstreet:Movie {title: 'Wall Street', year: 1987}), - (reddawn:Movie {title: 'Red Dawn', year: 1984}), - (apocalypsenow:Movie {title: 'Apocalypse Now', year: 1979}), - (charlie)-[:ACTED_IN]->(wallstreet), - (charlie)-[:ACTED_IN]->(reddawn), - (charlie)-[:ACTED_IN]->(apocalypsenow), - (martin)-[:ACTED_IN]->(wallstreet), - (martin)-[:ACTED_IN]->(apocalypsenow) ----- -//// - -.Query -[source, cypher, indent=0] ----- -RETURN {key: 'Value', listKey: [{inner: 'Map1'}, {inner: 'Map2'}]} ----- - -.Result -[role="queryresult",options="header,footer",cols="1*`. -* Variable selector - Projects a variable, with the variable name as the key, and the value the variable is pointing to as the value of the projection. Its syntax is just the variable. -* All-properties selector - projects all key-value pairs from the `map_variable` value. - -The following conditions apply: - -* If the `map_variable` points to a `null` value, the whole map projection will evaluate to `null`. -* The key names in a map must be of type `String`. - - -[[cypher-map-projection-examples]] -=== Examples of map projections - -Find *'Charlie Sheen'* and return data about him and the movies he has acted in. -This example shows an example of map projection with a literal entry, which in turn also uses map projection inside the aggregating `collect()`. - -.Query -[source, cypher, indent=0] ----- -MATCH (actor:Person {name: 'Charlie Sheen'})-[:ACTED_IN]->(movie:Movie) -WITH actor, collect(movie{.title, .year}) AS movies -RETURN actor{.name, .realName, movies: movies} ----- - -.Result -[role="queryresult",options="header,footer",cols="1*(movie:Movie) -WITH actor, count(movie) AS nbrOfMovies -RETURN actor{.name, nbrOfMovies} ----- - -.Result -[role="queryresult",options="header,footer",cols="1* 2, name -> "Martin Sheen"}+ -| +{nbrOfMovies -> 3, name -> "Charlie Sheen"}+ -1+d|Rows: 2 -|=== - -Again, focusing on *'Charlie Sheen'*, this time returning all properties from the node. -Here we use an all-properties selector to project all the node properties, and additionally, explicitly project the property `age`. -Since this property does not exist on the node, a `null` value is projected instead. - -.Query -[source, cypher] ----- -MATCH (actor:Person {name: 'Charlie Sheen'}) -RETURN actor{.*, .age} ----- - -.Result -[role="queryresult",options="header,footer",cols="1*` operators. +Cypher supports comparing values (see xref::values-and-types/property-structural-composite.adoc[]) by equality using the `=` and `<>` operators. Values of the same type are only equal if they are the same identical value (e.g. `3 = 3` and `"x" <> "xy"`). @@ -370,18 +370,18 @@ For example, `1 > b` and `1 < b` are both false when b is NaN. * *Ordering* of spatial values: ** `ORDER BY` requires all values to be orderable. ** Points are ordered after arrays and before temporal types. - ** Points of different CRS are ordered by the CRS code (the value of SRID field). For the currently supported set of xref::syntax/spatial.adoc#cypher-spatial-crs[Coordinate Reference Systems] this means the order: 4326, 4979, 7302, 9157 + ** Points of different CRS are ordered by the CRS code (the value of SRID field). For the currently supported set of xref::values-and-types/spatial.adoc#cypher-spatial-crs[Coordinate Reference Systems] this means the order: 4326, 4979, 7302, 9157 ** Points of the same CRS are ordered by each coordinate value in turn, `x` first, then `y` and finally `z`. ** Note that this order is different to the order returned by the spatial index, which will be the order of the space filling curve. * *Comparison* of temporal values: - ** xref::syntax/temporal.adoc#cypher-temporal-instants[Temporal instant values] are comparable within the same type. + ** xref::values-and-types/temporal.adoc#cypher-temporal-instants[Temporal instant values] are comparable within the same type. An instant is considered less than another instant if it occurs before that instant in time, and it is considered greater than if it occurs after. ** Instant values that occur at the same point in time -- but that have a different time zone -- are not considered equal, and must therefore be ordered in some predictable way. Cypher prescribes that, after the primary order of point in time, instant values be ordered by effective time zone offset, from west (negative offset from UTC) to east (positive offset from UTC). This has the effect that times that represent the same point in time will be ordered with the time with the earliest local time first. If two instant values represent the same point in time, and have the same time zone offset, but a different named time zone (this is possible for _DateTime_ only, since _Time_ only has an offset), these values are not considered equal, and ordered by the time zone identifier, alphabetically, as its third ordering component. If the type, point in time, offset, and time zone name are all equal, then the values are equal, and any difference in order is impossible to observe. - ** xref::syntax/temporal.adoc#cypher-temporal-durations[_Duration_] values cannot be compared, since the length of a _day_, _month_ or _year_ is not known without knowing which _day_, _month_ or _year_ it is. + ** xref::values-and-types/temporal.adoc#cypher-temporal-durations[_Duration_] values cannot be compared, since the length of a _day_, _month_ or _year_ is not known without knowing which _day_, _month_ or _year_ it is. Since _Duration_ values are not comparable, the result of applying a comparison operator between two _Duration_ values is `null`. * *Ordering* of temporal values: ** `ORDER BY` requires all values to be orderable. @@ -395,23 +395,23 @@ For example, `1 > b` and `1 < b` are both false when b is NaN. * Comparing for ordering when one argument is `null` (e.g. `null < 3` is `null`). * *Ordering* of values with *different* types: ** The ordering is, in ascending order, defined according to the following list: - *** xref::syntax/maps.adoc#cypher-literal-maps[`Map`] - *** xref::syntax/values.adoc#structural-types[`Node`] - *** xref::syntax/values.adoc#structural-types[`Relationship`] - *** xref::syntax/lists.adoc[`List`] + *** xref::values-and-types/maps.adoc#cypher-literal-maps[`Map`] + *** xref::values-and-types/property-structural-composite.adoc#structural-types[`Node`] + *** xref::values-and-types/property-structural-composite.adoc#structural-types[`Relationship`] + *** xref::values-and-types/lists.adoc[`List`] *** xref::syntax/patterns.adoc#cypher-pattern-path-variables[`Path`] - *** xref::syntax/temporal.adoc[`DateTime`] - *** xref::syntax/temporal.adoc[`LocalDateTime`] - *** xref::syntax/temporal.adoc[`Date`] - *** xref::syntax/temporal.adoc[`Time`] - *** xref::syntax/temporal.adoc[`LocalTime`] - *** xref::syntax/temporal.adoc[`Duration`] + *** xref::values-and-types/temporal.adoc[`DateTime`] + *** xref::values-and-types/temporal.adoc[`LocalDateTime`] + *** xref::values-and-types/temporal.adoc[`Date`] + *** xref::values-and-types/temporal.adoc[`Time`] + *** xref::values-and-types/temporal.adoc[`LocalTime`] + *** xref::values-and-types/temporal.adoc[`Duration`] *** xref::syntax/expressions.adoc#cypher-expressions-general[`String`] *** xref::syntax/expressions.adoc#cypher-expressions-general[`Boolean`] *** xref::syntax/expressions.adoc#cypher-expressions-general[`Number`] ** The value `null` is considered larger than any value. * *Ordering* of composite type values: - ** For the xref::syntax/values.adoc#composite-types[composite types] (e.g. maps and lists), elements of the containers are compared pairwise for ordering and thus determine the ordering of two container types. + ** For the xref::values-and-types/property-structural-composite.adoc#composite-types[composite types] (e.g. maps and lists), elements of the containers are compared pairwise for ordering and thus determine the ordering of two container types. For example, `[1, 'foo', 3]` is ordered before `[1, 2, 'bar']` since `'foo'` is ordered before `2`. @@ -571,7 +571,7 @@ RETURN 'neo' + '4j' AS result Temporal operators comprise: -* adding a xref::syntax/temporal.adoc#cypher-temporal-durations[_Duration_] to either a xref::syntax/temporal.adoc#cypher-temporal-instants[temporal instant] or another _Duration_: `+` +* adding a xref::values-and-types/temporal.adoc#cypher-temporal-durations[_Duration_] to either a xref::values-and-types/temporal.adoc#cypher-temporal-instants[temporal instant] or another _Duration_: `+` * subtracting a _Duration_ from either a temporal instant or another _Duration_: `-` * multiplying a _Duration_ with a number: `*` * dividing a _Duration_ by a number: `/` @@ -609,17 +609,17 @@ The following table shows -- for each combination of operation and operand type | xref::syntax/operators.adoc#syntax-multiply-divide-duration-number[`*`] | _Duration_ -| xref::syntax/values.adoc#property-types[Number] +| xref::values-and-types/property-structural-composite.adoc#property-types[Number] | _Duration_ | xref::syntax/operators.adoc#syntax-multiply-divide-duration-number[`*`] -| xref::syntax/values.adoc#property-types[Number] +| xref::values-and-types/property-structural-composite.adoc#property-types[Number] | _Duration_ | _Duration_ | xref::syntax/operators.adoc#syntax-multiply-divide-duration-number[`/`] | _Duration_ -| xref::syntax/values.adoc#property-types[Number] +| xref::values-and-types/property-structural-composite.adoc#property-types[Number] | _Duration_ |=== @@ -645,7 +645,7 @@ RETURN aDateTime + aDuration, aDateTime - aDuration 2+d|Rows: 1 |=== -xref::syntax/temporal.adoc#cypher-temporal-duration-component[Components of a _Duration_] that do not apply to the temporal instant are ignored. +xref::values-and-types/temporal.adoc#cypher-temporal-duration-component[Components of a _Duration_] that do not apply to the temporal instant are ignored. For example, when adding a _Duration_ to a _Date_, the _hours_, _minutes_, _seconds_ and _nanoseconds_ of the _Duration_ are ignored (_Time_ behaves in an analogous manner): .Query @@ -738,7 +738,7 @@ The map operators comprise: [NOTE] ==== -The behavior of the `[]` operator with respect to `null` is detailed in xref::syntax/working-with-null.adoc#cypher-null-bracket-operator[]. +The behavior of the `[]` operator with respect to `null` is detailed in xref::values-and-types/working-with-null.adoc#cypher-null-bracket-operator[]. ==== @@ -789,7 +789,7 @@ RETURN a[$myKey] AS result 1+d|Rows: 1 |=== -More details on maps can be found in xref::syntax/maps.adoc[Maps]. +More details on maps can be found in xref::values-and-types/maps.adoc[Maps]. [[query-operators-list]] @@ -803,7 +803,7 @@ The list operators comprise: [NOTE] ==== -The behavior of the `IN` and `[]` operators with respect to `null` is detailed xref::syntax/working-with-null.adoc[here]. +The behavior of the `IN` and `[]` operators with respect to `null` is detailed xref::values-and-types/working-with-null.adoc[here]. ==== @@ -973,5 +973,5 @@ RETURN 3 IN l[0] AS result 1+d|Rows: 1 |=== -More details on lists can be found in xref::syntax/lists.adoc#cypher-lists-general[Lists in general]. +More details on lists can be found in xref::values-and-types/lists.adoc#cypher-lists-general[Lists in general]. diff --git a/modules/ROOT/pages/syntax/spatial.adoc b/modules/ROOT/pages/syntax/spatial.adoc deleted file mode 100644 index b5101392d..000000000 --- a/modules/ROOT/pages/syntax/spatial.adoc +++ /dev/null @@ -1,267 +0,0 @@ -:description: Cypher has built-in support for handling spatial values (points), and the underlying database supports storing these point values as properties on nodes and relationships. - -[[spatial-values]] -= Spatial values - -[abstract] --- -Cypher has built-in support for handling spatial values (points), and the underlying database supports storing these point values as properties on nodes and relationships. --- - - -[NOTE] -==== -Refer to xref::functions/spatial.adoc[Spatial functions] for information regarding spatial _functions_ allowing for the creation and manipulation of spatial values. - -Refer to xref::syntax/operators.adoc#cypher-ordering[Ordering and comparison of values] for information regarding the comparison and ordering of spatial values. -==== - - -[[spatial-values-introduction]] -== Introduction - -Neo4j supports only one type of spatial geometry, the _Point_ with the following characteristics: - -* Each point can have either 2 or 3 dimensions. This means it contains either 2 or 3 64-bit floating point values, which together are called the _Coordinate_. -* Each point will also be associated with a specific xref::syntax/spatial.adoc#cypher-spatial-crs[Coordinate Reference System] (CRS) that determines the meaning of the values in the _Coordinate_. -* Instances of _Point_ and lists of _Point_ can be assigned to node and relationship properties. -* Nodes with _Point_ or _List(Point)_ properties can be indexed using a point index. This is true for all CRS (and for both 2D and 3D). -* The xref::functions/spatial.adoc#functions-distance[distance function] will work on points in all CRS and in both 2D and 3D but only if the two points have the same CRS (and therefore also same dimension). - - -[[spatial-values-crs]] -== Coordinate Reference Systems - -Four Coordinate Reference Systems (CRS) are supported, each of which falls within one of two types: _geographic coordinates_ modeling points on the earth, or _cartesian coordinates_ modeling points in euclidean space: - -* xref::syntax/spatial.adoc#cypher-spatial-crs-geographic[Geographic coordinate reference systems] - ** WGS-84: longitude, latitude (x, y) - ** WGS-84-3D: longitude, latitude, height (x, y, z) -* xref::syntax/spatial.adoc#cypher-spatial-crs-cartesian[Cartesian coordinate reference systems] - ** Cartesian: x, y - ** Cartesian 3D: x, y, z - -Data within different coordinate systems are entirely incomparable, and cannot be implicitly converted from one to the other. -This is true even if they are both cartesian or both geographic. For example, if you search for 3D points using a 2D range, you will get no results. -However, they can be ordered, as discussed in more detail in xref::syntax/operators.adoc#cypher-ordering[Ordering and comparison of values]. - - -[[spatial-values-crs-geographic]] -=== Geographic coordinate reference systems - -Two Geographic Coordinate Reference Systems (CRS) are supported, modeling points on the earth: - -* link:https://spatialreference.org/ref/epsg/4326/[WGS 84 2D] - ** A 2D geographic point in the _WGS 84_ CRS is specified in one of two ways: - *** `longitude` and `latitude` (if these are specified, and the `crs` is not, then the `crs` is assumed to be `WGS-84`) - *** `x` and `y` (in this case the `crs` must be specified, or will be assumed to be `Cartesian`) - ** Specifying this CRS can be done using either the name 'wgs-84' or the SRID 4326 as described in xref::functions/spatial.adoc#functions-point-wgs84-2d[Point(WGS-84)] -* link:https://spatialreference.org/ref/epsg/4979/[WGS 84 3D] - ** A 3D geographic point in the _WGS 84_ CRS is specified one of in two ways: - *** `longitude`, `latitude` and either `height` or `z` (if these are specified, and the `crs` is not, then the `crs` is assumed to be `WGS-84-3D`) - *** `x`, `y` and `z` (in this case the `crs` must be specified, or will be assumed to be `Cartesian-3D`) - ** Specifying this CRS can be done using either the name 'wgs-84-3d' or the SRID 4979 as described in xref::functions/spatial.adoc#functions-point-wgs84-3d[Point(WGS-84-3D)] - -The units of the `latitude` and `longitude` fields are in decimal degrees, and need to be specified as floating point numbers using Cypher literals. -It is not possible to use any other format, like 'degrees, minutes, seconds'. The units of the `height` field are in meters. When geographic points -are passed to the `distance` function, the result will always be in meters. If the coordinates are in any other format or unit than supported, it -is necessary to explicitly convert them. -For example, if the incoming `$height` is a string field in kilometers, you would need to type `height: toFloat($height) * 1000`. Likewise if the -results of the `distance` function are expected to be returned in kilometers, an explicit conversion is required. -For example: `RETURN point.distance(a,b) / 1000 AS km`. An example demonstrating conversion on incoming and outgoing values is: - -.Query -[source, cypher, indent=0] ----- -WITH - point({latitude:toFloat('13.43'), longitude:toFloat('56.21')}) AS p1, - point({latitude:toFloat('13.10'), longitude:toFloat('56.41')}) AS p2 -RETURN toInteger(point.distance(p1, p2)/1000) AS km ----- - -.Result -[role="queryresult",options="header,footer",cols="1*+ | +5.0+ -2+d|Rows: 1 -|=== - - -[[spatial-values-spatial-instants]] -== Spatial instants - -[[spatial-values-spatial-instants-creating-points]] -=== Creating points - -All point types are created from two components: - -* The _Coordinate_ containing either 2 or 3 floating point values (64-bit) -* The Coordinate Reference System (or CRS) defining the meaning (and possibly units) of the values in the _Coordinate_ - -For most use cases it is not necessary to specify the CRS explicitly as it will be deduced from the keys used to specify the coordinate. Two rules -are applied to deduce the CRS from the coordinate: - -* Choice of keys: - ** If the coordinate is specified using the keys `latitude` and `longitude` the CRS will be assumed to be _Geographic_ and therefor either `WGS-84` or `WGS-84-3D`. - ** If instead `x` and `y` are used, then the default CRS would be `Cartesian` or `Cartesian-3D` -* Number of dimensions: - ** If there are 2 dimensions in the coordinate, `x` & `y` or `longitude` & `latitude` the CRS will be a 2D CRS - ** If there is a third dimensions in the coordinate, `z` or `height` the CRS will be a 3D CRS - -All fields are provided to the `point` function in the form of a map of explicitly named arguments. We specifically do not support an ordered list -of coordinate fields because of the contradictory conventions between geographic and cartesian coordinates, where geographic coordinates normally -list `y` before `x` (`latitude` before `longitude`). -See for example the following query which returns points created in each of the four supported CRS. Take particular note of the order and keys -of the coordinates in the original `point` function calls, and how those values are displayed in the results: - -.Query -[source, cypher, indent=0] ----- -RETURN - point({x: 3, y: 0}) AS cartesian_2d, - point({x: 0, y: 4, z: 1}) AS cartesian_3d, - point({latitude: 12, longitude: 56}) AS geo_2d, - point({latitude: 12, longitude: 56, height: 1000}) AS geo_3d ----- - -.Result -[role="queryresult",options="header,footer",cols="4*+`, and `+>=+`, or the specific order of an `ORDER BY n.point` query will need to be rewritten. - -The most efficient way to do this is to explicitly specify the ordering. -For example, by using `point.x`, `point.y` in _cartesian coordinates_, or `point.longitude` and `point.latitude` in _geographic coordinates_. - diff --git a/modules/ROOT/pages/values-and-types/casting-data.adoc b/modules/ROOT/pages/values-and-types/casting-data.adoc new file mode 100644 index 000000000..0287ab7cd --- /dev/null +++ b/modules/ROOT/pages/values-and-types/casting-data.adoc @@ -0,0 +1,171 @@ +:description: This section provides information about how to cast data values using Cypher functions. +[[casting-data-values]] += Casting data values + +Cypher supports a number of functions to cast values to different data types. +This section will provide an overview of those functions, as well examples of how to use them in practice. + +== Functions for converting data values + +The following functions are available for casting data values: + +[options="header", cols="m,2a"] +|=== +| Function | Description + +| toBoolean() | Converts a string, integer, or boolean value to a boolean value. + +| toBooleanList() | Converts a list of values and returns a list of boolean values. +If any values are not convertible to boolean they will be `null` in the list returned. + +| toBooleanOrNull() | Converts a string, integer or boolean value to a boolean value. +For any other input value, `null` will be returned. + +| toFloat() | Converts an integer, floating point, or a string value to a floating point number value. +Otherwise `null` is returned. + +| toFloatList() | Converts a list of values and returns a list of floating point values. +If any values are not convertible to floating point they will be `null` in the list returned. + +| toFloatOrNull() | Converts an integer, floating point, or a string value to a floating point number. +For any other input value, `null` will be returned. + +| toInteger() | Converts a boolean, integer, floating point or a string value to an integer value. + +| toIntegerList() | Converts a list of values and returns a list of integer values. If any values are not convertible to integer they will be `null` in the list returned. + +| toIntegertOrNull() | Converts a boolean, integer, floating point or a string value to an integer value. +For any other input value, `null` will be returned. + +| toString() | Converts an integer, float, boolean, string, point, duration, date, time, localtime, localdatetime, or datetime value to a string value. + +| toStringList() | Converts a list of values and returns a list of string values. +If any values are not convertible to string they will be `null` in the list returned. + +| toStringOrNull() | Converts an integer, float, boolean, string, point, duration, date, time, localtime, localdatetime, or datetime value to a string. +For any other input value, `null` will be returned. +|=== + +More information about these, and many other functions, can be found in the section on xref:functions/index.adoc[]. + +[[converting-data-values-examples]] +== Examples + +The following graph is used for the examples below: + +image::values_and_types_converting_data_graph.svg[] + +To recreate it, run the following query against an empty Neo4j database: + +[source, cypher, role=noheader,test-setup] +---- +CREATE (keanu:Person {name:'Keanu Reeves', age: 58, active:true}), + (carrieAnne:Person {name:'Carrie-Anne Moss', age: 55, active:true}), + (keanu)-[r:KNOWS {since:1999}]->(carrieAnne) +---- + +[[converting-data-values-examples-returning-converted-values]] +=== Returning converted values + +In the below query, the function `toFloat` is used to cast two string values. +It shows that `null` is returned if the data casting is not possible. + +[source, cypher] +---- +MATCH (keanu:Person {name:'Keanu Reeves'}) +RETURN toFloat(keanu.age), toInteger(keanu.name) +---- + +.Result +[role="queryresult",options="header,footer",cols="2*+ +1+d|Rows: 1 +|=== + +.Query +[source, cypher] +---- +RETURN range(0, 10)[5..15] AS list +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(johnnyMnemonic), + (keanu)-[:ACTED_IN]->(theMatrixRevolutions), + (keanu)-[:ACTED_IN]->(theMatrixReloaded), + (keanu)-[:ACTED_IN]->(theReplacements), + (keanu)-[:ACTED_IN]->(theMatrix), + (keanu)-[:ACTED_IN]->(theDevilsAdvocate), + (keanu)-[:ACTED_IN]->(theMatrixResurrections) +---- + +=== Examples + +This example returns a list that contains the year when the movies were released. +The pattern matching in the pattern comprehension looks for `Matrix` in the movie title and that the node `keanu` (`Person` node with the name `Keanu Reeves`) has a relationship with the movie. + +.Query +[source, cypher] +---- +MATCH (keanu:Person {name: 'Keanu Reeves'}) +RETURN [(keanu)-->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.released] AS years +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(b:Movie) | b.title] AS movieTitles +SET keanu.resume = movieTitles +RETURN keanu.resume +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(b:Movie) | b.title] + [(keanu)-->(b:Movie) | b.released] AS movieTitles +SET keanu.resume = movieTitles +RETURN keanu.resume +---- + +[source,error] +---- +Neo4j only supports a subset of Cypher types for storage as singleton or array properties. Please refer to section cypher/syntax/values of the manual for more details. +---- + + +[[cypher-list-comprehension]] +== List comprehension + +List comprehension is a syntactic construct available in Cypher for creating a list based on existing lists. + +For example, the following query returns a new list from the previously created `resume` property (a list of strings) of `Keanu Reeves`: + +.Query +[source, cypher] +---- +MATCH (keanu:Person {name:'Keanu Reeves'}) +RETURN [x IN keanu.resume WHERE x contains 'The Matrix'] AS matrixList +---- + +.Result +[role="queryresult",options="header,footer",cols="1*` will be returned. + + +.Query +[source, cypher, indent=0] +---- +RETURN {key: 'Value', listKey: [{inner: 'Map1'}, {inner: 'Map2'}]} +---- + +.Result +[role="queryresult",options="header,footer",cols="1*`. +* Variable selector - Projects a variable, with the variable name as the key, and the value the variable is pointing to as the value of the projection. +Its syntax is just the variable. +* All-properties selector - projects all key-value pairs from the `map_variable` value. + +The following conditions apply: + +* If the `map_variable` points to a `null` value, the whole map projection will evaluate to `null`. +* The key names in a map must be of type `String`. + + +[[cypher-map-projection-examples]] +=== Example graph + +The following graph is used for the examples below: + +image::values_and_types_maps_graph.svg[] + +To recreate the graph, run the following query against an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE + (keanu:Person {name: 'Keanu Reeves', nationality: 'Canadian'}), + (carrieAnne:Person {name: 'Carrie-Anne Moss'}), + (theMatrixRevolutions:Movie {title: 'The Matrix Revolutions', released: 2003}), + (theMatrixReloaded:Movie {title: 'The Matrix Reloaded', released: 2003}), + (theMatrix:Movie {title: 'The Matrix', released: 1999}), + (theDevilsAdvocate:Movie {title: 'The Devils Advocate', released: 1997}), + (theMatrixResurrections:Movie {title: 'The Matrix Resurrections', released: 2021}), + (keanu)-[:ACTED_IN]->(theMatrix), + (keanu)-[:ACTED_IN]->(theMatrixRevolutions), + (keanu)-[:ACTED_IN]->(theMatrixReloaded), + (keanu)-[:ACTED_IN]->(theMatrixResurrections), + (keanu)-[:ACTED_IN]->(theDevilsAdvocate), + (carrieAnne)-[:ACTED_IN]->(theMatrix), + (carrieAnne)-[:ACTED_IN]->(theMatrixRevolutions), + (carrieAnne)-[:ACTED_IN]->(theMatrixReloaded), + (carrieAnne)-[:ACTED_IN]->(theMatrixResurrections) +---- + +=== Examples + +The below query finds the `Keanu Reeves` node and the movies he has acted in. +It is an example of a map projection with a literal entry, which in turn also uses map projection inside the aggregating xref:functions/aggregating.adoc#functions-collect[collect()] function. + +.Query +[source, cypher, indent=0] +---- +MATCH (keanu:Person {name: 'Keanu Reeves'})-[:ACTED_IN]->(movie:Movie) +WITH keanu, collect(movie{.title, .released}) AS movies +RETURN keanu{.name, movies: movies} +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(movie:Movie) +WITH actor, count(movie) AS numberOfMovies +RETURN actor{.name, numberOfMovies} +---- + +.Result +[role="queryresult",options="header,footer",cols="1*+ | +5.0+ +2+d|Rows: 1 +|=== + + +[[spatial-values-spatial-instants]] +== Spatial instants + + +All point types are created from two components: + +* The _Coordinate_ containing either 2 or 3 floating point values (64-bit). +* The Coordinate Reference System (or CRS) defining the meaning (and possibly units) of the values in the _Coordinate_. + +For most use cases, it is not necessary to specify the CRS explicitly as it will be deduced from the keys used to specify the coordinate. +Two rules are applied to deduce the CRS from the coordinate: + +* Choice of keys: + ** If the coordinate is specified using the keys `latitude` and `longitude` the CRS will be assumed to be _Geographic_ and therefor either `WGS-84` or `WGS-84-3D`. + ** If instead `x` and `y` are used, then the default CRS would be `Cartesian` or `Cartesian-3D`. +* Number of dimensions: + ** If there are 2 dimensions in the coordinate, `x` & `y` or `longitude` & `latitude` the CRS will be a 2D CRS. + ** If there is a third dimensions in the coordinate, `z` or `height` the CRS will be a 3D CRS. + +All fields are provided to the `point` function in the form of a map of explicitly named arguments. +Neo4j does not support an ordered list of coordinate fields because of the contradictory conventions between geographic and cartesian coordinates, where geographic coordinates normally +list `y` before `x` (`latitude` before `longitude`). + +The following query which returns points created in each of the four supported CRSs. +Take particular note of the order and keys of the coordinates in the original `point` function, and how those values are displayed in the results: + +.Query +[source, cypher, indent=0] +---- +RETURN + point({x: 3, y: 0}) AS cartesian_2d, + point({x: 0, y: 4, z: 1}) AS cartesian_3d, + point({latitude: 12, longitude: 56}) AS geo_2d, + point({latitude: 12, longitude: 56, height: 1000}) AS geo_3d +---- + +.Result +[role="queryresult",options="header,footer",cols="4*+`, and `+>=+`. +Attempting to do so will return `null`. + +To compare spatial points within a specific range, instead use the spatial functions xref::functions/spatial.adoc#functions-distance[point.distance] or xref::functions/spatial.adoc#functions-withinBBox[point.withinBBox]. + + diff --git a/modules/ROOT/pages/syntax/temporal.adoc b/modules/ROOT/pages/values-and-types/temporal.adoc similarity index 94% rename from modules/ROOT/pages/syntax/temporal.adoc rename to modules/ROOT/pages/values-and-types/temporal.adoc index aad72d3c4..7cd5f4255 100644 --- a/modules/ROOT/pages/syntax/temporal.adoc +++ b/modules/ROOT/pages/values-and-types/temporal.adoc @@ -1,12 +1,11 @@ :description: Cypher has built-in support for handling temporal values, and the underlying database supports storing these temporal values as properties on nodes and relationships. [[cypher-temporal]] -= Temporal (Date/Time) values += Temporal values -[abstract] --- -Cypher has built-in support for handling temporal values, and the underlying database supports storing these temporal values as properties on nodes and relationships. --- + +Cypher has built-in support for handling temporal values, which can be stored as properties on nodes and relationships in Neo4j databases. +This section will discuss how Cypher handles time zones, before exploring temporal values in more detail. [NOTE] ==== @@ -15,7 +14,9 @@ Cypher has built-in support for handling temporal values, and the underlying dat * Refer to xref::syntax/operators.adoc#cypher-ordering[Ordering and comparison of values] for information regarding the comparison and ordering of temporal values. ==== -The following table lists the temporal value types and supported components: +== Temporal value types + +The following table lists the temporal value types and their supported components: [options="header", cols="^,^,^,^"] |=== @@ -40,7 +41,7 @@ A `Duration` represents a temporal amount, capturing the difference in time betw == Time zones Time zones are represented either as an offset from UTC, or as a logical identifier of a _named time zone_ (these are based on the link:https://www.iana.org/time-zones[IANA time zone database]). -In either case the time is stored as UTC internally, and the time zone offset is only applied when the time is presented. +In either case, the time is stored as UTC internally, and the time zone offset is only applied when the time is presented. This means that temporal instants can be ordered without taking time zone into account. If, however, two times are identical in UTC, then they are ordered by timezone. @@ -57,7 +58,7 @@ There are three ways of specifying a time zone in Cypher: * Specifying a named time zone. * Specifying both the offset and the time zone name (with the requirement that these match). -See xref::syntax/temporal.adoc#cypher-temporal-specify-time-zone[Specifying time zones] for examples. +See xref::values-and-types/temporal.adoc#cypher-temporal-specify-time-zone[specifying time zones] for examples. The named time zone form uses the rules of the IANA time zone database to manage _daylight savings time_ (DST). @@ -95,12 +96,12 @@ The character `T` is a literal character. [[cypher-temporal-specify-date]] -==== Specifying dates +=== Specifying dates [options="header"] |=== | Component | Format | Description -| Year | `YYYY` | Specified with at least four digits (xref::syntax/temporal.adoc#cypher-temporal-year[special rules apply in certain cases]). +| Year | `YYYY` | Specified with at least four digits (xref::values-and-types/temporal.adoc#cypher-temporal-year[special rules apply in certain cases]). | Month | `MM` | Specified with a double digit number from `01` to `12`. | Week | `ww` | Always prefixed with `W` and specified with a double digit number from `01` to `53`. | Quarter | `q` | Always prefixed with `Q` and specified with a single digit number from `1` to `4`. @@ -153,7 +154,7 @@ For example, `2013-06` will be interpreted as being the same date as `2013-06-01 [[cypher-temporal-specify-time]] -==== Specifying times +=== Specifying times [options="header"] |=== @@ -161,7 +162,7 @@ For example, `2013-06` will be interpreted as being the same date as `2013-06-01 | `Hour` | `HH` | Specified with a double digit number from `00` to `23`. | `Minute` | `MM` | Specified with a double digit number from `00` to `59`. | `Second` | `SS` | Specified with a double digit number from `00` to `59`. -| `fraction` | `sssssssss` | Specified with a number from `0` to `999999999`. It is not required to specify trailing zeros. +| `fraction` | `sssssssss` | Specified with a number from `0` to `999999999`. It is not required to specify leading zeros. `fraction` is an optional, sub-second component of `Second`. This can be separated from `Second` using either a full stop (`.`) or a comma (`,`). The `fraction` is in addition to the two digits of `Second`. @@ -227,125 +228,8 @@ The following formats are supported for specifying time zones: | `[ZoneName]` | `[ZoneName]` | `[America/Regina]` | {check-mark} | |=== - -[[cypher-temporal-specify-instant-examples]] -==== Examples - -Here are examples of parsing temporal instant values using various formats. - -For more details, refer to xref::functions/temporal/index.adoc#functions-temporal-create-overview[An overview of temporal instant type creation]. - -.+datetime+ -====== - -Parsing a _DateTime_ using the _calendar date_ format: - -.Query -[source, cypher] ----- -RETURN datetime('2015-06-24T12:50:35.556+0100') AS theDateTime ----- - -.Result -[role="queryresult",options="header,footer",cols="1*T

Sourced from asciidoctor-kroki's releases.

🐙 JavaScript - v0.18.1

What's Changed

Improvements

Bug fixes

Documentation

New Contributors

Full Changelog: https://github.com/ggrossetie/asciidoctor-kroki/compare/v0.17.0...v0.18.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=asciidoctor-kroki&package-manager=npm_and_yarn&previous-version=0.17.0&new-version=0.18.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb20c1fe4..b1da1f6f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@neo4j-antora/mark-terms": "1.1.0", "@neo4j-documentation/macros": "^1.0.2", "@neo4j-documentation/remote-include": "^1.0.0", - "asciidoctor-kroki": "^0.17.0" + "asciidoctor-kroki": "^0.18.1" }, "devDependencies": { "express": "^4.17.1", @@ -401,9 +401,9 @@ "dev": true }, "node_modules/asciidoctor-kroki": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/asciidoctor-kroki/-/asciidoctor-kroki-0.17.0.tgz", - "integrity": "sha512-aObUUfAtcfUTjhAP32bgrcoKXLRta57o3V5k+t73FXDKiLi+QfkHE+9+H4mGPTnghXBtiRYzsu7BbVGfTHoQzQ==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/asciidoctor-kroki/-/asciidoctor-kroki-0.18.1.tgz", + "integrity": "sha512-eQxbBCaPTbyNoJtk62Gp+6h4LlJp2147g7eS0QIVjqaLpFa8sseH0BlMiBoATrJUYv1w3nR+FTzvloBJ/MioYg==", "dependencies": { "json5": "2.2.3", "mkdirp": "2.1.3", @@ -415,7 +415,7 @@ "node": ">=10" }, "peerDependencies": { - "@asciidoctor/core": "~2.2" + "@asciidoctor/core": ">=2.2 <4.0" } }, "node_modules/asciidoctor-kroki/node_modules/unxhr": { @@ -3316,9 +3316,9 @@ "dev": true }, "asciidoctor-kroki": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/asciidoctor-kroki/-/asciidoctor-kroki-0.17.0.tgz", - "integrity": "sha512-aObUUfAtcfUTjhAP32bgrcoKXLRta57o3V5k+t73FXDKiLi+QfkHE+9+H4mGPTnghXBtiRYzsu7BbVGfTHoQzQ==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/asciidoctor-kroki/-/asciidoctor-kroki-0.18.1.tgz", + "integrity": "sha512-eQxbBCaPTbyNoJtk62Gp+6h4LlJp2147g7eS0QIVjqaLpFa8sseH0BlMiBoATrJUYv1w3nR+FTzvloBJ/MioYg==", "requires": { "json5": "2.2.3", "mkdirp": "2.1.3", diff --git a/package.json b/package.json index 0af9b2b1e..b2681c50b 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@neo4j-antora/mark-terms": "1.1.0", "@neo4j-documentation/macros": "^1.0.2", "@neo4j-documentation/remote-include": "^1.0.0", - "asciidoctor-kroki": "^0.17.0" + "asciidoctor-kroki": "^0.18.1" }, "devDependencies": { "express": "^4.17.1", From 4721f60ef50891929c9977ed183d98e051e3b0f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 11:33:58 +0100 Subject: [PATCH 341/383] Bump @antora/cli from 3.1.3 to 3.1.6 (#820) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [@antora/cli](https://gitlab.com/antora/antora) from 3.1.3 to 3.1.6.
Changelog

Sourced from @​antora/cli's changelog.

== 3.1.6 (2023-12-19)

=== Added

  • playbook-builder: Add git.read_concurrency playbook key to control how many git indexes to load into memory at once (#1096)

=== Changed

  • playbook-builder: Change default value of git.fetch_concurrency to 1 (#1096)
  • content-aggregator: Don't read git tree twice if start path is empty; add dirname to tree object in same function
  • content-aggregator: Fill in progress bar with incomplete marks if cloning a repository fails
  • content-aggregator: Mention that credentials may have been rejected if server requests them, then sends 404 response
  • content-aggregator: Flag git clone/fetch error as recoverable if an unexpected network error occurs (#1096)
  • content-aggregator: Decouple step to load (fetch or clone) repositories from step to scan repositories to discover references and start paths and collect files (#1096)

=== Fixed

  • logger: Add hostname to ignore list to prevent hostname property on logged error from modifying logger name (#1070)
  • content-aggregator: Retry failed fetch/clone operations in serial if git.fetch_concurrency > 1 and an unexpected error occurs (#1096)

== 3.1.5 (2023-11-23)

=== Fixed

  • asciidoc-loader: Do not fail to load AsciiDoc if target of image macro resolves to an unpublished image (#1092)
  • site-generator: Retry loadUi in isolation after aggregateContent if network connection occurs when retrieving remote UI bundle (#1095)
  • cli: Remove empty default on description for --extension option

=== Changed

  • ui-loader: Change gulp-vinyl-zip dependency to @​vscode/gulp-vinyl-zip (no functional changes)
  • asciidoc-loader: Fix relative path computation when path is extensionless and to matches parent folder of from (#1094)
  • page-composer: Fix result of relativize helper when path is extensionless and to matches parent folder of from (#1094)
  • file-publisher: Change gulp-vinyl-zip dependency to @​vscode/gulp-vinyl-zip (no functional changes)

== 3.1.4 (2023-06-08)

=== Fixed

  • content-aggregator: Try credentials from credentials store if auth fails with credentials in URL (#1063)
  • content-aggregator: Consider local branches in non-managed bare repository that has at least one remote branch (#1064)
Commits
  • 14a6725 release 3.1.6
  • fd16b9c update CHANGELOG and what's new page for upcoming 3.1.6 release
  • e4abd1d backport fix for #1070 add hostname to ignore list on pino-pretty settings to...
  • 13c0796 a provision of #1096 select all start paths before collecting files so start ...
  • f8a1191 a provision of #1096 retry failed fetch/clone operations in serial if git.fet...
  • ceb40b0 a provision of #1096 add git.read_concurrency playbook key to control how man...
  • 09219b3 resolves #1096 decouple step to load repositories when collecting files
  • d1ee749 a provision of #1096 flag unknown git errors a recoverable to retry without c...
  • 1ad2196 replace trimRight with trimEnd
  • 2da2f16 extract identifyAuthStatus function
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@antora/cli&package-manager=npm_and_yarn&previous-version=3.1.3&new-version=3.1.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- package-lock.json | 219 +++++++++++++++++++++++++++++++++++++++++----- package.json | 2 +- 2 files changed, 200 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index b1da1f6f6..36411e0d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "5.0.0", "license": "ISC", "dependencies": { - "@antora/cli": "^3.1.3", + "@antora/cli": "^3.1.6", "@antora/site-generator-default": "^3.1.3", "@neo4j-antora/antora-add-notes": "^0.1.6", "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", @@ -39,14 +39,14 @@ } }, "node_modules/@antora/cli": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.3.tgz", - "integrity": "sha512-ABngPywk4yZfUE/gaej7ApFeB/0cr5bkW7S4wqObzU5m29q2wDcvrtn/c2mrzFSeSDNAf9y7RCRnudAV5SteBA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.6.tgz", + "integrity": "sha512-aJLN6JyXYUfMfMVr/6JEenxnnG8evKfyp01wd/Cj7mkJXD/lj/Gqws2bXyP/hjUEl/HuX4/P9AcIfMLT/vfQJw==", "dependencies": { - "@antora/logger": "3.1.3", - "@antora/playbook-builder": "3.1.3", + "@antora/logger": "3.1.6", + "@antora/playbook-builder": "3.1.6", "@antora/user-require-helper": "~2.0", - "commander": "~9.4" + "commander": "~10.0" }, "bin": { "antora": "bin/antora" @@ -55,6 +55,102 @@ "node": ">=16.0.0" } }, + "node_modules/@antora/cli/node_modules/@antora/logger": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.6.tgz", + "integrity": "sha512-36kU8gMbPslcPu8u9EbXsz6+9G9+LWPKhO7n8mEQqxlcCqmChwgetK6VbsL102rynpgPsstX6IAKg2wbptJK5g==", + "dependencies": { + "@antora/expand-path-helper": "~2.0", + "pino": "~8.14", + "pino-pretty": "~10.0", + "sonic-boom": "~3.3" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@antora/cli/node_modules/@antora/playbook-builder": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.6.tgz", + "integrity": "sha512-bZcDastZViAgPVPNvvbbw7ci63gL5YnyG5X7NuHJoORgzyGQAsMYEjzfa9yfNfXubUmXv/oSteUSxbACjdjzWg==", + "dependencies": { + "@iarna/toml": "~2.2", + "convict": "~6.2", + "js-yaml": "~4.1", + "json5": "~2.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@antora/cli/node_modules/pino": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.14.2.tgz", + "integrity": "sha512-zKu9aWeSWTy1JgvxIpZveJKKsAr4+6uNMZ0Vf0KRwzl/UNZA3XjHiIl/0WwqLMkDwuHuDkT5xAgPA2jpKq4whA==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.0.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^2.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.1.0", + "thread-stream": "^2.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/@antora/cli/node_modules/pino-pretty": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.0.1.tgz", + "integrity": "sha512-yrn00+jNpkvZX/NrPVCPIVHAfTDy3ahF0PND9tKqZk4j9s+loK8dpzrJj4dGb7i+WLuR50ussuTAiWoMWU+qeA==", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^4.0.1", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.0.0", + "pump": "^3.0.0", + "readable-stream": "^4.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/@antora/cli/node_modules/readable-stream": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", + "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@antora/cli/node_modules/sonic-boom": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", + "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/@antora/content-aggregator": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.3.tgz", @@ -710,11 +806,11 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, "node_modules/commander": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", - "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "engines": { - "node": "^12.20.0 || >=14" + "node": ">=14" } }, "node_modules/concat-map": { @@ -3025,14 +3121,97 @@ } }, "@antora/cli": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.3.tgz", - "integrity": "sha512-ABngPywk4yZfUE/gaej7ApFeB/0cr5bkW7S4wqObzU5m29q2wDcvrtn/c2mrzFSeSDNAf9y7RCRnudAV5SteBA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.6.tgz", + "integrity": "sha512-aJLN6JyXYUfMfMVr/6JEenxnnG8evKfyp01wd/Cj7mkJXD/lj/Gqws2bXyP/hjUEl/HuX4/P9AcIfMLT/vfQJw==", "requires": { - "@antora/logger": "3.1.3", - "@antora/playbook-builder": "3.1.3", + "@antora/logger": "3.1.6", + "@antora/playbook-builder": "3.1.6", "@antora/user-require-helper": "~2.0", - "commander": "~9.4" + "commander": "~10.0" + }, + "dependencies": { + "@antora/logger": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.6.tgz", + "integrity": "sha512-36kU8gMbPslcPu8u9EbXsz6+9G9+LWPKhO7n8mEQqxlcCqmChwgetK6VbsL102rynpgPsstX6IAKg2wbptJK5g==", + "requires": { + "@antora/expand-path-helper": "~2.0", + "pino": "~8.14", + "pino-pretty": "~10.0", + "sonic-boom": "~3.3" + } + }, + "@antora/playbook-builder": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.6.tgz", + "integrity": "sha512-bZcDastZViAgPVPNvvbbw7ci63gL5YnyG5X7NuHJoORgzyGQAsMYEjzfa9yfNfXubUmXv/oSteUSxbACjdjzWg==", + "requires": { + "@iarna/toml": "~2.2", + "convict": "~6.2", + "js-yaml": "~4.1", + "json5": "~2.2" + } + }, + "pino": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.14.2.tgz", + "integrity": "sha512-zKu9aWeSWTy1JgvxIpZveJKKsAr4+6uNMZ0Vf0KRwzl/UNZA3XjHiIl/0WwqLMkDwuHuDkT5xAgPA2jpKq4whA==", + "requires": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.0.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^2.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.1.0", + "thread-stream": "^2.0.0" + } + }, + "pino-pretty": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.0.1.tgz", + "integrity": "sha512-yrn00+jNpkvZX/NrPVCPIVHAfTDy3ahF0PND9tKqZk4j9s+loK8dpzrJj4dGb7i+WLuR50ussuTAiWoMWU+qeA==", + "requires": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^4.0.1", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.0.0", + "pump": "^3.0.0", + "readable-stream": "^4.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "readable-stream": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", + "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + } + }, + "sonic-boom": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", + "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", + "requires": { + "atomic-sleep": "^1.0.0" + } + } } }, "@antora/content-aggregator": { @@ -3540,9 +3719,9 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, "commander": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", - "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==" + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" }, "concat-map": { "version": "0.0.1", diff --git a/package.json b/package.json index b2681c50b..833622a15 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "author": "Neo4j", "license": "ISC", "dependencies": { - "@antora/cli": "^3.1.3", + "@antora/cli": "^3.1.6", "@antora/site-generator-default": "^3.1.3", "@neo4j-antora/antora-add-notes": "^0.1.6", "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", From 4fc46629fa2ee132b963da47cebcb7a2fbf4085d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 13:15:33 +0100 Subject: [PATCH 342/383] Bump @antora/site-generator-default from 3.1.3 to 3.1.6 (#819) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [@antora/site-generator-default](https://gitlab.com/antora/antora) from 3.1.3 to 3.1.6.
Changelog

Sourced from @​antora/site-generator-default's changelog.

== 3.1.6 (2023-12-19)

=== Added

  • playbook-builder: Add git.read_concurrency playbook key to control how many git indexes to load into memory at once (#1096)

=== Changed

  • playbook-builder: Change default value of git.fetch_concurrency to 1 (#1096)
  • content-aggregator: Don't read git tree twice if start path is empty; add dirname to tree object in same function
  • content-aggregator: Fill in progress bar with incomplete marks if cloning a repository fails
  • content-aggregator: Mention that credentials may have been rejected if server requests them, then sends 404 response
  • content-aggregator: Flag git clone/fetch error as recoverable if an unexpected network error occurs (#1096)
  • content-aggregator: Decouple step to load (fetch or clone) repositories from step to scan repositories to discover references and start paths and collect files (#1096)

=== Fixed

  • logger: Add hostname to ignore list to prevent hostname property on logged error from modifying logger name (#1070)
  • content-aggregator: Retry failed fetch/clone operations in serial if git.fetch_concurrency > 1 and an unexpected error occurs (#1096)

== 3.1.5 (2023-11-23)

=== Fixed

  • asciidoc-loader: Do not fail to load AsciiDoc if target of image macro resolves to an unpublished image (#1092)
  • site-generator: Retry loadUi in isolation after aggregateContent if network connection occurs when retrieving remote UI bundle (#1095)
  • cli: Remove empty default on description for --extension option

=== Changed

  • ui-loader: Change gulp-vinyl-zip dependency to @​vscode/gulp-vinyl-zip (no functional changes)
  • asciidoc-loader: Fix relative path computation when path is extensionless and to matches parent folder of from (#1094)
  • page-composer: Fix result of relativize helper when path is extensionless and to matches parent folder of from (#1094)
  • file-publisher: Change gulp-vinyl-zip dependency to @​vscode/gulp-vinyl-zip (no functional changes)

== 3.1.4 (2023-06-08)

=== Fixed

  • content-aggregator: Try credentials from credentials store if auth fails with credentials in URL (#1063)
  • content-aggregator: Consider local branches in non-managed bare repository that has at least one remote branch (#1064)
Commits
  • 14a6725 release 3.1.6
  • fd16b9c update CHANGELOG and what's new page for upcoming 3.1.6 release
  • e4abd1d backport fix for #1070 add hostname to ignore list on pino-pretty settings to...
  • 13c0796 a provision of #1096 select all start paths before collecting files so start ...
  • f8a1191 a provision of #1096 retry failed fetch/clone operations in serial if git.fet...
  • ceb40b0 a provision of #1096 add git.read_concurrency playbook key to control how man...
  • 09219b3 resolves #1096 decouple step to load repositories when collecting files
  • d1ee749 a provision of #1096 flag unknown git errors a recoverable to retry without c...
  • 1ad2196 replace trimRight with trimEnd
  • 2da2f16 extract identifyAuthStatus function
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@antora/site-generator-default&package-manager=npm_and_yarn&previous-version=3.1.3&new-version=3.1.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 937 +++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 420 insertions(+), 519 deletions(-) diff --git a/package-lock.json b/package-lock.json index 36411e0d0..d5c06b878 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "ISC", "dependencies": { "@antora/cli": "^3.1.6", - "@antora/site-generator-default": "^3.1.3", + "@antora/site-generator-default": "^3.1.6", "@neo4j-antora/antora-add-notes": "^0.1.6", "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", "@neo4j-antora/antora-page-roles": "^0.3.1", @@ -26,11 +26,11 @@ } }, "node_modules/@antora/asciidoc-loader": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.3.tgz", - "integrity": "sha512-nCmfdxPSBW3rwZ6Aa+xZH9XRDSHOkx28B5VJ2gHlZr8Nl1cx3nvwcP7l/FqYCW/q4mke/ozj/PpjsyC1K5BZVQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.6.tgz", + "integrity": "sha512-Tqy4QFuZiKe/yX+3H8+vTLE6HH+VDm9OkKwq3G769jcC+40wz6y3poV4r4t1XJFAWwa/AKGM99ZcnJcJ3rtW+A==", "dependencies": { - "@antora/logger": "3.1.3", + "@antora/logger": "3.1.6", "@antora/user-require-helper": "~2.0", "@asciidoctor/core": "~2.2" }, @@ -55,114 +55,18 @@ "node": ">=16.0.0" } }, - "node_modules/@antora/cli/node_modules/@antora/logger": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.6.tgz", - "integrity": "sha512-36kU8gMbPslcPu8u9EbXsz6+9G9+LWPKhO7n8mEQqxlcCqmChwgetK6VbsL102rynpgPsstX6IAKg2wbptJK5g==", - "dependencies": { - "@antora/expand-path-helper": "~2.0", - "pino": "~8.14", - "pino-pretty": "~10.0", - "sonic-boom": "~3.3" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@antora/cli/node_modules/@antora/playbook-builder": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.6.tgz", - "integrity": "sha512-bZcDastZViAgPVPNvvbbw7ci63gL5YnyG5X7NuHJoORgzyGQAsMYEjzfa9yfNfXubUmXv/oSteUSxbACjdjzWg==", - "dependencies": { - "@iarna/toml": "~2.2", - "convict": "~6.2", - "js-yaml": "~4.1", - "json5": "~2.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@antora/cli/node_modules/pino": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.14.2.tgz", - "integrity": "sha512-zKu9aWeSWTy1JgvxIpZveJKKsAr4+6uNMZ0Vf0KRwzl/UNZA3XjHiIl/0WwqLMkDwuHuDkT5xAgPA2jpKq4whA==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.0.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.1.0", - "thread-stream": "^2.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/@antora/cli/node_modules/pino-pretty": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.0.1.tgz", - "integrity": "sha512-yrn00+jNpkvZX/NrPVCPIVHAfTDy3ahF0PND9tKqZk4j9s+loK8dpzrJj4dGb7i+WLuR50ussuTAiWoMWU+qeA==", - "dependencies": { - "colorette": "^2.0.7", - "dateformat": "^4.6.3", - "fast-copy": "^3.0.0", - "fast-safe-stringify": "^2.1.1", - "help-me": "^4.0.1", - "joycon": "^3.1.1", - "minimist": "^1.2.6", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.0.0", - "pump": "^3.0.0", - "readable-stream": "^4.0.0", - "secure-json-parse": "^2.4.0", - "sonic-boom": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "pino-pretty": "bin.js" - } - }, - "node_modules/@antora/cli/node_modules/readable-stream": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", - "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@antora/cli/node_modules/sonic-boom": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", - "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, "node_modules/@antora/content-aggregator": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.3.tgz", - "integrity": "sha512-c+Z+7RVmgu2rvVDRzG8yqSJEmJHnpmTC24aVgvFbb/oU7+YRAVifacgzRl2khOmuky7qKiQxLYmyXJBtRgb37w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.6.tgz", + "integrity": "sha512-kOKWn/1gBvd9XOp00/wFzH4lb3yCa5u65ZKmfe9VwC7uOl5Tg9zT0lxMa7miEbPAmfhcOr0zRYXa2ybsoKBWNw==", "dependencies": { "@antora/expand-path-helper": "~2.0", - "@antora/logger": "3.1.3", + "@antora/logger": "3.1.6", "@antora/user-require-helper": "~2.0", "braces": "~3.0", "cache-directory": "~2.0", "glob-stream": "~7.0", - "hpagent": "~1.1", + "hpagent": "~1.2", "isomorphic-git": "~1.21", "js-yaml": "~4.1", "multi-progress": "~4.0", @@ -177,12 +81,12 @@ } }, "node_modules/@antora/content-classifier": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.3.tgz", - "integrity": "sha512-auPS1WVRa1khhY7EpEkP+strR+6A6UOm23skEUKzQKESKl6j9KRqxxurvMMKDUmtrAeZ1GqjX5dC/bPfUPNpmw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.6.tgz", + "integrity": "sha512-e5Fs38Cfbl2kecxpRLFftflphbjg2wPfWlwjLZjs8d0R5ISSCM38q8ecDKCQHQlrYJkSrxiSpWqg0irNqAHnLw==", "dependencies": { - "@antora/asciidoc-loader": "3.1.3", - "@antora/logger": "3.1.3", + "@antora/asciidoc-loader": "3.1.6", + "@antora/logger": "3.1.6", "mime-types": "~2.1", "vinyl": "~2.2" }, @@ -191,11 +95,11 @@ } }, "node_modules/@antora/document-converter": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.3.tgz", - "integrity": "sha512-9h8jCsf3C1YNMpmKX141kY4WD/W8Roo8T4Y6OToIIKRjNSPnT8KWLFB8NzHXyLNhV1H9EeS/eNdbvxDzBd/VvQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.6.tgz", + "integrity": "sha512-bdzlkwq1WMnfdc6FUZNcO58LwjMqYmv3m9dI/eAJryGiKa9ChBFskwA1ob7rB87Qxjzu6UHcNucbt910hjEOAw==", "dependencies": { - "@antora/asciidoc-loader": "3.1.3" + "@antora/asciidoc-loader": "3.1.6" }, "engines": { "node": ">=16.0.0" @@ -210,13 +114,13 @@ } }, "node_modules/@antora/file-publisher": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.3.tgz", - "integrity": "sha512-4c2RVaJkLvYXbUZP2VF/hFVSZ6UAPNPgt/iurexr3D7OqKrYJnYqchXsLhut2yCLYVOzedXGMst0y2XaEi52qA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.6.tgz", + "integrity": "sha512-UPTyFWY7lrG/Qj6SBxgoVBg1fW3JemJzW62y6pKuGHF59TEKJiaVTUlHEaVgkbpkCngn2os+VOX7fHK0jsBU9A==", "dependencies": { "@antora/expand-path-helper": "~2.0", "@antora/user-require-helper": "~2.0", - "gulp-vinyl-zip": "~2.5", + "@vscode/gulp-vinyl-zip": "~2.5", "vinyl": "~2.2", "vinyl-fs": "~3.0" }, @@ -225,36 +129,36 @@ } }, "node_modules/@antora/logger": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.3.tgz", - "integrity": "sha512-6h0PTYHF9C/I9CiYJmlTME7H9dEkMI1luGMQdYhPL9OicbZ5/HsUPKOvGfLNHzw2iyG0yp5bLVvcr9K69ArgtQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.6.tgz", + "integrity": "sha512-36kU8gMbPslcPu8u9EbXsz6+9G9+LWPKhO7n8mEQqxlcCqmChwgetK6VbsL102rynpgPsstX6IAKg2wbptJK5g==", "dependencies": { "@antora/expand-path-helper": "~2.0", - "pino": "~8.7", - "pino-pretty": "~9.1", - "sonic-boom": "~3.2" + "pino": "~8.14", + "pino-pretty": "~10.0", + "sonic-boom": "~3.3" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/navigation-builder": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.3.tgz", - "integrity": "sha512-t7xHXdK6YjmB79FlsKTGPWHHcEhRzLGZEp9Qjg45S1UphPWWCWEhz6nC/if+MKyoRPGn3aoiSh3n33XC/eYxew==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.6.tgz", + "integrity": "sha512-0iqktzBKQ4kgOM+pbm1bYdGUlN6Blw5zAxURr+W7X96b45mUHCTNz1nZrsDvBLbVXpSTk//ih85Ioh0RU4RJTw==", "dependencies": { - "@antora/asciidoc-loader": "3.1.3" + "@antora/asciidoc-loader": "3.1.6" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/page-composer": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.3.tgz", - "integrity": "sha512-X0qrJ1x6jReNjzPtenbgnxfVMbsc5RbuVzBEOqWnX9i+PI4dSYUJ9dKZW9rXE6Vv7Yq6vjOn7JlgHXcvo/RCzQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.6.tgz", + "integrity": "sha512-zl2UXVmHEy23zWsGzz3ZpnqtzTVfYvAVS7osPZpSyto3unwtQUI0dR+JpWndElsEDt71JJedbsXa/y/tNC2ZOQ==", "dependencies": { - "@antora/logger": "3.1.3", + "@antora/logger": "3.1.6", "handlebars": "~4.7", "require-from-string": "~2.0" }, @@ -263,9 +167,9 @@ } }, "node_modules/@antora/playbook-builder": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.3.tgz", - "integrity": "sha512-EjkTrXwFAhFxDFU6eHRuExqvuvoHxyR1IbCAgjyj6Pi6hvGIZoGbH9cu1Gy1sWE6XmZl5yA5Gwi++LMyQPmuuA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.6.tgz", + "integrity": "sha512-bZcDastZViAgPVPNvvbbw7ci63gL5YnyG5X7NuHJoORgzyGQAsMYEjzfa9yfNfXubUmXv/oSteUSxbACjdjzWg==", "dependencies": { "@iarna/toml": "~2.2", "convict": "~6.2", @@ -277,9 +181,9 @@ } }, "node_modules/@antora/redirect-producer": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.3.tgz", - "integrity": "sha512-SLa8HkDSeH45KUjpID7exv8yvDYcsgup4FKP9xtGJsEH4Ikv4uRmSqrL55jwliCwvuU6Wojt+PFHKtnZVwKKIg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.6.tgz", + "integrity": "sha512-tzlrJa2vny0HPBtIAgEM/xCMTfOi4z2CYUt4Ctz7rV8PBv6NOjlLkbu7Goc57CpR9wmJ3C4AGJcVHN0tah0FmA==", "dependencies": { "vinyl": "~2.2" }, @@ -288,23 +192,23 @@ } }, "node_modules/@antora/site-generator": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.3.tgz", - "integrity": "sha512-sT8EWKZd8u6KRgUpTd/8j33+G5mON4VX7C7e2MEI8gVBCaPutUPuYO/2IycmN3n294Nua+szI/1Aqt6uVfksHg==", - "dependencies": { - "@antora/asciidoc-loader": "3.1.3", - "@antora/content-aggregator": "3.1.3", - "@antora/content-classifier": "3.1.3", - "@antora/document-converter": "3.1.3", - "@antora/file-publisher": "3.1.3", - "@antora/logger": "3.1.3", - "@antora/navigation-builder": "3.1.3", - "@antora/page-composer": "3.1.3", - "@antora/playbook-builder": "3.1.3", - "@antora/redirect-producer": "3.1.3", - "@antora/site-mapper": "3.1.3", - "@antora/site-publisher": "3.1.3", - "@antora/ui-loader": "3.1.3", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.6.tgz", + "integrity": "sha512-NKRTVDGB7CuzEBx68iQGweSTDXLvqccExEfBiudPEhyagcn4U+fwef+0LjE4A2imcpD/QQC7l5U8VaNObQYnRQ==", + "dependencies": { + "@antora/asciidoc-loader": "3.1.6", + "@antora/content-aggregator": "3.1.6", + "@antora/content-classifier": "3.1.6", + "@antora/document-converter": "3.1.6", + "@antora/file-publisher": "3.1.6", + "@antora/logger": "3.1.6", + "@antora/navigation-builder": "3.1.6", + "@antora/page-composer": "3.1.6", + "@antora/playbook-builder": "3.1.6", + "@antora/redirect-producer": "3.1.6", + "@antora/site-mapper": "3.1.6", + "@antora/site-publisher": "3.1.6", + "@antora/ui-loader": "3.1.6", "@antora/user-require-helper": "~2.0" }, "engines": { @@ -312,22 +216,22 @@ } }, "node_modules/@antora/site-generator-default": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.3.tgz", - "integrity": "sha512-xSseFMr1P+8ltaAzeK47/vjMQWHJb4Ya3sbfdjQCG/GR63jk0q+/sVhBE2euO0cwmJAC+mCog+wVUPaPmXPTEw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.6.tgz", + "integrity": "sha512-HXnqYYQdQHwjg6NAvEr3oolt4Kmb3cEVqMsR9BoN8ZPfVpnx+kM9r1/qqpgx9BZxY2oTDYPhFFeygdq2Fu5rIg==", "dependencies": { - "@antora/site-generator": "3.1.3" + "@antora/site-generator": "3.1.6" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/site-mapper": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.3.tgz", - "integrity": "sha512-egyuCTgGVCcvNfH0BaWSsG5zJon3smXDjlRbIODqI6zu99HXsNqrVLMP62Z3lzEibKYXc/7XY/q5Ggm+prh0Sw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.6.tgz", + "integrity": "sha512-Jc5AqY2uS3wO21iwEFyJuXOspTLN6UdNnZP/Os2oguR+cSsjwUx+l6+X7lquIndq+dXUQS3tMQkwNkhLgfcsrw==", "dependencies": { - "@antora/content-classifier": "3.1.3", + "@antora/content-classifier": "3.1.6", "vinyl": "~2.2" }, "engines": { @@ -335,27 +239,27 @@ } }, "node_modules/@antora/site-publisher": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.3.tgz", - "integrity": "sha512-dSHyyKj5l3ZIRo0EjNxmpZ+G3OgPQ4fgwdmA8xOWtX+eiU4sV3MQ9j6tlc4lloAzUVIumt7LOd0n9Quie6yCNA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.6.tgz", + "integrity": "sha512-AOpM12gmMJeucebEGneHvOJAXQgco0lAg7Vx9CH7slHVeJy6mM74Mcut7KkKlv3AOJJKgYfdYkJndvq9dqbWmQ==", "dependencies": { - "@antora/file-publisher": "3.1.3" + "@antora/file-publisher": "3.1.6" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/ui-loader": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.3.tgz", - "integrity": "sha512-xaOUn5vOb9RR6LVsCCwROvvWXTADvC/ITO1yKAiJtV9xNrI08szzmWXjgvb07PpRDRPD5QgYoZFmqFPk+0WE2w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.6.tgz", + "integrity": "sha512-IivfKW7fCaV7GpXIOxyk8X2mJiYoM6U0CDaFzWiKerJeDikW1Oi9KGxCMe2+onYBJrgzQxAZsIzjr9fXUcDZWw==", "dependencies": { "@antora/expand-path-helper": "~2.0", + "@vscode/gulp-vinyl-zip": "~2.5", "braces": "~3.0", "cache-directory": "~2.0", "glob-stream": "~7.0", - "gulp-vinyl-zip": "~2.5", - "hpagent": "~1.1", + "hpagent": "~1.2", "js-yaml": "~4.1", "picomatch": "~2.3", "should-proxy": "~1.0", @@ -431,6 +335,23 @@ "resolved": "https://registry.npmjs.org/@neo4j-documentation/remote-include/-/remote-include-1.0.0.tgz", "integrity": "sha512-SprNp9XsWiMBC0T44vs3JUwEYhoyJlg+du5kP0f9RGewXrSeEgsr5tY7nQDa4Bou9iG0sBl0+2u4XZjiVMkiuw==" }, + "node_modules/@vscode/gulp-vinyl-zip": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@vscode/gulp-vinyl-zip/-/gulp-vinyl-zip-2.5.0.tgz", + "integrity": "sha512-PP/xkOoLBSY3V04HmzRxF+NOxkRJ/m2D0YwWpfx1FCFv5G8+sZUGPvxX+LRgdJ5vQcR1RHck5x1IkHi75Qjdbw==", + "dependencies": { + "queue": "^4.2.1", + "through": "^2.3.8", + "through2": "^2.0.3", + "vinyl": "^2.0.2", + "vinyl-fs": "^3.0.3", + "yauzl": "^2.2.1", + "yazl": "^2.2.1" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -687,12 +608,13 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -918,11 +840,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -1075,9 +1011,9 @@ "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" }, "node_modules/fast-redact": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.2.0.tgz", - "integrity": "sha512-zaTadChr+NekyzallAMXATXLOR8MNx3zqpZ0MUF2aGf4EathnG0f32VLODNlY8IuGY3HoRO2L6/6fSzNsLaHIw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", + "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==", "engines": { "node": ">=6" } @@ -1210,19 +1146,22 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1294,36 +1233,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/gulp-vinyl-zip": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/gulp-vinyl-zip/-/gulp-vinyl-zip-2.5.0.tgz", - "integrity": "sha512-KPi5/2SUmkXXDvKU4L2U1dkPOP03SbhONTOgNZlL23l9Yopt+euJ1bBXwWrSMbsyh3JLW/TYuC8CI4c4Kq4qrw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "queue": "^4.2.1", - "through": "^2.3.8", - "through2": "^2.0.3", - "vinyl": "^2.0.2", - "vinyl-fs": "^3.0.3", - "yauzl": "^2.2.1", - "yazl": "^2.2.1" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dependencies": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, @@ -1337,17 +1269,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1358,11 +1279,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1390,6 +1311,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/help-me": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", @@ -1437,9 +1369,9 @@ } }, "node_modules/hpagent": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.1.0.tgz", - "integrity": "sha512-bgJcBmNTZaJO03xtXOTNfoFEf/3VwoZ/gJ2O4ekTCZu4LSFtfzQFrJ0kjq8ZSS0+IdghXqQIiDUnpp0eUR9IJg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", + "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", "engines": { "node": ">=14" } @@ -1492,9 +1424,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "engines": { "node": ">= 4" } @@ -2009,12 +1941,12 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -2026,9 +1958,12 @@ } }, "node_modules/on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } }, "node_modules/on-finished": { "version": "2.4.1", @@ -2138,9 +2073,9 @@ } }, "node_modules/pino": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.7.0.tgz", - "integrity": "sha512-l9sA5uPxmZzwydhMWUcm1gI0YxNnYl8MfSr2h8cwLvOAzQLBLewzF247h/vqHe3/tt6fgtXeG9wdjjoetdI/vA==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.14.2.tgz", + "integrity": "sha512-zKu9aWeSWTy1JgvxIpZveJKKsAr4+6uNMZ0Vf0KRwzl/UNZA3XjHiIl/0WwqLMkDwuHuDkT5xAgPA2jpKq4whA==", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", @@ -2168,23 +2103,24 @@ } }, "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz", - "integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", + "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", - "process": "^0.11.10" + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/pino-pretty": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-9.1.1.tgz", - "integrity": "sha512-iJrnjgR4FWQIXZkUF48oNgoRI9BpyMhaEmihonHeCnZ6F50ZHAS4YGfGBT/ZVNsPmd+hzkIPGzjKdY08+/yAXw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.0.1.tgz", + "integrity": "sha512-yrn00+jNpkvZX/NrPVCPIVHAfTDy3ahF0PND9tKqZk4j9s+loK8dpzrJj4dGb7i+WLuR50ussuTAiWoMWU+qeA==", "dependencies": { "colorette": "^2.0.7", "dateformat": "^4.6.3", @@ -2206,23 +2142,24 @@ } }, "node_modules/pino-pretty/node_modules/readable-stream": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz", - "integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", + "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", - "process": "^0.11.10" + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/pino-std-serializers": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.1.tgz", - "integrity": "sha512-wHuWB+CvSVb2XqXM0W/WOYUkVSPbiJb9S5fNB7TBhd8s892Xq910bRxwHtC4l71hgztObTjXL6ZheZXFjhDrDQ==" + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" }, "node_modules/process": { "version": "0.11.10", @@ -2238,9 +2175,9 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/process-warning": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz", - "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", + "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==" }, "node_modules/progress": { "version": "2.0.3", @@ -2533,6 +2470,20 @@ "node": ">= 0.8.0" } }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -2626,9 +2577,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.1.tgz", - "integrity": "sha512-iITeTHxy3B9FGu8aVdiDXUVAcHMF9Ss0cCsAOo2HfCrmVGT3/DT5oYaeu0M/YKZDlKTvChEyPq0zI9Hf33EX6A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", + "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", "dependencies": { "atomic-sleep": "^1.0.0" } @@ -2695,9 +2646,9 @@ } }, "node_modules/thread-stream": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.3.0.tgz", - "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", + "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", "dependencies": { "real-require": "^0.2.0" } @@ -3111,11 +3062,11 @@ }, "dependencies": { "@antora/asciidoc-loader": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.3.tgz", - "integrity": "sha512-nCmfdxPSBW3rwZ6Aa+xZH9XRDSHOkx28B5VJ2gHlZr8Nl1cx3nvwcP7l/FqYCW/q4mke/ozj/PpjsyC1K5BZVQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.6.tgz", + "integrity": "sha512-Tqy4QFuZiKe/yX+3H8+vTLE6HH+VDm9OkKwq3G769jcC+40wz6y3poV4r4t1XJFAWwa/AKGM99ZcnJcJ3rtW+A==", "requires": { - "@antora/logger": "3.1.3", + "@antora/logger": "3.1.6", "@antora/user-require-helper": "~2.0", "@asciidoctor/core": "~2.2" } @@ -3129,103 +3080,20 @@ "@antora/playbook-builder": "3.1.6", "@antora/user-require-helper": "~2.0", "commander": "~10.0" - }, - "dependencies": { - "@antora/logger": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.6.tgz", - "integrity": "sha512-36kU8gMbPslcPu8u9EbXsz6+9G9+LWPKhO7n8mEQqxlcCqmChwgetK6VbsL102rynpgPsstX6IAKg2wbptJK5g==", - "requires": { - "@antora/expand-path-helper": "~2.0", - "pino": "~8.14", - "pino-pretty": "~10.0", - "sonic-boom": "~3.3" - } - }, - "@antora/playbook-builder": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.6.tgz", - "integrity": "sha512-bZcDastZViAgPVPNvvbbw7ci63gL5YnyG5X7NuHJoORgzyGQAsMYEjzfa9yfNfXubUmXv/oSteUSxbACjdjzWg==", - "requires": { - "@iarna/toml": "~2.2", - "convict": "~6.2", - "js-yaml": "~4.1", - "json5": "~2.2" - } - }, - "pino": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.14.2.tgz", - "integrity": "sha512-zKu9aWeSWTy1JgvxIpZveJKKsAr4+6uNMZ0Vf0KRwzl/UNZA3XjHiIl/0WwqLMkDwuHuDkT5xAgPA2jpKq4whA==", - "requires": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.0.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.1.0", - "thread-stream": "^2.0.0" - } - }, - "pino-pretty": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.0.1.tgz", - "integrity": "sha512-yrn00+jNpkvZX/NrPVCPIVHAfTDy3ahF0PND9tKqZk4j9s+loK8dpzrJj4dGb7i+WLuR50ussuTAiWoMWU+qeA==", - "requires": { - "colorette": "^2.0.7", - "dateformat": "^4.6.3", - "fast-copy": "^3.0.0", - "fast-safe-stringify": "^2.1.1", - "help-me": "^4.0.1", - "joycon": "^3.1.1", - "minimist": "^1.2.6", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.0.0", - "pump": "^3.0.0", - "readable-stream": "^4.0.0", - "secure-json-parse": "^2.4.0", - "sonic-boom": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "readable-stream": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", - "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", - "requires": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - } - }, - "sonic-boom": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", - "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", - "requires": { - "atomic-sleep": "^1.0.0" - } - } } }, "@antora/content-aggregator": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.3.tgz", - "integrity": "sha512-c+Z+7RVmgu2rvVDRzG8yqSJEmJHnpmTC24aVgvFbb/oU7+YRAVifacgzRl2khOmuky7qKiQxLYmyXJBtRgb37w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.6.tgz", + "integrity": "sha512-kOKWn/1gBvd9XOp00/wFzH4lb3yCa5u65ZKmfe9VwC7uOl5Tg9zT0lxMa7miEbPAmfhcOr0zRYXa2ybsoKBWNw==", "requires": { "@antora/expand-path-helper": "~2.0", - "@antora/logger": "3.1.3", + "@antora/logger": "3.1.6", "@antora/user-require-helper": "~2.0", "braces": "~3.0", "cache-directory": "~2.0", "glob-stream": "~7.0", - "hpagent": "~1.1", + "hpagent": "~1.2", "isomorphic-git": "~1.21", "js-yaml": "~4.1", "multi-progress": "~4.0", @@ -3237,22 +3105,22 @@ } }, "@antora/content-classifier": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.3.tgz", - "integrity": "sha512-auPS1WVRa1khhY7EpEkP+strR+6A6UOm23skEUKzQKESKl6j9KRqxxurvMMKDUmtrAeZ1GqjX5dC/bPfUPNpmw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.6.tgz", + "integrity": "sha512-e5Fs38Cfbl2kecxpRLFftflphbjg2wPfWlwjLZjs8d0R5ISSCM38q8ecDKCQHQlrYJkSrxiSpWqg0irNqAHnLw==", "requires": { - "@antora/asciidoc-loader": "3.1.3", - "@antora/logger": "3.1.3", + "@antora/asciidoc-loader": "3.1.6", + "@antora/logger": "3.1.6", "mime-types": "~2.1", "vinyl": "~2.2" } }, "@antora/document-converter": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.3.tgz", - "integrity": "sha512-9h8jCsf3C1YNMpmKX141kY4WD/W8Roo8T4Y6OToIIKRjNSPnT8KWLFB8NzHXyLNhV1H9EeS/eNdbvxDzBd/VvQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.6.tgz", + "integrity": "sha512-bdzlkwq1WMnfdc6FUZNcO58LwjMqYmv3m9dI/eAJryGiKa9ChBFskwA1ob7rB87Qxjzu6UHcNucbt910hjEOAw==", "requires": { - "@antora/asciidoc-loader": "3.1.3" + "@antora/asciidoc-loader": "3.1.6" } }, "@antora/expand-path-helper": { @@ -3261,50 +3129,50 @@ "integrity": "sha512-CSMBGC+tI21VS2kGW3PV7T2kQTM5eT3f2GTPVLttwaNYbNxDve08en/huzszHJfxo11CcEs26Ostr0F2c1QqeA==" }, "@antora/file-publisher": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.3.tgz", - "integrity": "sha512-4c2RVaJkLvYXbUZP2VF/hFVSZ6UAPNPgt/iurexr3D7OqKrYJnYqchXsLhut2yCLYVOzedXGMst0y2XaEi52qA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.6.tgz", + "integrity": "sha512-UPTyFWY7lrG/Qj6SBxgoVBg1fW3JemJzW62y6pKuGHF59TEKJiaVTUlHEaVgkbpkCngn2os+VOX7fHK0jsBU9A==", "requires": { "@antora/expand-path-helper": "~2.0", "@antora/user-require-helper": "~2.0", - "gulp-vinyl-zip": "~2.5", + "@vscode/gulp-vinyl-zip": "~2.5", "vinyl": "~2.2", "vinyl-fs": "~3.0" } }, "@antora/logger": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.3.tgz", - "integrity": "sha512-6h0PTYHF9C/I9CiYJmlTME7H9dEkMI1luGMQdYhPL9OicbZ5/HsUPKOvGfLNHzw2iyG0yp5bLVvcr9K69ArgtQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.6.tgz", + "integrity": "sha512-36kU8gMbPslcPu8u9EbXsz6+9G9+LWPKhO7n8mEQqxlcCqmChwgetK6VbsL102rynpgPsstX6IAKg2wbptJK5g==", "requires": { "@antora/expand-path-helper": "~2.0", - "pino": "~8.7", - "pino-pretty": "~9.1", - "sonic-boom": "~3.2" + "pino": "~8.14", + "pino-pretty": "~10.0", + "sonic-boom": "~3.3" } }, "@antora/navigation-builder": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.3.tgz", - "integrity": "sha512-t7xHXdK6YjmB79FlsKTGPWHHcEhRzLGZEp9Qjg45S1UphPWWCWEhz6nC/if+MKyoRPGn3aoiSh3n33XC/eYxew==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.6.tgz", + "integrity": "sha512-0iqktzBKQ4kgOM+pbm1bYdGUlN6Blw5zAxURr+W7X96b45mUHCTNz1nZrsDvBLbVXpSTk//ih85Ioh0RU4RJTw==", "requires": { - "@antora/asciidoc-loader": "3.1.3" + "@antora/asciidoc-loader": "3.1.6" } }, "@antora/page-composer": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.3.tgz", - "integrity": "sha512-X0qrJ1x6jReNjzPtenbgnxfVMbsc5RbuVzBEOqWnX9i+PI4dSYUJ9dKZW9rXE6Vv7Yq6vjOn7JlgHXcvo/RCzQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.6.tgz", + "integrity": "sha512-zl2UXVmHEy23zWsGzz3ZpnqtzTVfYvAVS7osPZpSyto3unwtQUI0dR+JpWndElsEDt71JJedbsXa/y/tNC2ZOQ==", "requires": { - "@antora/logger": "3.1.3", + "@antora/logger": "3.1.6", "handlebars": "~4.7", "require-from-string": "~2.0" } }, "@antora/playbook-builder": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.3.tgz", - "integrity": "sha512-EjkTrXwFAhFxDFU6eHRuExqvuvoHxyR1IbCAgjyj6Pi6hvGIZoGbH9cu1Gy1sWE6XmZl5yA5Gwi++LMyQPmuuA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.6.tgz", + "integrity": "sha512-bZcDastZViAgPVPNvvbbw7ci63gL5YnyG5X7NuHJoORgzyGQAsMYEjzfa9yfNfXubUmXv/oSteUSxbACjdjzWg==", "requires": { "@iarna/toml": "~2.2", "convict": "~6.2", @@ -3313,70 +3181,70 @@ } }, "@antora/redirect-producer": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.3.tgz", - "integrity": "sha512-SLa8HkDSeH45KUjpID7exv8yvDYcsgup4FKP9xtGJsEH4Ikv4uRmSqrL55jwliCwvuU6Wojt+PFHKtnZVwKKIg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.6.tgz", + "integrity": "sha512-tzlrJa2vny0HPBtIAgEM/xCMTfOi4z2CYUt4Ctz7rV8PBv6NOjlLkbu7Goc57CpR9wmJ3C4AGJcVHN0tah0FmA==", "requires": { "vinyl": "~2.2" } }, "@antora/site-generator": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.3.tgz", - "integrity": "sha512-sT8EWKZd8u6KRgUpTd/8j33+G5mON4VX7C7e2MEI8gVBCaPutUPuYO/2IycmN3n294Nua+szI/1Aqt6uVfksHg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.6.tgz", + "integrity": "sha512-NKRTVDGB7CuzEBx68iQGweSTDXLvqccExEfBiudPEhyagcn4U+fwef+0LjE4A2imcpD/QQC7l5U8VaNObQYnRQ==", "requires": { - "@antora/asciidoc-loader": "3.1.3", - "@antora/content-aggregator": "3.1.3", - "@antora/content-classifier": "3.1.3", - "@antora/document-converter": "3.1.3", - "@antora/file-publisher": "3.1.3", - "@antora/logger": "3.1.3", - "@antora/navigation-builder": "3.1.3", - "@antora/page-composer": "3.1.3", - "@antora/playbook-builder": "3.1.3", - "@antora/redirect-producer": "3.1.3", - "@antora/site-mapper": "3.1.3", - "@antora/site-publisher": "3.1.3", - "@antora/ui-loader": "3.1.3", + "@antora/asciidoc-loader": "3.1.6", + "@antora/content-aggregator": "3.1.6", + "@antora/content-classifier": "3.1.6", + "@antora/document-converter": "3.1.6", + "@antora/file-publisher": "3.1.6", + "@antora/logger": "3.1.6", + "@antora/navigation-builder": "3.1.6", + "@antora/page-composer": "3.1.6", + "@antora/playbook-builder": "3.1.6", + "@antora/redirect-producer": "3.1.6", + "@antora/site-mapper": "3.1.6", + "@antora/site-publisher": "3.1.6", + "@antora/ui-loader": "3.1.6", "@antora/user-require-helper": "~2.0" } }, "@antora/site-generator-default": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.3.tgz", - "integrity": "sha512-xSseFMr1P+8ltaAzeK47/vjMQWHJb4Ya3sbfdjQCG/GR63jk0q+/sVhBE2euO0cwmJAC+mCog+wVUPaPmXPTEw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.6.tgz", + "integrity": "sha512-HXnqYYQdQHwjg6NAvEr3oolt4Kmb3cEVqMsR9BoN8ZPfVpnx+kM9r1/qqpgx9BZxY2oTDYPhFFeygdq2Fu5rIg==", "requires": { - "@antora/site-generator": "3.1.3" + "@antora/site-generator": "3.1.6" } }, "@antora/site-mapper": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.3.tgz", - "integrity": "sha512-egyuCTgGVCcvNfH0BaWSsG5zJon3smXDjlRbIODqI6zu99HXsNqrVLMP62Z3lzEibKYXc/7XY/q5Ggm+prh0Sw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.6.tgz", + "integrity": "sha512-Jc5AqY2uS3wO21iwEFyJuXOspTLN6UdNnZP/Os2oguR+cSsjwUx+l6+X7lquIndq+dXUQS3tMQkwNkhLgfcsrw==", "requires": { - "@antora/content-classifier": "3.1.3", + "@antora/content-classifier": "3.1.6", "vinyl": "~2.2" } }, "@antora/site-publisher": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.3.tgz", - "integrity": "sha512-dSHyyKj5l3ZIRo0EjNxmpZ+G3OgPQ4fgwdmA8xOWtX+eiU4sV3MQ9j6tlc4lloAzUVIumt7LOd0n9Quie6yCNA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.6.tgz", + "integrity": "sha512-AOpM12gmMJeucebEGneHvOJAXQgco0lAg7Vx9CH7slHVeJy6mM74Mcut7KkKlv3AOJJKgYfdYkJndvq9dqbWmQ==", "requires": { - "@antora/file-publisher": "3.1.3" + "@antora/file-publisher": "3.1.6" } }, "@antora/ui-loader": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.3.tgz", - "integrity": "sha512-xaOUn5vOb9RR6LVsCCwROvvWXTADvC/ITO1yKAiJtV9xNrI08szzmWXjgvb07PpRDRPD5QgYoZFmqFPk+0WE2w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.6.tgz", + "integrity": "sha512-IivfKW7fCaV7GpXIOxyk8X2mJiYoM6U0CDaFzWiKerJeDikW1Oi9KGxCMe2+onYBJrgzQxAZsIzjr9fXUcDZWw==", "requires": { "@antora/expand-path-helper": "~2.0", + "@vscode/gulp-vinyl-zip": "~2.5", "braces": "~3.0", "cache-directory": "~2.0", "glob-stream": "~7.0", - "gulp-vinyl-zip": "~2.5", - "hpagent": "~1.1", + "hpagent": "~1.2", "js-yaml": "~4.1", "picomatch": "~2.3", "should-proxy": "~1.0", @@ -3441,6 +3309,20 @@ "resolved": "https://registry.npmjs.org/@neo4j-documentation/remote-include/-/remote-include-1.0.0.tgz", "integrity": "sha512-SprNp9XsWiMBC0T44vs3JUwEYhoyJlg+du5kP0f9RGewXrSeEgsr5tY7nQDa4Bou9iG0sBl0+2u4XZjiVMkiuw==" }, + "@vscode/gulp-vinyl-zip": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@vscode/gulp-vinyl-zip/-/gulp-vinyl-zip-2.5.0.tgz", + "integrity": "sha512-PP/xkOoLBSY3V04HmzRxF+NOxkRJ/m2D0YwWpfx1FCFv5G8+sZUGPvxX+LRgdJ5vQcR1RHck5x1IkHi75Qjdbw==", + "requires": { + "queue": "^4.2.1", + "through": "^2.3.8", + "through2": "^2.0.3", + "vinyl": "^2.0.2", + "vinyl-fs": "^3.0.3", + "yauzl": "^2.2.1", + "yazl": "^2.2.1" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -3619,12 +3501,13 @@ } }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" } }, "chokidar": { @@ -3801,11 +3684,22 @@ "mimic-response": "^3.1.0" } }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "requires": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } @@ -3930,9 +3824,9 @@ "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" }, "fast-redact": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.2.0.tgz", - "integrity": "sha512-zaTadChr+NekyzallAMXATXLOR8MNx3zqpZ0MUF2aGf4EathnG0f32VLODNlY8IuGY3HoRO2L6/6fSzNsLaHIw==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", + "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==" }, "fast-safe-stringify": { "version": "2.1.1", @@ -4042,19 +3936,19 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "glob": { @@ -4110,45 +4004,31 @@ } } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "gulp-vinyl-zip": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/gulp-vinyl-zip/-/gulp-vinyl-zip-2.5.0.tgz", - "integrity": "sha512-KPi5/2SUmkXXDvKU4L2U1dkPOP03SbhONTOgNZlL23l9Yopt+euJ1bBXwWrSMbsyh3JLW/TYuC8CI4c4Kq4qrw==", - "requires": { - "queue": "^4.2.1", - "through": "^2.3.8", - "through2": "^2.0.3", - "vinyl": "^2.0.2", - "vinyl-fs": "^3.0.3", - "yauzl": "^2.2.1", - "yazl": "^2.2.1" - } - }, "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "requires": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -4156,11 +4036,11 @@ "dev": true }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "requires": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" } }, "has-proto": { @@ -4173,6 +4053,14 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "requires": { + "function-bind": "^1.1.2" + } + }, "help-me": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", @@ -4213,9 +4101,9 @@ } }, "hpagent": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.1.0.tgz", - "integrity": "sha512-bgJcBmNTZaJO03xtXOTNfoFEf/3VwoZ/gJ2O4ekTCZu4LSFtfzQFrJ0kjq8ZSS0+IdghXqQIiDUnpp0eUR9IJg==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", + "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==" }, "http-errors": { "version": "2.0.0", @@ -4245,9 +4133,9 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==" }, "ignore-by-default": { "version": "1.0.1", @@ -4625,20 +4513,20 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, "on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==" }, "on-finished": { "version": "2.4.1", @@ -4732,9 +4620,9 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" }, "pino": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.7.0.tgz", - "integrity": "sha512-l9sA5uPxmZzwydhMWUcm1gI0YxNnYl8MfSr2h8cwLvOAzQLBLewzF247h/vqHe3/tt6fgtXeG9wdjjoetdI/vA==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.14.2.tgz", + "integrity": "sha512-zKu9aWeSWTy1JgvxIpZveJKKsAr4+6uNMZ0Vf0KRwzl/UNZA3XjHiIl/0WwqLMkDwuHuDkT5xAgPA2jpKq4whA==", "requires": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", @@ -4759,22 +4647,23 @@ }, "dependencies": { "readable-stream": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz", - "integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", + "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", "requires": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", - "process": "^0.11.10" + "process": "^0.11.10", + "string_decoder": "^1.3.0" } } } }, "pino-pretty": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-9.1.1.tgz", - "integrity": "sha512-iJrnjgR4FWQIXZkUF48oNgoRI9BpyMhaEmihonHeCnZ6F50ZHAS4YGfGBT/ZVNsPmd+hzkIPGzjKdY08+/yAXw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.0.1.tgz", + "integrity": "sha512-yrn00+jNpkvZX/NrPVCPIVHAfTDy3ahF0PND9tKqZk4j9s+loK8dpzrJj4dGb7i+WLuR50ussuTAiWoMWU+qeA==", "requires": { "colorette": "^2.0.7", "dateformat": "^4.6.3", @@ -4793,22 +4682,23 @@ }, "dependencies": { "readable-stream": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz", - "integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.1.tgz", + "integrity": "sha512-uQjbf34vmf/asGnOHQEw07Q4llgMACQZTWWa4MmICS0IKJoHbLwKCy71H3eR99Dw5iYejc6W+pqZZEeqRtUFAw==", "requires": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", - "process": "^0.11.10" + "process": "^0.11.10", + "string_decoder": "^1.3.0" } } } }, "pino-std-serializers": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.1.tgz", - "integrity": "sha512-wHuWB+CvSVb2XqXM0W/WOYUkVSPbiJb9S5fNB7TBhd8s892Xq910bRxwHtC4l71hgztObTjXL6ZheZXFjhDrDQ==" + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" }, "process": { "version": "0.11.10", @@ -4821,9 +4711,9 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "process-warning": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz", - "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", + "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==" }, "progress": { "version": "2.0.3", @@ -5047,6 +4937,17 @@ "send": "0.18.0" } }, + "set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "requires": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -5103,9 +5004,9 @@ } }, "sonic-boom": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.1.tgz", - "integrity": "sha512-iITeTHxy3B9FGu8aVdiDXUVAcHMF9Ss0cCsAOo2HfCrmVGT3/DT5oYaeu0M/YKZDlKTvChEyPq0zI9Hf33EX6A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", + "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", "requires": { "atomic-sleep": "^1.0.0" } @@ -5154,9 +5055,9 @@ } }, "thread-stream": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.3.0.tgz", - "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", + "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", "requires": { "real-require": "^0.2.0" } diff --git a/package.json b/package.json index 833622a15..6af7b3590 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "license": "ISC", "dependencies": { "@antora/cli": "^3.1.6", - "@antora/site-generator-default": "^3.1.3", + "@antora/site-generator-default": "^3.1.6", "@neo4j-antora/antora-add-notes": "^0.1.6", "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", "@neo4j-antora/antora-page-roles": "^0.3.1", From b2fb7c0818acaf696e39bd4c3ffc512119c40ecb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 13:20:20 +0100 Subject: [PATCH 343/383] Bump @neo4j-antora/antora-add-notes from 0.1.6 to 0.3.1 (#817) Bumps @neo4j-antora/antora-add-notes from 0.1.6 to 0.3.1. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@neo4j-antora/antora-add-notes&package-manager=npm_and_yarn&previous-version=0.1.6&new-version=0.3.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d5c06b878..459a3ce06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@antora/cli": "^3.1.6", "@antora/site-generator-default": "^3.1.6", - "@neo4j-antora/antora-add-notes": "^0.1.6", + "@neo4j-antora/antora-add-notes": "^0.3.1", "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", "@neo4j-antora/antora-page-roles": "^0.3.1", "@neo4j-antora/antora-table-footnotes": "^0.3.2", @@ -301,9 +301,9 @@ "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, "node_modules/@neo4j-antora/antora-add-notes": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-add-notes/-/antora-add-notes-0.1.6.tgz", - "integrity": "sha512-B06tnV2TBAA5D6DkjByxnOvwcyZ/+3lRXf/0nIAxqnfo/XBjQVsw00X8xQ4gk9sbAnT1KndAdQtW03kG6fTgng==" + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-add-notes/-/antora-add-notes-0.3.1.tgz", + "integrity": "sha512-0zapKJr9U+cNBaz5m342Zqb5+1VuaFuybhGtSzEdiG4MRmvsAZunxLX7lSG0cWm6L4DlQa0jzOKYYzpkqoap/g==" }, "node_modules/@neo4j-antora/antora-modify-sitemaps": { "version": "0.4.4", @@ -3275,9 +3275,9 @@ "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, "@neo4j-antora/antora-add-notes": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-add-notes/-/antora-add-notes-0.1.6.tgz", - "integrity": "sha512-B06tnV2TBAA5D6DkjByxnOvwcyZ/+3lRXf/0nIAxqnfo/XBjQVsw00X8xQ4gk9sbAnT1KndAdQtW03kG6fTgng==" + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@neo4j-antora/antora-add-notes/-/antora-add-notes-0.3.1.tgz", + "integrity": "sha512-0zapKJr9U+cNBaz5m342Zqb5+1VuaFuybhGtSzEdiG4MRmvsAZunxLX7lSG0cWm6L4DlQa0jzOKYYzpkqoap/g==" }, "@neo4j-antora/antora-modify-sitemaps": { "version": "0.4.4", diff --git a/package.json b/package.json index 6af7b3590..a25fa946c 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "@antora/cli": "^3.1.6", "@antora/site-generator-default": "^3.1.6", - "@neo4j-antora/antora-add-notes": "^0.1.6", + "@neo4j-antora/antora-add-notes": "^0.3.1", "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", "@neo4j-antora/antora-page-roles": "^0.3.1", "@neo4j-antora/antora-table-footnotes": "^0.3.2", From e5d902d5fdb939953373e5f10121cf704268506d Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 21 Dec 2023 15:01:21 +0000 Subject: [PATCH 344/383] Use dependabot groups (#822) --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3a3cce576..1e63f2a92 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,8 @@ updates: directory: "/" # Location of package manifests schedule: interval: "weekly" + groups: + prod-dependencies: + dependency-type: "production" + dev-dependencies: + dependency-type: "development" From 5bd9bddacbff673f48c029f12dcefb1754ce873a Mon Sep 17 00:00:00 2001 From: Matthew Parnell Date: Tue, 2 Jan 2024 14:56:28 +0000 Subject: [PATCH 345/383] db.index.vector.createNodeIndex replaced by CREATE VECTOR INDEX (#824) Paired with https://github.com/neo4j/docs-operations/pull/1295 --- modules/ROOT/pages/indexes-for-vector-search.adoc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/indexes-for-vector-search.adoc b/modules/ROOT/pages/indexes-for-vector-search.adoc index 65718e9f0..c2af0708b 100644 --- a/modules/ROOT/pages/indexes-for-vector-search.adoc +++ b/modules/ROOT/pages/indexes-for-vector-search.adoc @@ -48,8 +48,13 @@ The procedures and commands for vector indexes are listed in the following table | Create vector index. | `+CREATE VECTOR INDEX ...+` -| Create a vector index for the specified label and property with the given vector dimensionality using the given similarity function. -See the xref:indexes-for-search-performance.adoc#indexes-create-indexes[`CREATE INDEX`] command for more details. +| label:new[Introduced in 5.15] Create a vector index for the specified label and property with the given vector dimensionality using the given similarity function. +See the xref:indexes-for-search-performance.adoc#indexes-create-indexes[`CREATE INDEX`] command for more details. Replaces {link-procedures-reference}#procedure_db_index_vector_createNodeIndex[`db.index.vector.createNodeIndex`]. + +| Create vector index. +| {link-procedures-reference}#procedure_db_index_vector_createNodeIndex[`db.index.vector.createNodeIndex`] +| It is replaced by `CREATE VECTOR INDEX ...`. + | Use vector index. | {link-procedures-reference}#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] @@ -62,7 +67,7 @@ Returns the requested number of approximate nearest neighbor nodes and their sim | Listing all vector indexes. | `SHOW VECTOR INDEXES` -| Lists all vector indexes, see the xref:indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES`] command for details. +| label:new[Introduced in 5.15] Lists all vector indexes, see the xref:indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES`] command for details. | Set vector property. | {link-procedures-reference}#procedure_db_create_setNodeVectorProperty[`db.create.setNodeVectorProperty`] @@ -70,7 +75,7 @@ Returns the requested number of approximate nearest neighbor nodes and their sim | Set vector property. | {link-procedures-reference}#procedure_db_create_setVectorProperty[`db.create.setVectorProperty`] -| label:beta[] label:deprecated[] It is replaced by {link-procedures-reference}#procedure_db_create_setNodeVectorProperty[`db.create.setNodeVectorProperty`] +| label:beta[] label:deprecated[] It is replaced by {link-procedures-reference}#procedure_db_create_setNodeVectorProperty[`db.create.setNodeVectorProperty`]. |=== From 3f2ee9d9dd1eb131a8431a363802e2ba52dab6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 4 Jan 2024 12:48:55 +0100 Subject: [PATCH 346/383] Update style-guide advice about functions (#826) --- modules/ROOT/pages/styleguide.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/styleguide.adoc b/modules/ROOT/pages/styleguide.adoc index b1de62712..520297a78 100644 --- a/modules/ROOT/pages/styleguide.adoc +++ b/modules/ROOT/pages/styleguide.adoc @@ -14,8 +14,8 @@ For rules and recommendations for naming of labels, relationship types and prope * When using Cypher language constructs in prose, use a `monospaced` font and follow the styling rules. * When referring to labels and relationship types, the colon should be included as follows: `:Label`, `:REL_TYPE`. -* When referring to functions, use lower camel case and parentheses should be used as follows: `shortestPath()`. -Arguments should normally not be included. +* When referring to functions, use lower camel case and parentheses. +For example: `toString()`. * If you are storing Cypher statements in a separate file, use the file extension `.cypher`. From b59fbff5231cbcc88c9154c4bd0b5def5af613fe Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Fri, 5 Jan 2024 11:54:53 +0000 Subject: [PATCH 347/383] Use new GHA workflows (#828) Updates include: - Improved security - Enhanced comments with links to updated pages in the deployed HTML - Branch verification, including an option to check links and style --- .github/workflows/docs-branch-checks.yml | 55 ++++++++++++++++++ .github/workflows/docs-deploy-surge.yml | 53 +++++++++++++---- .github/workflows/docs-pr-checks.yml | 74 ++++++++++++++++++++++++ .github/workflows/docs-pr.yml | 30 ---------- .github/workflows/docs-teardown.yml | 20 +++++-- 5 files changed, 187 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/docs-branch-checks.yml create mode 100644 .github/workflows/docs-pr-checks.yml delete mode 100644 .github/workflows/docs-pr.yml diff --git a/.github/workflows/docs-branch-checks.yml b/.github/workflows/docs-branch-checks.yml new file mode 100644 index 000000000..1f09367e4 --- /dev/null +++ b/.github/workflows/docs-branch-checks.yml @@ -0,0 +1,55 @@ +# This is an example of the docs-pr.yml workflow available from the recrwplay org +name: "Verify Branch" + +on: + # push: + # branches: + # - dev + # schedule: + # - cron: '00 16 * * *' + workflow_dispatch: + inputs: + html: + description: 'Generate HTML' + type: boolean + required: false + default: true + links: + description: 'Check links' + type: boolean + required: false + default: true + lint: + description: 'Lint docs' + type: boolean + required: false + default: true + +jobs: + + docs-build: + if: ${{ inputs.html }} + name: Generate HTML + uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@dev + with: + retain-artifacts: 14 + deploy-id: 0 + package-script: 'publish-verify' + cli-options: '--extension ./.docs-tools/extensions/antora/aliases-redirects' + + docs-verify: + name: Verify HTML + needs: docs-build + uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@dev + + docs-links: + if: ${{ inputs.links }} + name: Check links + needs: docs-build + uses: neo4j/docs-tools/.github/workflows/reusable-docs-links.yml@dev + + docs-lint: + if: ${{ inputs.lint }} + name: Lint docs + uses: neo4j/docs-tools/.github/workflows/reusable-docs-vale.yml@dev + \ No newline at end of file diff --git a/.github/workflows/docs-deploy-surge.yml b/.github/workflows/docs-deploy-surge.yml index 0eeec088d..ec2ad6fb8 100644 --- a/.github/workflows/docs-deploy-surge.yml +++ b/.github/workflows/docs-deploy-surge.yml @@ -1,32 +1,38 @@ # Use this starter workflow to deploy HTML generated by Antora to surge.sh # Docs are published at --.surge.sh -# By default, this workflow runs on completion of a workflow called "Verify PR" +# +# By default, this workflow runs on completion of a workflow called "Verify docs PR" +# # This workflow expects the triggering workflow to generate an artifact called "docs" - # - update the reference to "docs" and "docs.zip" in this workflow if your triggering workflow generates an artifact with a different name + name: "Deploy to surge" on: workflow_run: - workflows: ["Verify PR"] + workflows: ["Verify Docs PR"] types: - completed jobs: publish-docs: + # Uncomment this if statement to deploy only when the PR builds cleanly # if: github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest steps: - name: "Download built documentation" - uses: actions/github-script@v6.0.0 + uses: actions/github-script@v6.4.1 + env: + RUN_ID: ${{ github.event.workflow_run.id }} + WORKSPACE: ${{ github.workspace }} with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, - run_id: ${{ github.event.workflow_run.id }}, + run_id: ${{ env.RUN_ID }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "docs" @@ -38,7 +44,7 @@ jobs: archive_format: 'zip', }); var fs = require('fs'); - fs.writeFileSync('${{ github.workspace }}/docs.zip', Buffer.from(download.data)); + fs.writeFileSync('${{ env.WORKSPACE }}/docs.zip', Buffer.from(download.data)); - run: unzip docs.zip @@ -47,6 +53,15 @@ jobs: deployid=$(> "$GITHUB_OUTPUT" + + - id: get-deploy-url + env: + ORG: ${{ github.event.repository.owner.login }} + REPO: ${{ github.event.repository.name }} + DEPLOYID: ${{ steps.get-deploy-id.outputs.deploy-id }} + run: | + deployurl=$ORG-$REPO-$DEPLOYID.surge.sh + echo "deploy-url=$deployurl" >> $GITHUB_OUTPUT - uses: actions/setup-node@v3 with: @@ -55,17 +70,35 @@ jobs: - name: Deploy docs to surge shell: bash env: + DEPLOY_URL: ${{ steps.get-deploy-url.outputs.deploy-url }} SURGE_TOKEN: "${{ secrets.DOCS_SURGE_TOKEN }}" run: | npm install -g surge - surge ./site ${{ github.event.repository.owner.login}}-${{ github.event.repository.name}}-${{ steps.get-deploy-id.outputs.deploy-id }}.surge.sh --token "$SURGE_TOKEN" + surge ./site $DEPLOY_URL --token "$SURGE_TOKEN" - - name: Comment on PR + # If the PR artifacts include a changelog file, add it to the PR as a comment + # The changelog contains links to new and changed files in the deployed docs + - name: Comment on PR (changelog) + if: ${{ hashFiles('changelog') != '' }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + number: ${{ steps.get-deploy-id.outputs.deploy-id }} + recreate: true + header: docs-pr-changes + path: changelog + GITHUB_TOKEN: ${{ secrets.DOCS_PR_COMMENT_TOKEN }} + + # If there's no changelog, add a generic comment to the PR + - name: Comment on PR (no changelog) + if: ${{ hashFiles('changelog') == '' }} + env: + DEPLOY_URL: ${{ steps.get-deploy-url.outputs.deploy-url }} uses: marocchino/sticky-pull-request-comment@v2 with: number: ${{ steps.get-deploy-id.outputs.deploy-id }} + header: docs-pr-changes message: | - This PR includes documentation updates. + Looks like you've updated the documentation! - You can view the updated docs at https://${{ github.event.repository.owner.login}}-${{ github.event.repository.name}}-${{ steps.get-deploy-id.outputs.deploy-id }}.surge.sh + Check out your changes at https://${{ env.DEPLOY_URL }} GITHUB_TOKEN: ${{ secrets.DOCS_PR_COMMENT_TOKEN }} diff --git a/.github/workflows/docs-pr-checks.yml b/.github/workflows/docs-pr-checks.yml new file mode 100644 index 000000000..b4c1ff54a --- /dev/null +++ b/.github/workflows/docs-pr-checks.yml @@ -0,0 +1,74 @@ + +name: "Verify Docs PR" + +on: + pull_request: + branches: + - dev + +jobs: + + # Generate HTML + docs-build-pr: + uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@dev + with: + deploy-id: ${{ github.event.number }} + retain-artifacts: 14 + pageList: true + + # Parse the json log output from the HTML build, and output warnings and errors as annotations + # Optionally, fail the build if there are warnings or errors + # By default, the job fails if there are errors, passes if there are warnings only. + docs-verify-pr: + needs: docs-build-pr + uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@dev + with: + failOnWarnings: true + + # Get lists of changes in the PR + # - all updated asciidoc files + # - all updated asciidoc pages + # - all new asciidoc pages + docs-changes-pr: + runs-on: ubuntu-latest + outputs: + asciidoc-files: ${{ steps.get-file-changes.outputs.asciidoc_all_changed_files }} + pages-modified: ${{ steps.get-file-changes.outputs.pages_modified_files }} + pages-added: ${{ steps.get-file-changes.outputs.pages_added_files }} + steps: + - name: Get file changes + id: get-file-changes + uses: tj-actions/changed-files@v41 + with: + separator: ',' + files_yaml: | + pages: + - modules/**/pages/**/*.adoc + asciidoc: + - modules/**/*.adoc + + # Generate a PR comment if the docs are using the pageList extension + # The extension maps asciidoc source files to their HTML output paths + # The comment will contain links to new and changed pages in the deployed HTML docs + docs-updates-comment-pr: + if: needs.docs-build-pr.outputs.pages-listed == 'success' + needs: [docs-build-pr, docs-changes-pr] + uses: neo4j/docs-tools/.github/workflows/reusable-docs-pr-changes.yml@dev + with: + pages-modified: ${{ needs.docs-changes-pr.outputs.pages-modified }} + pages-added: ${{ needs.docs-changes-pr.outputs.pages-added }} + + # Use vale to verify the changes against the style guide + # You can specify your own vale config file if you want to override or replace the default Neo4j rules + # docs-lint-pr: + # needs: docs-changes-pr + # if: needs.docs-changes-pr.outputs.asciidoc-files != '' + # uses: neo4j/docs-tools/.github/workflows/reusable-docs-vale.yml@dev + # with: + # files: ${{ needs.docs-changes-pr.outputs.asciidoc-files }} + # vale-fail-on-error: true + # # use-default-rules: false + # # vale-config-file: .vale.ini + # separator: ',' + + diff --git a/.github/workflows/docs-pr.yml b/.github/workflows/docs-pr.yml deleted file mode 100644 index f5b4ec581..000000000 --- a/.github/workflows/docs-pr.yml +++ /dev/null @@ -1,30 +0,0 @@ -# Use this starter workflow to verify PRs that include a change to the docs -# The default setup runs against PRs targeting your default branch -# Default artifact retention is 7 days - -name: "Verify PR" - -on: - pull_request: - branches: - - "3.5" - - "4.[0-9]" - - "5.x" - - "dev" - -jobs: - - # note that the build job requires a build-verify script in package.json - # build-verify should build html with Antora, and output a .json log file - # eg 'antora playbook.yml --log-format=json --log-file ./build/log/log.json' - docs-build-for-pr: - uses: recrwplay/actions-demo/.github/workflows/reusable-docs-build.yml@main - with: - deploy-id: ${{ github.event.number }} - retain-artifacts: 14 - - docs-verify-for-pr: - needs: docs-build-for-pr - uses: recrwplay/actions-demo/.github/workflows/reusable-docs-verify.yml@main - with: - failOnWarnings: true \ No newline at end of file diff --git a/.github/workflows/docs-teardown.yml b/.github/workflows/docs-teardown.yml index 84b9317ec..1d93cba74 100644 --- a/.github/workflows/docs-teardown.yml +++ b/.github/workflows/docs-teardown.yml @@ -4,10 +4,7 @@ name: "Documentation Teardown" on: pull_request_target: branches: - - "3.5" - - "4.[0-9]" - - "5.x" - - "dev" + - dev types: - closed @@ -19,17 +16,30 @@ jobs: - uses: actions/setup-node@v3 with: node-version: lts/* + + - id: get-deploy-url + env: + ORG: ${{ github.event.repository.owner.login }} + REPO: ${{ github.event.repository.name }} + DEPLOYID: ${{ github.event.pull_request.number }} + run: | + deployurl=$ORG-$REPO-$DEPLOYID.surge.sh + echo "deploy-url=$deployurl" >> $GITHUB_OUTPUT + - name: Teardown documentation shell: bash env: SURGE_TOKEN: "${{ secrets.DOCS_SURGE_TOKEN }}" + DEPLOY_URL: ${{ steps.get-deploy-url.outputs.deploy-url }} run: | npm install -g surge - surge teardown ${{ github.event.repository.owner.login}}-${{ github.event.repository.name}}-${{ github.event.pull_request.number }}.surge.sh --token "$SURGE_TOKEN" + surge teardown $DEPLOY_URL --token "$SURGE_TOKEN" + - name: Comment on PR uses: marocchino/sticky-pull-request-comment@v2 with: number: ${{ github.event.pull_request.number }} + header: docs-pr-changes message: | Thanks for the documentation updates. From d5be7e4d5e6832fa251c76b29a188c8d512e483a Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Mon, 8 Jan 2024 14:09:57 +0000 Subject: [PATCH 348/383] Update docs-branch-checks.yml (#829) --- .github/workflows/docs-branch-checks.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/docs-branch-checks.yml b/.github/workflows/docs-branch-checks.yml index 1f09367e4..d30ba342e 100644 --- a/.github/workflows/docs-branch-checks.yml +++ b/.github/workflows/docs-branch-checks.yml @@ -34,8 +34,6 @@ jobs: with: retain-artifacts: 14 deploy-id: 0 - package-script: 'publish-verify' - cli-options: '--extension ./.docs-tools/extensions/antora/aliases-redirects' docs-verify: name: Verify HTML @@ -52,4 +50,4 @@ jobs: if: ${{ inputs.lint }} name: Lint docs uses: neo4j/docs-tools/.github/workflows/reusable-docs-vale.yml@dev - \ No newline at end of file + From b90c9cf1fe37f666e18b2ab6e31928d0960ae786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:41:24 +0100 Subject: [PATCH 349/383] New Indexes chapter (#770) --- modules/ROOT/content-nav.adoc | 18 +- modules/ROOT/images/full_text_graph.svg | 1 + .../images/using_indexes_example_graph.svg | 9 + .../tutorials/advanced-query-tuning.adoc | 2 +- .../tutorials/basic-query-tuning.adoc | 2 +- modules/ROOT/pages/clauses/index.adoc | 12 +- modules/ROOT/pages/clauses/match.adoc | 2 +- modules/ROOT/pages/clauses/merge.adoc | 2 +- .../pages/clauses/transaction-clauses.adoc | 2 +- modules/ROOT/pages/clauses/where.adoc | 2 +- modules/ROOT/pages/constraints/examples.adoc | 8 +- modules/ROOT/pages/constraints/index.adoc | 2 +- ...ions-additions-removals-compatibility.adoc | 8 +- .../pages/indexes-for-full-text-search.adoc | 485 ------ .../pages/indexes-for-search-performance.adoc | 1298 --------------- modules/ROOT/pages/indexes/index.adoc | 15 + .../index-hints.adoc} | 10 +- .../managing-indexes.adoc | 1099 +++++++++++++ .../search-performance-indexes/overview.adoc | 24 + .../using-indexes.adoc | 1011 ++++++++++++ .../semantic-indexes/full-text-indexes.adoc | 394 +++++ .../indexes/semantic-indexes/overview.adoc | 14 + .../semantic-indexes/vector-indexes.adoc} | 19 +- modules/ROOT/pages/indexes/syntax.adoc | 277 ++++ .../ROOT/pages/planning-and-tuning/index.adoc | 3 +- .../planning-and-tuning/query-tuning.adoc | 304 ++++ .../query-tuning/index.adoc | 29 - .../query-tuning/indexes.adoc | 1443 ----------------- .../ROOT/pages/values-and-types/spatial.adoc | 11 +- 29 files changed, 3203 insertions(+), 3303 deletions(-) create mode 100644 modules/ROOT/images/full_text_graph.svg create mode 100644 modules/ROOT/images/using_indexes_example_graph.svg delete mode 100644 modules/ROOT/pages/indexes-for-full-text-search.adoc delete mode 100644 modules/ROOT/pages/indexes-for-search-performance.adoc create mode 100644 modules/ROOT/pages/indexes/index.adoc rename modules/ROOT/pages/{planning-and-tuning/query-tuning/using.adoc => indexes/search-performance-indexes/index-hints.adoc} (99%) create mode 100644 modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc create mode 100644 modules/ROOT/pages/indexes/search-performance-indexes/overview.adoc create mode 100644 modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc create mode 100644 modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc create mode 100644 modules/ROOT/pages/indexes/semantic-indexes/overview.adoc rename modules/ROOT/pages/{indexes-for-vector-search.adoc => indexes/semantic-indexes/vector-indexes.adoc} (94%) create mode 100644 modules/ROOT/pages/indexes/syntax.adoc create mode 100644 modules/ROOT/pages/planning-and-tuning/query-tuning.adoc delete mode 100644 modules/ROOT/pages/planning-and-tuning/query-tuning/index.adoc delete mode 100644 modules/ROOT/pages/planning-and-tuning/query-tuning/indexes.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index c163f5e20..6b1add734 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -76,9 +76,15 @@ ** xref:functions/database.adoc[] ** xref:functions/user-defined.adoc[] -* xref:indexes-for-search-performance.adoc[] -* xref:indexes-for-full-text-search.adoc[] -* xref:indexes-for-vector-search.adoc[] +* xref:indexes/index.adoc[] +** xref:indexes/search-performance-indexes/overview.adoc[] +*** xref:indexes/search-performance-indexes/managing-indexes.adoc[] +*** xref:indexes/search-performance-indexes/using-indexes.adoc[] +*** xref:indexes/search-performance-indexes/index-hints.adoc[] +** xref:indexes/semantic-indexes/overview.adoc[] +*** xref:indexes/semantic-indexes/full-text-indexes.adoc[] +*** xref:indexes/semantic-indexes/vector-indexes.adoc[] +** xref:indexes/syntax.adoc[] * xref:constraints/index.adoc[] ** xref:constraints/syntax.adoc[] @@ -91,10 +97,8 @@ ** xref:planning-and-tuning/runtimes/index.adoc[] *** xref:planning-and-tuning/runtimes/concepts.adoc[Concepts] *** xref:planning-and-tuning/runtimes/reference.adoc[] -** xref:planning-and-tuning/query-tuning/index.adoc[] -*** xref:planning-and-tuning/query-tuning/indexes.adoc[] -*** xref:planning-and-tuning/query-tuning/using.adoc[] -*** xref:planning-and-tuning/query-tuning/query-options.adoc[] +** xref:planning-and-tuning/query-tuning.adoc[] + * xref:query-caches/index.adoc[] ** xref:query-caches/unified-query-caches.adoc[] diff --git a/modules/ROOT/images/full_text_graph.svg b/modules/ROOT/images/full_text_graph.svg new file mode 100644 index 000000000..bfae87e68 --- /dev/null +++ b/modules/ROOT/images/full_text_graph.svg @@ -0,0 +1 @@ +EMAILEDmessage:'I have booked a team meeting tomorrow'REVIEWEDmessage:'Nils-Erik is reportedly difficult to work with.'Employeename:'Maya Tanaka'position:'Senior Engineer'team:'Operations'Managername:'Lisa Danielsson'position:'Engineering manager'Employeename:'Nils-Erik Karlsson'position:'Engineer'team:'Kernel'peerReviews:['Nils-Erik is difficult to work with.', 'Nils-Erik is often late for work.']Employeename:'Nils Johansson'position:'Engineer'team:'Operations' \ No newline at end of file diff --git a/modules/ROOT/images/using_indexes_example_graph.svg b/modules/ROOT/images/using_indexes_example_graph.svg new file mode 100644 index 000000000..f0c6251a6 --- /dev/null +++ b/modules/ROOT/images/using_indexes_example_graph.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/modules/ROOT/pages/appendix/tutorials/advanced-query-tuning.adoc b/modules/ROOT/pages/appendix/tutorials/advanced-query-tuning.adoc index c3b7fa647..1a91c2f5c 100644 --- a/modules/ROOT/pages/appendix/tutorials/advanced-query-tuning.adoc +++ b/modules/ROOT/pages/appendix/tutorials/advanced-query-tuning.adoc @@ -5,7 +5,7 @@ This page describes advanced query optimizations based on native index capabilities. One of the most important and useful ways of optimizing Cypher queries involves creating appropriate indexes. -This is described in more detail in xref::indexes-for-search-performance.adoc[], and demonstrated in xref::appendix/tutorials/basic-query-tuning.adoc[]. +This is described in more detail in xref:indexes/search-performance-indexes/managing-indexes.adoc[], and demonstrated in xref::appendix/tutorials/basic-query-tuning.adoc[]. In summary, an index will be based on the combination of a `Label` and a `property`. Any Cypher query that searches for nodes with a specific label and some predicate on the property (equality, range or existence) will be planned to use the index if the cost planner deems that to be the most efficient solution. diff --git a/modules/ROOT/pages/appendix/tutorials/basic-query-tuning.adoc b/modules/ROOT/pages/appendix/tutorials/basic-query-tuning.adoc index a5ed3db92..ed355ae42 100644 --- a/modules/ROOT/pages/appendix/tutorials/basic-query-tuning.adoc +++ b/modules/ROOT/pages/appendix/tutorials/basic-query-tuning.adoc @@ -764,5 +764,5 @@ Total database accesses: 5, total allocated memory: 184 1 row ---- -Our execution plan is down to a single row and uses the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[Node Index Seek] operator which does an index seek (see xref::indexes-for-search-performance.adoc[]) to find the appropriate node. +Our execution plan is down to a single row and uses the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[Node Index Seek] operator which does an index seek (see xref::indexes/search-performance-indexes/managing-indexes.adoc[]) to find the appropriate node. diff --git a/modules/ROOT/pages/clauses/index.adoc b/modules/ROOT/pages/clauses/index.adoc index 4f22dae2c..0fabe4112 100644 --- a/modules/ROOT/pages/clauses/index.adoc +++ b/modules/ROOT/pages/clauses/index.adoc @@ -234,22 +234,22 @@ m| xref:clauses/transaction-clauses.adoc#query-terminate-transactions[TERMINATE == Reading hints These comprise clauses used to specify planner hints when tuning a query. -More details regarding the usage of these -- and query tuning in general -- can be found in xref::planning-and-tuning/query-tuning/using.adoc[Planner hints and the USING keyword]. +More details regarding the usage of these -- and query tuning in general -- can be found in xref::indexes/search-performance-indexes/index-hints.adoc[Planner hints and the USING keyword]. [options="header"] |=== | Hint | Description -m| xref::planning-and-tuning/query-tuning/using.adoc#query-using-index-hint[USING INDEX] +m| xref::indexes/search-performance-indexes/index-hints.adoc#query-using-index-hint[USING INDEX] | Index hints are used to specify which index, if any, the planner should use as a starting point. -m| xref::planning-and-tuning/query-tuning/using.adoc#query-using-index-hint[USING INDEX SEEK] +m| xref::indexes/search-performance-indexes/index-hints.adoc#query-using-index-hint[USING INDEX SEEK] | Index seek hint instructs the planner to use an index seek for this clause. -m| xref::planning-and-tuning/query-tuning/using.adoc#query-using-scan-hint[USING SCAN] +m| xref::indexes/search-performance-indexes/index-hints.adoc#query-using-scan-hint[USING SCAN] | Scan hints are used to force the planner to do a label scan (followed by a filtering operation) instead of using an index. -m| xref::planning-and-tuning/query-tuning/using.adoc#query-using-join-hint[USING JOIN] +m| xref::indexes/search-performance-indexes/index-hints.adoc#query-using-join-hint[USING JOIN] | Join hints are used to enforce a join operation at specified points. |=== @@ -263,7 +263,7 @@ These comprise clauses to create, show, and drop indexes and constraints. |=== | Clause | Description -m| xref::indexes-for-search-performance.adoc#indexes-syntax[CREATE \| SHOW \| DROP INDEX] +m| xref:indexes/search-performance-indexes/managing-indexes.adoc#indexes-syntax[CREATE \| SHOW \| DROP INDEX] | Create, show or drop an index. m| xref::constraints/syntax.adoc[CREATE \| SHOW \| DROP CONSTRAINT] diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 2c4113488..ae412579c 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -24,7 +24,7 @@ Cypher is declarative, and so usually the query itself does not specify the algo Neo4j will automatically work out the best approach to finding start nodes and matching patterns. Predicates in `WHERE` parts can be evaluated before pattern matching, during pattern matching, or after finding matches. However, there are cases where you can influence the decisions taken by the query compiler. -Read more about indexes in xref::indexes-for-search-performance.adoc[], and more about specifying hints to force Neo4j to solve a query in a specific way in xref::planning-and-tuning/query-tuning/using.adoc[Planner hints and the USING keyword]. +Read more about indexes in xref:indexes/search-performance-indexes/managing-indexes.adoc[], and more about specifying hints to force Neo4j to solve a query in a specific way in xref::indexes/search-performance-indexes/index-hints.adoc[Planner hints and the USING keyword]. [[match-example-graph]] diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index fd9abb1e5..347a3ba01 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -15,7 +15,7 @@ If there isn't a node with the specific `name` property, a new node will be crea [NOTE] ==== For performance reasons, creating a schema index on the label or property is highly recommended when using `MERGE`. -See xref::indexes-for-search-performance.adoc[] for more information. +See xref:indexes/search-performance-indexes/managing-indexes.adoc[] for more information. ==== When using `MERGE` on full patterns, the behavior is that either the whole pattern matches, or the whole pattern is created. diff --git a/modules/ROOT/pages/clauses/transaction-clauses.adoc b/modules/ROOT/pages/clauses/transaction-clauses.adoc index b5a3edddb..e0b877c75 100644 --- a/modules/ROOT/pages/clauses/transaction-clauses.adoc +++ b/modules/ROOT/pages/clauses/transaction-clauses.adoc @@ -82,7 +82,7 @@ m| MAP m| planner a| The name of the Cypher planner used to plan the query currently executing in this transaction, or an empty string if no query is currently executing. -For details, see xref::planning-and-tuning/query-tuning/index.adoc#cypher-planner[Cypher planner]. +For details, see xref::planning-and-tuning/query-tuning.adoc#cypher-planner[Cypher planner]. m| STRING m| runtime diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index 1ec5a6bfd..b0505906d 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -16,7 +16,7 @@ In the case of `WITH`, however, `WHERE` simply filters the results. In the case of multiple `MATCH` / `OPTIONAL MATCH` clauses, the predicate in `WHERE` is always a part of the patterns in the directly preceding `MATCH` / `OPTIONAL MATCH`. Both results and performance may be impacted if `WHERE` is put inside the wrong `MATCH` clause. -xref::indexes-for-search-performance.adoc[Indexes] may be used to optimize queries using `WHERE` in a variety of cases. +xref:indexes/search-performance-indexes/managing-indexes.adoc[Indexes] may be used to optimize queries using `WHERE` in a variety of cases. [[where-example-graph]] == Example graph diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc index 712fbc4c7..76678f29f 100644 --- a/modules/ROOT/pages/constraints/examples.adoc +++ b/modules/ROOT/pages/constraints/examples.adoc @@ -325,7 +325,7 @@ CREATE CONSTRAINT book_title FOR (book:Book) REQUIRE book.title IS UNIQUE ---- In this case, the constraint cannot be created because it is in conflict with the existing graph. -Either use xref::indexes-for-search-performance.adoc[] instead, or remove/correct the offending nodes and then re-apply the constraint. +Either use xref:indexes/search-performance-indexes/managing-indexes.adoc[] instead, or remove/correct the offending nodes and then re-apply the constraint. .Error message [source, error] @@ -672,7 +672,7 @@ CREATE CONSTRAINT series_title FOR ()-[sequel:SEQUEL_OF]-() REQUIRE (sequel.seri ---- In this case, the constraint cannot be created because it is in conflict with the existing graph. -Either use xref::indexes-for-search-performance.adoc[] instead, or remove/correct the offending relationships and then re-apply the constraint. +Either use xref:indexes/search-performance-indexes/managing-indexes.adoc[] instead, or remove/correct the offending relationships and then re-apply the constraint. .Error message [source, error] @@ -2368,7 +2368,7 @@ CREATE CONSTRAINT actor_born FOR (actor:Actor) REQUIRE (actor.born) IS NODE KEY ---- In this case, the node key constraint cannot be created because it is in conflict with the existing graph. -Either use xref::indexes-for-search-performance.adoc[] instead, or remove/correct the offending nodes and then re-apply the constraint. +Either use xref:indexes/search-performance-indexes/managing-indexes.adoc[] instead, or remove/correct the offending nodes and then re-apply the constraint. .Error message [source, error] @@ -2729,7 +2729,7 @@ CREATE CONSTRAINT knows_level FOR ()-[knows:KNOWS]-() REQUIRE (knows.level) IS R ---- In this case, the relationship key constraint cannot be created because it is in conflict with the existing graph. -Either use xref::indexes-for-search-performance.adoc[] instead, or remove or correct the offending relationships and then re-apply the constraint. +Either use xref:indexes/search-performance-indexes/managing-indexes.adoc[] instead, or remove or correct the offending relationships and then re-apply the constraint. .Error message [source, error] diff --git a/modules/ROOT/pages/constraints/index.adoc b/modules/ROOT/pages/constraints/index.adoc index 4bcf3b3e0..e79ed3d5d 100644 --- a/modules/ROOT/pages/constraints/index.adoc +++ b/modules/ROOT/pages/constraints/index.adoc @@ -86,7 +86,7 @@ Creating a constraint has the following implications on indexes: * Adding a node key, relationship key, or property uniqueness constraint on a single property also adds an index on that property, and therefore, an index of the same index type, label/relationship type, and property combination cannot be added separately. * Adding a node key, relationship key, or property uniqueness constraint for a set of properties also adds an index on those properties, and therefore, an index of the same index type, label/relationship type, and properties combination cannot be added separately. * Cypher will use these indexes for lookups just like other indexes. - Refer to xref::indexes-for-search-performance.adoc[] for more details on indexes. + Refer to xref:indexes/search-performance-indexes/managing-indexes.adoc[] for more details on indexes. * If a node key, relationship key, or property uniqueness constraint is dropped and the backing index is still required, the index need to be created explicitly. Additionally, the following is true for constraints: diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 139144ec4..47807d95f 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -134,7 +134,7 @@ label:updated[] SHOW VECTOR INDEXES ---- -| Extended xref:indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES`] with easy filtering for vector indexes. +| Extended xref:indexes/search-performance-indexes/managing-indexes.adoc#indexes-list-indexes[`SHOW INDEXES`] with easy filtering for vector indexes. This is equivalent to `SHOW INDEXES WHERE type = 'VECTOR'`. a| @@ -146,7 +146,7 @@ label:updated[] MATCH (n:Label) WHERE $param IS :: STRING NOT NULL AND n.prop = $param ---- -| `IS :: STRING NOT NULL` is now an xref:planning-and-tuning/query-tuning/indexes.adoc#text-index-type-predicate-expressions[index-compatible predicate]. +| `IS :: STRING NOT NULL` is now an xref:indexes/search-performance-indexes/using-indexes.adoc#text-indexes-type-predicate-expressions[index-compatible predicate]. |=== @@ -4217,7 +4217,7 @@ label:new[] DROP INDEX name ---- a| -xref:indexes-for-search-performance.adoc#indexes-drop-an-index[New command] for dropping an index by name. +xref:indexes/search-performance-indexes/managing-indexes.adoc#indexes-drop-an-index[New command] for dropping an index by name. a| @@ -4339,7 +4339,7 @@ An example of this is `CALL db.index.explicit.searchNodes('my_index','email:me*' | `MATCH (n)-[x:A\|:B\|:C]-() RETURN n` | Syntax | Deprecated | Replaced by `MATCH (n)-[x:A\|B\|C]-() RETURN n` | `MATCH (n)-[x:A\|:B\|:C*]-() RETURN n` | Syntax | Deprecated | Replaced by `MATCH (n)-[x:A\|B\|C*]-() RETURN n` | link:/docs/java-reference/5/extending-neo4j/aggregation-functions#extending-neo4j-aggregation-functions[User-defined aggregation functions] | Functionality | Added | -| xref:indexes-for-search-performance.adoc[Composite indexes] | Index | Added | +| xref:indexes/search-performance-indexes/managing-indexes.adoc[Composite indexes] | Index | Added | | xref:constraints/examples.adoc#constraints-examples-node-key[Node Key] | Index | Added | Neo4j Enterprise Edition only | `CYPHER runtime=compiled` (Compiled runtime) | Functionality | Added | Neo4j Enterprise Edition only | xref:functions/list.adoc#functions-reverse-list[reverse()] | Function | Extended | Now also allows a list as input diff --git a/modules/ROOT/pages/indexes-for-full-text-search.adoc b/modules/ROOT/pages/indexes-for-full-text-search.adoc deleted file mode 100644 index 66fb6912c..000000000 --- a/modules/ROOT/pages/indexes-for-full-text-search.adoc +++ /dev/null @@ -1,485 +0,0 @@ -:description: This chapter describes how to use full-text indexes, to enable full-text search. - -[[indexes-fulltext-search]] -= Full-text search index - -Full-text indexes are powered by the link:https://lucene.apache.org/[Apache Lucene] indexing and search library, and can be used to index nodes and relationships by string properties. -A full-text index allows you to write queries that match within the _contents_ of indexed string properties. -For instance, the range and text indexes described in previous sections can only perform limited matching on strings; exact, prefix, substring, or suffix matches. -A full-text index will instead tokenize the indexed string values, so it can match _terms_ anywhere within the strings. -How the indexed strings are tokenized and broken into terms, is determined by what analyzer the full-text index is configured with. -For instance, the _swedish_ analyzer knows how to tokenize and stem Swedish words, and will avoid indexing Swedish stop words. -The complete list of stop words for each analyzer is included in the result of the `db.index.fulltext.listAvailableAnalyzers` procedure. - - -Full-text indexes: - -* support the indexing of both nodes and relationships. -* support configuring custom analyzers, including analyzers that are not included with Lucene itself. -* can be queried using the Lucene query language. -* can return the _score_ for each result from a query. -* are kept up to date automatically, as nodes and relationships are added, removed, and modified. -* will automatically populate newly created indexes with the existing data in a store. -* can be checked by the consistency checker, and they can be rebuilt if there is a problem with them. -* are a projection of the store, and can only index nodes and relationships by the contents of their properties. -* include only property values of types `STRING` or `LIST`. -* can support any number of documents in a single index. -* are created, dropped, and updated transactionally, and is automatically replicated throughout a cluster. -* can be accessed via Cypher procedures. -* can be configured to be _eventually consistent_, in which index updating is moved from the commit path to a background thread. -Using this feature, it is possible to work around the slow Lucene writes from the performance critical commit process, thus removing the main bottlenecks for Neo4j write performance. - -At first sight, the construction of full-text indexes can seem similar to regular indexes. -However there are some things that are interesting to note: -In contrast to xref::indexes-for-search-performance.adoc[other indexes], a full-text index can be: - -* applied to more than one label. -* applied to more than one relationship type. -* applied to more than one property at a time (similar to a xref::indexes-for-search-performance.adoc#indexes-create-a-composite-range-index-for-nodes[_composite index_]) but with an important difference: -While a composite index applies only to entities that match the indexed label and _all_ of the indexed properties, full-text index will index entities that have at least one of the indexed labels or relationship types, and at least one of the indexed properties. - -For information on how to configure full-text indexes, refer to link:{neo4j-docs-base-uri}/operations-manual/{page-version}/performance/index-configuration#index-configuration-fulltext[Operations Manual -> Indexes to support full-text search]. - - -[[indexes-fulltext-search-manage]] -== Full-text search procedures - -Full-text indexes are managed through commands and used through built-in procedures, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures[Operations Manual -> Procedures] for a complete reference. - -The commands and procedures for full-text indexes are listed in the table below: - - -[options="header"] -|=== -| Usage | Procedure/Command | Description - -| Create full-text node index. -| `+CREATE FULLTEXT INDEX ...+` -| Create a node full-text index for the given labels and properties. -See the xref:indexes-for-search-performance.adoc#indexes-create-indexes[`CREATE INDEX`] command for more details. - -| Create full-text relationship index. -| `+CREATE FULLTEXT INDEX ...+` -a| -Create a relationship full-text index for the given relationship types and properties. - -| List available analyzers. -| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_listavailableanalyzers[`db.index.fulltext.listAvailableAnalyzers`] -| List the available analyzers that the full-text indexes can be configured with. - -| Use full-text node index. -| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_querynodes[`db.index.fulltext.queryNodes`] -| Query the given full-text index. Returns the matching nodes and their Lucene query score, ordered by score. - -| Use full-text relationship index. -| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_queryrelationships[`db.index.fulltext.queryRelationships`] -| Query the given full-text index. Returns the matching relationships and their Lucene query score, ordered by score. - -| Drop full-text index. -| `+DROP INDEX ...+` -| Drop the specified index. -See the xref:indexes-for-search-performance.adoc#indexes-drop-indexes[`DROP INDEX`] command for more details. - - -| Eventually consistent indexes. -| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_awaiteventuallyconsistentindexrefresh[`db.index.fulltext.awaitEventuallyConsistentIndexRefresh`] -| Wait for the updates from recently committed transactions to be applied to any eventually-consistent full-text indexes. - -| Listing all full-text indexes. -| `SHOW FULLTEXT INDEXES` -| Lists all full-text indexes, see the xref::indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES`] command for details. - -|=== - - -[[indexes-fulltext-search-create-and-configure]] -== Create and configure full-text indexes - -Full-text indexes are created with the `CREATE FULLTEXT INDEX` command. -An index can be given a unique name when created (or get a generated one), which is used to reference the specific index when querying or dropping it. -A full-text index applies to a list of labels or a list of relationship types, for node and relationship indexes respectively, and then a list of property names. - -Creating a full-text index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `CREATE INDEX` privilege]. - -[NOTE] -==== -More details about the syntax descriptions can be found link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/syntax/#administration-syntax-reading[here]. -==== - -.Syntax for creating full-text indexes -[options="header", width="100%", cols="5a, 3"] -|=== -| Command | Description - -| [source, syntax, role=noplay, indent=0] ----- -CREATE FULLTEXT INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName["\|" ...]) -ON EACH "[" n.propertyName[, ...] "]" -[OPTIONS "{" option: value[, ...] "}"] ----- -| Create a full-text index on nodes. - -| [source, syntax, role=noplay, indent=0] ----- -CREATE FULLTEXT INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r:TYPE_NAME["\|" ...]"]"-() -ON EACH "[" r.propertyName[, ...] "]" -[OPTIONS "{" option: value[, ...] "}"] ----- -| Create a full-text index on relationships. - -|=== - -It is considered best practice to give the index a name when it is created. -This name is needed for both dropping and querying the index. -If the index is not explicitly named, it will get an auto-generated name. -The index name can also be given as a parameter, `CREATE FULLTEXT INDEX $name FOR ...`. - -[NOTE] -==== -The index name must be unique among all indexes and constraints. -==== - -Index provider and configuration settings can be specified using the `OPTIONS` clause. -Supported settings are `fulltext.analyzer`, for specifying what analyzer to use when indexing and querying. -Use the `db.index.fulltext.listAvailableAnalyzers` procedure to see what options are available. -To make this index eventually consistent, `fulltext.eventually_consistent` should be set to `true`. -This will ensure that updates from committing transactions are applied in a background thread. - -The command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to create the same index twice. -With `IF NOT EXISTS`, no error is thrown and nothing happens should an index with the same name, schema or both already exist. -It may still throw an error should a constraint with the same name exist. - -.+CREATE FULLTEXT INDEX+ -====== - -For instance, if we have a movie with a title. - -.Query -[source, cypher] ----- -CREATE (m:Movie {title: "The Matrix"}) RETURN m.title ----- - -.Result -[role="queryresult",options="header,footer",cols="1*+ | +0.13076457381248474+ -3+d|Rows: 1 - -|=== - -The same is true for full-text indexes on relationships. -Though a relationship can only have one type, a relationship full-text index can index multiple types, and all relationships will be included that match one of the relationship types, and at least one of the indexed properties. - -====== - - -The `CREATE FULLTEXT INDEX` command take an optional clause, called `options`. This have two parts, the `indexProvider` and `indexConfig`. -The provider can only have the default value, `'fulltext-1.0'`. -The `indexConfig` is a map from string to string and booleans, and can be used to set index-specific configuration settings. - -The `fulltext.analyzer` setting can be used to configure an index-specific analyzer. -The possible values for the `fulltext.analyzer` setting can be listed with the `db.index.fulltext.listAvailableAnalyzers` procedure. - -The `fulltext.eventually_consistent` setting, if set to `true`, will put the index in an _eventually consistent_ update mode. -This means that updates will be applied in a background thread "as soon as possible", instead of during transaction commit like other indexes. - - -.+CREATE FULLTEXT INDEX+ -====== - -This statement creates a named full-text index using a parameter. Creating and dropping indexes using parameters was introduced in Neo4j 5.16. - -.Parameters -[source,javascript, indent=0] ----- -{ - "name": "taggedByRelationshipIndex" -} ----- - -.Query -[source, cypher] ----- -CREATE FULLTEXT INDEX $name FOR ()-[r:TAGGED_AS]-() ON EACH [r.taggedByUser] -OPTIONS { - indexConfig: { - `fulltext.analyzer`: 'url_or_email', - `fulltext.eventually_consistent`: true - } -} ----- - -In this example, an eventually consistent relationship full-text index is created for the `TAGGED_AS` relationship type, and the `taggedByUser` property, and the index uses the `url_or_email` analyzer. -This could, for instance, be a system where people are assigning tags to documents, and where the index on the `taggedByUser` property will allow them to quickly find all of the documents they have tagged. -Had it not been for the relationship index, one would have had to add artificial connective nodes between the tags and the documents in the data model, just so these nodes could be indexed. - -.Result -[queryresult] ----- -Added 1 index. ----- - -====== - - -[[indexes-fulltext-search-query]] -== Query full-text indexes - -Full-text indexes will, in addition to any exact matches, also return _approximate_ matches to a given query. -Both the property values that are indexed, and the queries to the index, are processed through the analyzer such that the index can find that don't _exactly_ matches. -The `score` that is returned alongside each result entry, represents how well the index thinks that entry matches the given query. -The results are always returned in _descending score order_, where the best matching result entry is put first. - - -.Query full-text -====== - -To illustrate, in the example below, we search our movie database for `"Full Metal Jacket"`, and even though there is an exact match as the first result, we also get three other less interesting results: - -//// -[source, cypher, role=test-setup] ----- -CREATE (:Movie {title: "Full Metal Jacket"}), (:Movie {title: "The Jacket"}), (:Movie {title: "Yellow Jacket"}), (:Movie {title: "Full Moon High"}), (:Movie {title: "Metallica Through The Never", description: "The movie follows the young roadie Trip through his surreal adventure with the band."}); ----- -//// - -.Query -[source, cypher] ----- -CALL db.index.fulltext.queryNodes("titlesAndDescriptions", "Full Metal Jacket") YIELD node, score -RETURN node.title, score ----- - -.Result -[role="queryresult",options="header,footer",cols="2* Index configuration]. - - -[[indexes-types-and-limitations]] -== Indexes (types and limitations) - -A database index is a redundant copy of some of the data in the database for the purpose of making searches of related data more efficient. -This comes at the cost of additional storage space and slower writes, so deciding what to index and what not to index is an important and often non-trivial task. - -Once an index has been created, it will be managed and kept up to date by the DBMS. -Neo4j will automatically pick up and start using the index once it has been created and brought online. - -There are multiple index types available: - -* Range index. -* Lookup index. -* Text index. -* Point index. -* Full-text index. -* Vector index. - -See xref::indexes-for-full-text-search.adoc[Full-text search index] for more information about full-text indexes. -See xref::indexes-for-vector-search.adoc[Vector search index] for more information about vector indexes. -Lookup indexes contain nodes with one or more labels or relationship types, without regard for any properties. - -Cypher enables the creation of range indexes on one or more properties for all nodes or relationships with a given label or relationship type: - -* An index created on a single property for any given label or relationship type is called a _single-property index_. -* An index created on more than one property for any given label or relationship type is called a _composite index_. - -Differences in the usage patterns between composite and single-property indexes are described in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[]. - -Additionally, text and point indexes are a kind of single-property indexes, with the limitation that they only recognize properties with `STRING` and `POINT` values, respectively. -Nodes or relationships with the indexed label or relationship type where the indexed property is of another value type are not included in the index. - -The following is true for indexes: - -* Best practice is to give the index a name when it is created. -If the index is not explicitly named, it gets an auto-generated name. -* The index name must be unique among both indexes and constraints. -* Index creation is by default not idempotent, and an error will be thrown if you attempt to create the same index twice. -Using the keyword `IF NOT EXISTS` makes the command idempotent, and no error will be thrown if you attempt to create the same index twice. - -For a brief overview of the syntax, for all the index commands, see xref::indexes-for-search-performance.adoc#indexes-syntax[]. - - -[[indexes-create-indexes]] -== +CREATE INDEX+ - -Creating an index is done with the `+CREATE ... INDEX ...+` command. -If no index type is specified in the create command a range index will be created. - -Best practice is to give the index a name when it is created. -If the index is not explicitly named, it gets an auto-generated name. - -[IMPORTANT] -==== -The index name must be unique among both indexes and constraints. -==== - -The `+CREATE INDEX+` command is optionally idempotent. This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. -With `IF NOT EXISTS`, no error is thrown and nothing happens should an index with the same name or same schema and index type already exist. -It may still throw an error if conflicting constraints exist, such as constraints with the same name or schema and backing index type. - -Creating an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `CREATE INDEX` privilege]. - -[NOTE] -==== -The new index is not immediately available, but is created in the background. -==== - -[[indexes-create-range-index]] -=== Creating a range index - -Creating a range index can be done with the `CREATE INDEX` command. -Note that the index name must be unique. - -[source, syntax, role="noheader"] ----- -CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName) -ON (n.propertyName_1[, - n.propertyName_2, - ... - n.propertyName_n]) ----- -[source, syntax, role="noheader"] ----- -CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r:TYPE_NAME"]"-() -ON (r.propertyName_1[, - r.propertyName_2, - ... - r.propertyName_n]) ----- - -Range indexes have only one index provider available, `range-1.0`, and no supported index configuration. - -[discrete] -==== Examples - -* xref::indexes-for-search-performance.adoc#indexes-create-a-single-property-range-index-for-nodes[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-single-property-range-index-for-relationships[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-composite-range-index-for-nodes[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-composite-range-index-for-relationships[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-range-index-by-param[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-range-index-only-if-it-does-not-already-exist[] - -[discrete] -[[indexes-create-a-single-property-range-index-for-nodes]] -===== Create a single-property range index for nodes - -The following statement will create a named range index on all nodes labeled with `Person` and which have the `surname` property. - -.Creating a node range index on a single property -[source, cypher] ----- -CREATE INDEX node_range_index_name FOR (n:Person) ON (n.surname) ----- - -[discrete] -[[indexes-create-a-single-property-range-index-for-relationships]] -===== Create a single-property range index for relationships - -The following statement will create a named range index on all relationships with relationship type `KNOWS` and property `since`. - -.Creating a relationship range index on a single property -[source, cypher] ----- -CREATE INDEX rel_range_index_name FOR ()-[r:KNOWS]-() ON (r.since) ----- - -[discrete] -[[indexes-create-a-composite-range-index-for-nodes]] -===== Create a composite range index for nodes - -A range index on multiple properties is also called a composite index. -For node range indexes, only nodes with the specified label and that contain all the specified properties will be added to the index. - -The following statement will create a named composite range index on all nodes labeled with `Person` and which have both an `age` and `country` property. - -.Creating a composite node range index on multiple properties -[source, cypher] ----- -CREATE INDEX composite_range_node_index_name FOR (n:Person) ON (n.age, n.country) ----- - -[discrete] -[[indexes-create-a-composite-range-index-for-relationships]] -===== Create a composite range index for relationships - -A range index on multiple properties is also called a composite index. -For relationship range indexes, only relationships with the specified type and that contain all the specified properties will be added to the index. - -The following statement will create a named composite range index on all relationships labeled with `PURCHASED` and which have both a `date` and `amount` property. - -.Creating a composite relationship range index on multiple properties -[source, cypher] ----- -CREATE INDEX composite_range_rel_index_name FOR ()-[r:PURCHASED]-() ON (r.date, r.amount) ----- - -[discrete] -[[indexes-create-a-range-index-by-param]] -===== Create a range index using a parameter - -_This feature was introduced in Neo4j 5.16._ - -The following statement will create a named range index on all nodes labeled with `Person` and which have the `firstname` property using a parameter for the index name. - -.Parameters -[source,javascript, indent=0] ----- -{ - "name": "range_index_param" -} ----- - -.Creating a node range index on a single property -[source, cypher] ----- -CREATE INDEX $name FOR (n:Person) ON (n.firstname) ----- - -[discrete] -[[indexes-create-a-range-index-only-if-it-does-not-already-exist]] -===== Create a range index only if it does not already exist - -If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. - -.Creating a range index with `IF NOT EXISTS` -[source, cypher] ----- -CREATE INDEX node_range_index_name IF NOT EXISTS -FOR (n:Person) ON (n.surname) ----- - -The index will not be created if there already exists an index with the same schema and type, same name or both. - - -[[indexes-create-text-index]] -=== Creating a text index - -Creating a text index can be done with the `CREATE TEXT INDEX` command. -Note that the index name must be unique. - -[source, syntax, role="noheader"] ----- -CREATE TEXT INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName) -ON (n.propertyName) -[OPTIONS "{" option: value[, ...] "}"] ----- -[source, syntax, role="noheader"] ----- -CREATE TEXT INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r:TYPE_NAME"]"-() -ON (r.propertyName) -[OPTIONS "{" option: value[, ...] "}"] ----- - -As of Neo4j 5.1, text indexes have two index providers available, `text-2.0` (default) and `text-1.0` (deprecated), and no supported index configuration. - - -[NOTE] -==== -Text indexes only recognize `STRING` values and do not support multiple properties. -==== - -[discrete] -==== Examples - -* xref::indexes-for-search-performance.adoc#indexes-create-a-node-text-index[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-relationship-text-index[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-text-index-by-param[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-text-index-only-if-it-does-not-already-exist[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-text-index-specifying-the-index-provider[] - -[discrete] -[[indexes-create-a-node-text-index]] -===== Create a node text index - -The following statement will create a named text index on all nodes labeled with `Person` and which have the `nickname` `STRING` property. - -.Creating a node text index on a single property -[source, cypher] ----- -CREATE TEXT INDEX node_text_index_nickname FOR (n:Person) ON (n.nickname) ----- - -[discrete] -[[indexes-create-a-relationship-text-index]] -===== Create a relationship text index - -The following statement will create a named text index on all relationships with relationship type `KNOWS` and `STRING` property `interest`. - -.Creating a relationship text index on a single property -[source, cypher] ----- -CREATE TEXT INDEX rel_text_index_name FOR ()-[r:KNOWS]-() ON (r.interest) ----- - -[discrete] -[[indexes-create-a-text-index-by-param]] -===== Create a text index using a parameter - -_This feature was introduced in Neo4j 5.16._ - -The following statement will create a named text index on all nodes labeled with `Person` and which have the `favoriteColor` `STRING` property using a parameter for the index name. - -.Parameters -[source,javascript, indent=0] ----- -{ - "name": "text_index_param" -} ----- - -.Creating a node text index on a single property -[source, cypher] ----- -CREATE TEXT INDEX $name FOR (n:Person) ON (n.favoriteColor) ----- - -[discrete] -[[indexes-create-a-text-index-only-if-it-does-not-already-exist]] -===== Create a text index only if it does not already exist - -If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. - -The following statement will attempt to create a named text index on all nodes labeled with `Person` and which have the `nickname` `STRING` property. - -.Creating a text index with `IF NOT EXISTS` -[source, cypher] ----- -CREATE TEXT INDEX node_index_name IF NOT EXISTS FOR (n:Person) ON (n.nickname) ----- - -Note that the index will not be created if there already exists an index with the same schema and type, same name or both. - -[discrete] -[[indexes-create-a-text-index-specifying-the-index-provider]] -===== Create a text index specifying the index provider - -To create a text index with a specific index provider, the `OPTIONS` clause is used. -The valid values for the index provider are `text-2.0` and `text-1.0` (deprecated). -The default provider is `text-2.0`. - -.Creating a text index with index provider -[source, cypher] ----- -CREATE TEXT INDEX text_index_with_indexprovider FOR ()-[r:TYPE]-() ON (r.prop1) -OPTIONS {indexProvider: 'text-2.0'} ----- - -There is no supported index configuration for text indexes. - - -[[indexes-create-point-index]] -=== Creating a point index - -Creating a point index can be done with the `CREATE POINT INDEX` command. -Note that the index name must be unique. - -[source, syntax, role="noheader"] ----- -CREATE POINT INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName) -ON (n.propertyName) -[OPTIONS "{" option: value[, ...] "}"] ----- -[source, syntax, role="noheader"] ----- -CREATE POINT INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r:TYPE_NAME"]"-() -ON (r.propertyName) -[OPTIONS "{" option: value[, ...] "}"] ----- - -Point indexes have supported index configuration, see the last examples, but only one index provider available, `point-1.0`. - -[NOTE] -==== -Note that point indexes only recognize `POINT` values and do not support multiple properties. -==== - -[discrete] -==== Examples - -* xref::indexes-for-search-performance.adoc#indexes-create-a-node-point-index[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-relationship-point-index[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-point-index-by-param[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-point-index-only-if-it-does-not-already-exist[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-point-index-specifying-the-index-configuration[] - -[discrete] -[[indexes-create-a-node-point-index]] -===== Create a node point index - -The following statement will create a named point index on all nodes labeled with `Person` and which have the `sublocation` `POINT` property. - -.Creating a node point index on a single property -[source, cypher] ----- -CREATE POINT INDEX node_point_index_name FOR (n:Person) ON (n.sublocation) ----- - -[discrete] -[[indexes-create-a-relationship-point-index]] -===== Create a relationship point index - -The following statement will create a named point index on all relationships with relationship type `STREET` and `POINT` property `intersection`. - -.Creating a relationship point index on a single property -[source, cypher] ----- -CREATE POINT INDEX rel_point_index_name FOR ()-[r:STREET]-() ON (r.intersection) ----- - -[discrete] -[[indexes-create-a-point-index-by-param]] -===== Create a point index using a parameter - -_This feature was introduced in Neo4j 5.16._ - -The following statement will create a named point index on all relationships with relationship type `STREET` and `POINT` property `coordinate` using a parameter for the index name. - -.Parameters -[source,javascript, indent=0] ----- -{ - "name": "point_index_param" -} ----- - -.Creating a relationship point index on a single property -[source, cypher] ----- -CREATE POINT INDEX $name FOR ()-[r:STREET]-() ON (r.coordinate) ----- - -[discrete] -[[indexes-create-a-point-index-only-if-it-does-not-already-exist]] -===== Create a point index only if it does not already exist - -If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. - -.Creating a point index with `IF NOT EXISTS` -[source, cypher] ----- -CREATE POINT INDEX node_point_index IF NOT EXISTS -FOR (n:Person) ON (n.sublocation) ----- - -Note that the index will not be created if there already exists an index with the same schema and type, same name or both. - -[discrete] -[[indexes-create-a-point-index-specifying-the-index-configuration]] -===== Create a point index specifying the index configuration - -To create a point index with a specific index configuration, the `OPTIONS` clause is used. - -The valid configuration settings are: - -* `spatial.cartesian.min` -* `spatial.cartesian.max` -* `spatial.cartesian-3d.min` -* `spatial.cartesian-3d.max` -* `spatial.wgs-84.min` -* `spatial.wgs-84.max` -* `spatial.wgs-84-3d.min` -* `spatial.wgs-84-3d.max` - -Non-specified settings have their respective default values. - -The following statement will create a point index specifying the `spatial.cartesian.min` and `spatial.cartesian.max` settings. - -.Creating a point index with index configuration -[source, cypher] ----- -CREATE POINT INDEX point_index_with_config -FOR (n:Label) ON (n.prop2) -OPTIONS { - indexConfig: { - `spatial.cartesian.min`: [-100.0, -100.0], - `spatial.cartesian.max`: [100.0, 100.0] - } -} ----- - -Specifying the index configuration can be combined with specifying index provider. -Though only one valid value exists for the index provider, `point-1.0`, which is the default value. - - -[[indexes-create-token-index]] -=== Creating a token lookup index - -Creating a token lookup index (node label or relationship type lookup index) can be done with the `CREATE LOOKUP INDEX` command. -Note that the index name must be unique. - -[source, syntax, role="noheader"] ----- -CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] -FOR (n) -ON EACH labels(n) ----- -[source, syntax, role="noheader"] ----- -CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r"]"-() -ON [EACH] type(r) ----- - -Token lookup indexes have only one index provider available, `token-lookup-1.0`, and no supported index configuration. - -[discrete] -==== Examples - -* xref::indexes-for-search-performance.adoc#indexes-create-a-node-label-lookup-index[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-relationship-type-lookup-index[] -* xref::indexes-for-search-performance.adoc#indexes-create-a-lookup-index-only-if-it-does-not-already-exist[] - -[discrete] -[[indexes-create-a-node-label-lookup-index]] -===== Create a node label lookup index - -The following statement will create a named node label lookup index on all nodes with one or more labels: - -// Lookup indexes exist by default, recreating them would raise an error -.Creating a node label lookup index -[source, cypher, role=test-skip] ----- -CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) ----- - -[NOTE] -==== -Only one node label lookup index can exist at a time. -==== - -[discrete] -[[indexes-create-a-relationship-type-lookup-index]] -===== Create a relationship type lookup index - -The following statement will create a named relationship type lookup index on all relationships with any relationship type. - -// Lookup indexes exist by default, recreating them would raise an error -.Creating a relationship type lookup index -[source, cypher, role=test-skip] ----- -CREATE LOOKUP INDEX rel_type_lookup_index FOR ()-[r]-() ON EACH type(r) ----- - -[NOTE] -==== -Only one relationship type lookup index can exist at a time. -==== - -[discrete] -[[indexes-create-a-lookup-index-only-if-it-does-not-already-exist]] -===== Create a token lookup index only if it does not already exist - -If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. - -.Parameters -[source,javascript, indent=0] ----- -{ - "name": "node_label_lookup" -} ----- - -.Creating a node label lookup index with `IF NOT EXISTS` -[source, cypher] ----- -CREATE LOOKUP INDEX $name IF NOT EXISTS FOR (n) ON EACH labels(n) ----- - -The index will not be created if there already exists an index with the same schema and type, same name or both. - - -[[indexes-create-conflicting-index]] -=== Creating an index when a conflicting index or constraint exists - -* xref::indexes-for-search-performance.adoc#indexes-failure-to-create-an-already-existing-index[] -* xref::indexes-for-search-performance.adoc#indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-index[] -* xref::indexes-for-search-performance.adoc#indexes-failure-to-create-an-index-when-a-constraint-already-exists[] -* xref::indexes-for-search-performance.adoc#indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-constraint[] - -[discrete] -[[indexes-failure-to-create-an-already-existing-index]] -==== Failure to create an already existing index - -Create an index on the property `title` on nodes with the `Book` label, when that index already exists. - -//// -[source, cypher, role=test-setup] ----- -CREATE INDEX example_index FOR (n:Book) ON (n.title) ----- -//// - -.Creating a duplicated index -[source, cypher, role=test-fail] ----- -CREATE INDEX bookTitleIndex FOR (book:Book) ON (book.title) ----- - -In this case the index can not be created because it already exists. - -.Error message -[source, error] ----- -There already exists an index (:Book {title}). ----- - -[discrete] -[[indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-index]] -==== Failure to create an index with the same name as an already existing index - -Create a named index on the property `numberOfPages` on nodes with the `Book` label, when an index with the given name already exists. -The index type of the existing index does not matter. - -//// -[source, cypher, role=test-setup] ----- -CREATE TEXT INDEX indexOnBooks FOR (b:Label1) ON (b.prop1) ----- -//// - -.Creating an index with a duplicated name -[source, cypher, role=test-fail] ----- -CREATE INDEX indexOnBooks FOR (book:Book) ON (book.numberOfPages) ----- - -In this case the index can't be created because there already exists an index with the given name. - -.Error message -[source, error] ----- -There already exists an index called 'indexOnBooks'. ----- - -[discrete] -[[indexes-failure-to-create-an-index-when-a-constraint-already-exists]] -==== Failure to create an index when a constraint already exists - -Create an index on the property `isbn` on nodes with the `Book` label, when an index-backed constraint already exists on that schema. -This is only relevant for range indexes. - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT uniqueBookIsbn FOR (book:Book) REQUIRE (book.isbn) IS UNIQUE ----- -//// - -.Creating a range index on same schema as existing index-backed constraint -[source, cypher, role=test-fail] ----- -CREATE INDEX bookIsbnIndex FOR (book:Book) ON (book.isbn) ----- - -In this case the index can not be created because an index-backed constraint already exists on that label and property combination. - -.Error message -[source, error] ----- -There is a uniqueness constraint on (:Book {isbn}), so an index is already created that matches this. ----- - -[discrete] -[[indexes-failure-to-create-an-index-with-the-same-name-as-an-already-existing-constraint]] -==== Failure to create an index with the same name as an already existing constraint - -Create a named index on the property `numberOfPages` on nodes with the `Book` label, when a constraint with the given name already exists. - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT bookRecommendations FOR (book:Book) REQUIRE (book.recommend) IS NOT NULL ----- -//// - -.Creating an index with same name as an existing constraint -[source, cypher, role=test-fail] ----- -CREATE INDEX bookRecommendations FOR (book:Book) ON (book.recommendations) ----- - -In this case the index can not be created because there already exists a constraint with the given name. - -.Error message -[source, error] ----- -There already exists a constraint called 'bookRecommendations'. ----- - - -[[indexes-list-indexes]] -== +SHOW INDEXES+ - -Listing indexes can be done with `SHOW INDEXES`. - -[source, syntax, role="noheader"] ----- -SHOW [ALL \| FULLTEXT \| LOOKUP \| POINT \| RANGE \| TEXT \| VECTOR] INDEX[ES] - [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] - [WHERE expression] - [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ----- - -This command will produce a table with the following columns: - -.List indexes output -[options="header", cols="4,6,2"] -|=== -| Column | Description | Type - -| `id` -| The id of the index. label:default-output[] -| `INTEGER` - -| `name` -| Name of the index (explicitly set by the user or automatically assigned). label:default-output[] -| `STRING` - -| `state` -| Current state of the index. label:default-output[] -| `STRING` - -| `populationPercent` -| % of index population. label:default-output[] -| `FLOAT` - -| `type` -| The IndexType of this index (`FULLTEXT`, `LOOKUP`, `POINT`, `RANGE`, `TEXT`, or `VECTOR`). label:default-output[] -| `STRING` - -| `entityType` -| Type of entities this index represents (nodes or relationship). label:default-output[] -| `STRING` - -| `labelsOrTypes` -| The labels or relationship types of this index. label:default-output[] -| `LIST` - -| `properties` -| The properties of this index. label:default-output[] -| `LIST` - -| `indexProvider` -| The index provider for this index. label:default-output[] -| `STRING` - -// New in 5.0 -| `owningConstraint` -| The name of the constraint the index is associated with or `null` if the index is not associated with any constraint. label:default-output[] -| `STRING` - -| `lastRead` -| The last time the index was used for reading. -Returns `null` if the index has not been read since `trackedSince`, or if the statistics are not tracked. -label:default-output[] -label:new[Introduced in 5.8] -| `ZONED DATETIME` - -| `readCount` -| The number of read queries that have been issued to this index since `trackedSince`, or `null` if the statistics are not tracked. label:default-output[] -label:new[Introduced in 5.8] -| `INTEGER` - -| `trackedSince` -| The time when usage statistics tracking started for this index, or `null` if the statistics are not tracked. -label:new[Introduced in 5.8] -| `ZONED DATETIME` - -| `options` -| The options passed to `CREATE` command. -| `MAP` - -| `failureMessage` -| The failure description of a failed index. -| `STRING` - -| `createStatement` -| Statement used to create the index. -| `STRING` - -|=== - -[NOTE] -==== -The command `SHOW INDEXES` returns only the default output. -For a full output use the optional `YIELD` command. -Full output: `+SHOW INDEXES YIELD *+`. -==== - -Listing indexes also allows for `WHERE` and `YIELD` clauses to filter the returned rows and columns. - -Listing indexes require link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `SHOW INDEX` privilege]. - -[discrete] -=== Examples - -* xref::indexes-for-search-performance.adoc#indexes-listing-all-indexes[] -* xref::indexes-for-search-performance.adoc#indexes-listing-indexes-with-filtering[] - - -[discrete] -[[indexes-listing-all-indexes]] -==== Listing all indexes - -To list all indexes with the default output columns, the `SHOW INDEXES` command can be used. -If all columns are required, use `SHOW INDEXES YIELD *`. - -.Showing all indexes -[source, cypher, role=test-result-skip] ----- -SHOW INDEXES ----- - -// SHOW INDEXES default outputs -// 4.4: id, name, state, populationPercent, uniqueness, type, entityType, labelsOrTypes, properties, indexProvider -// 5.0: id, name, state, populationPercent, type, entityType, labelsOrTypes, properties, indexProvider, owningConstraint -// 5.8: id, name, state, populationPercent, type, entityType, labelsOrTypes, properties, indexProvider, owningConstraint, lastRead, readCount - -.Result -[queryresult] ----- -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | NULL | 0 | -| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | 2023-03-13T11:41:44.537Z | 1 | -| 16 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | 2023-04-10T15:41:44.537Z | 2 | -| 17 | "indexOnBooks" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Label1"] | ["prop1"] | "text-2.0" | NULL | NULL | 0 | -| 14 | "node_label_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "NODE" | NULL | NULL | "token-lookup-1.0" | NULL | 2023-04-13T08:11:15.537Z | 10 | -| 10 | "node_point_index_name" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Person"] | ["sublocation"] | "point-1.0" | NULL | 2023-04-05T16:21:44.692Z | 1 | -| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | 2022-12-30T02:01:44.537Z | 6 | -| 6 | "node_text_index_nickname" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["nickname"] | "text-2.0" | NULL | 2023-04-13T11:41:44.537Z | 2 | -| 12 | "point_index_param" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["coordinate"] | "point-1.0" | NULL | NULL | 0 | -| 13 | "point_index_with_config" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop2"] | "point-1.0" | NULL | NULL | 0 | -| 5 | "range_index_param" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["firstname"] | "range-1.0" | NULL | 2023-12-13T08:23:53.338Z | 2 | -| 11 | "rel_point_index_name" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["intersection"] | "point-1.0" | NULL | 2023-03-03T13:37:42.537Z | 2 | -| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | 2023-04-12T10:41:44.692Z | 5 | -| 7 | "rel_text_index_name" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["KNOWS"] | ["interest"] | "text-2.0" | NULL | 2023-04-01T10:40:44.537Z | 3 | -| 15 | "rel_type_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "RELATIONSHIP" | NULL | NULL | "token-lookup-1.0" | NULL | 2023-04-12T21:41:44.537Z | 7 | -| 8 | "text_index_param" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["favoriteColor"] | "text-2.0" | NULL | NULL | 0 | -| 9 | "text_index_with_indexprovider" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "text-2.0" | NULL | NULL | 0 | -| 18 | "uniqueBookIsbn" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["isbn"] | "range-1.0" | "uniqueBookIsbn" | 2023-04-13T11:41:44.692Z | 6 | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -18 rows ----- - -One of the output columns from `SHOW INDEXES` is the name of the index. -This can be used to drop the index with the xref::indexes-for-search-performance.adoc#indexes-drop-an-index[`DROP INDEX` command]. - - -[discrete] -[[indexes-listing-indexes-with-filtering]] -==== Listing indexes with filtering - -One way of filtering the output from `SHOW INDEXES` by index type is the use of type keywords, listed in the syntax description. - -For example, to show only range indexes, use `SHOW RANGE INDEXES`. - -Another more flexible way of filtering the output is to use the `WHERE` clause. -An example is to only show indexes not belonging to constraints. - -To show only range indexes that does not belong to a constraint we can combine the filtering versions. - -.Showing range indexes -[source, cypher, role=test-result-skip] ----- -SHOW RANGE INDEXES WHERE owningConstraint IS NULL ----- - -.Result -[queryresult] ----- -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | NULL | 0 | -| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | 2023-03-13T11:41:44.537Z | 1 | -| 16 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | 2023-04-10T15:41:44.537Z | 2 | -| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | 2022-12-30T02:01:44.537Z | 6 | -| 5 | "range_index_param" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["firstname"] | "range-1.0" | NULL | 2023-12-13T08:23:53.338Z | 2 | -| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | 2023-04-12T10:41:44.692Z | 5 | -+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -6 rows ----- - -This will only return the default output columns. - -To get all columns, use: - -[source, syntax, role="noheader"] ----- -SHOW RANGE INDEXES YIELD * WHERE owningConstraint IS NULL ----- - - -[[indexes-drop-indexes]] -== +DROP INDEX+ - -An index can be dropped (removed) using the name with the `DROP INDEX index_name` command. -This command can drop indexes of any type, except those backing constraints. -The name of the index can be found using the xref::indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES` command], given in the output column `name`. - -[source, syntax, role="noheader"] ----- -DROP INDEX index_name [IF EXISTS] ----- - -The `DROP INDEX` command is optionally idempotent. -This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. -With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. - -Dropping an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. - -[discrete] -=== Examples - -* xref::indexes-for-search-performance.adoc#indexes-drop-an-index[] -* xref::indexes-for-search-performance.adoc#indexes-drop-an-index-by-param[] -* xref::indexes-for-search-performance.adoc#indexes-drop-a-non-existing-index[] - - -[discrete] -[[indexes-drop-an-index]] -==== Drop an index - -The following statement will attempt to drop the index named `example_index`. - -.Dropping an index -[source, cypher] ----- -DROP INDEX example_index ----- - -If an index with that name exists it is removed, if not the command fails. - - -[discrete] -[[indexes-drop-an-index-by-param]] -==== Drop an index using a parameter - -_This feature was introduced in Neo4j 5.16._ - -The following statement will attempt to drop the index named `node_range_index_name` using a parameter for the index name. - -.Parameters -[source,javascript, indent=0] ----- -{ - "name": "range_index_param" -} ----- - -.Dropping an index -[source, cypher] ----- -DROP INDEX $name ----- - -If an index with that name exists it is removed, if not the command fails. - - -[discrete] -[[indexes-drop-a-non-existing-index]] -==== Drop a non-existing index - -If it is uncertain if an index exists and you want to drop it if it does but not get an error should it not, use `IF EXISTS`. - -The following statement will attempt to drop the index named `missing_index_name`. - -.Dropping an index with `IF EXISTS` -[source, cypher] ----- -DROP INDEX missing_index_name IF EXISTS ----- - -If an index with that name exists it is removed, if not the command does nothing. - - -[[indexes-syntax]] -== Syntax - -[IMPORTANT] -==== -The index name must be unique among both indexes and constraints. -==== - -[NOTE] -==== -Best practice is to give the index a name when it is created. -If the index is not explicitly named, it gets an auto-generated name. -==== - -[NOTE] -==== -The `+CREATE ... INDEX ...+` command is optionally idempotent. This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. -With `IF NOT EXISTS`, no error is thrown and nothing happens should an index with the same name or same schema and index type already exist. -It may still throw an error if conflicting constraints exist, such as constraints with the same name or schema and backing index type. -==== - -.+Create a range index on nodes+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName) -ON (n.propertyName_1[, - n.propertyName_2, - ... - n.propertyName_n]) -[OPTIONS "{" option: value[, ...] "}"] ----- - -| Description -| -Create a range index on nodes, either on a single property or composite. - -Index provider can be specified using the `OPTIONS` clause. -There is only one available index provider for this index. - -|=== - - -.+Create a range index on relationships+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r:TYPE_NAME"]"-() -ON (r.propertyName_1[, - r.propertyName_2, - ... - r.propertyName_n]) -[OPTIONS "{" option: value[, ...] "}"] ----- - -| Description -| -Create a range index on relationships, either on a single property or composite. - -Index provider can be specified using the `OPTIONS` clause. -There is only one available index provider for this index. - -|=== - - -.+Create a text index on nodes+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -CREATE TEXT INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName) -ON (n.propertyName) -[OPTIONS "{" option: value[, ...] "}"] ----- - -| Description -| -Create a text index on nodes where the property has a `STRING` value. - -Index provider can be specified using the `OPTIONS` clause. - -|=== - - -.+Create a text index on relationships+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -CREATE TEXT INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r:TYPE_NAME"]"-() -ON (r.propertyName) -[OPTIONS "{" option: value[, ...] "}"] ----- - -| Description -| -Create a text index on relationships where the property has a `STRING` value. - -Index provider can be specified using the `OPTIONS` clause. - -|=== - - -.+Create a point index on nodes+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -CREATE POINT INDEX [index_name] [IF NOT EXISTS] -FOR (n:LabelName) -ON (n.propertyName) -[OPTIONS "{" option: value[, ...] "}"] ----- - -| Description -| -Create a point index on nodes where the property has a `POINT` value. - -Index provider and configuration can be specified using the `OPTIONS` clause. -There is only one available index provider for this index. - -|=== - - -.+Create a point index on relationships+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -CREATE POINT INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r:TYPE_NAME"]"-() -ON (r.propertyName) -[OPTIONS "{" option: value[, ...] "}"] ----- - -| Description -| -Create a point index on relationships where the property has a `POINT` value. - -Index provider and configuration can be specified using the `OPTIONS` clause. -There is only one available index provider for this index. - -|=== - - -.+Create a node label lookup index+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] -FOR (n) -ON EACH labels(n) -[OPTIONS "{" option: value[, ...] "}"] ----- - -| Description -| -Create a node label lookup index. - -Index provider can be specified using the `OPTIONS` clause. -There is only one available index provider for this index. - -|=== - - -.+Create a relationship type lookup index+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -CREATE LOOKUP INDEX [index_name] [IF NOT EXISTS] -FOR ()-"["r"]"-() -ON [EACH] type(r) -[OPTIONS "{" option: value[, ...] "}"] ----- - -| Description -| -Create a relationship type lookup index. - -Index provider can be specified using the `OPTIONS` clause. -There is only one available index provider for this index. - -|=== - - -.+Drop an index+ -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -DROP INDEX index_name [IF EXISTS] ----- - -| Description -| Drop an index of any index type. - -| Note -| -The command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. -With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. - -|=== - - -.List indexes -[options="noheader", width="100%", cols="2, 8a"] -|=== - -| Syntax -| -[source, syntax, role="noheader"] ----- -SHOW [ALL \| FULLTEXT \| LOOKUP \| POINT \| RANGE \| TEXT \| VECTOR] INDEX[ES] - [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] - [WHERE expression] - [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ----- - -| Description -| List indexes in the database, either all or filtered on index type. - -| Note -| When using the `RETURN` clause, the `YIELD` clause is mandatory and must not be omitted. - -|=== - -[NOTE] -==== -More details about the syntax descriptions can be found link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/syntax/#administration-syntax-reading[here]. -==== - -Creating an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `CREATE INDEX` privilege], -while dropping an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege] and listing indexes require link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `SHOW INDEX` privilege]. - -xref:planning-and-tuning/query-tuning/using.adoc[Planner hints and the USING keyword] describes how to make the Cypher planner use specific indexes (especially in cases where the planner would not necessarily have used them). - - -[[indexes-single-vs-composite-index]] -== Composite index limitations - -Like single-property range indexes, composite range indexes support all predicates: - -* equality check: `n.prop = value` -* list membership check: `n.prop IN list` -* existence check: `n.prop IS NOT NULL` -* range search: `n.prop > value` -* prefix search: `STARTS WITH` - -[NOTE] -==== -For details about each operator, see xref::syntax/operators.adoc[Operators]. -==== - -However, predicates might be planned as existence check and a filter. -For most predicates, this can be avoided by following these restrictions: - -* If there is any `equality check` and `list membership check` predicates, -they need to be for the first properties defined by the index. -* There can be up to one `range search` or `prefix search` predicate. -* There can be any number of `existence check` predicates. -* Any predicate after a `range search`, `prefix search` or `existence check` predicate has to be an `existence check` predicate. - -[NOTE] -==== -The `suffix search` (`ENDS WITH`) and `substring search` (`CONTAINS`) predicates can utilize the index as well. -However, they are always planned as an existence check and a filter and any predicates following after will therefore also be planned as such. -==== - -For example, an index on nodes with `:Label(prop1,prop2,prop3,prop4,prop5,prop6)` and predicates: - -[source, cypher, role=test-skip] ----- -WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 < 'e' AND n.prop5 = true AND n.prop6 IS NOT NULL ----- - -will be planned as: - -[source, cypher, role=test-skip] ----- -WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 IS NOT NULL AND n.prop5 IS NOT NULL AND n.prop6 IS NOT NULL ----- - -with filters on `n.prop4 < 'e'` and `n.prop5 = true`, since `n.prop3` has a `range search` predicate. - -And an index on nodes with `:Label(prop1,prop2)` with predicates: - -[source, cypher, role=test-skip] ----- -WHERE n.prop1 ENDS WITH 'x' AND n.prop2 = false ----- - -will be planned as: - -[source, cypher, role=test-skip] ----- -WHERE n.prop1 IS NOT NULL AND n.prop2 IS NOT NULL ----- - -with filters on `n.prop1 ENDS WITH 'x'` and `n.prop2 = false`, since `n.prop1` has a `suffix search` predicate. - -Composite indexes require predicates on all properties indexed. -If there are predicates on only a subset of the indexed properties, it will not be possible to use the composite index. -To get this kind of fallback behavior, it is necessary to create additional indexes on the relevant sub-set of properties or on single properties. - diff --git a/modules/ROOT/pages/indexes/index.adoc b/modules/ROOT/pages/indexes/index.adoc new file mode 100644 index 000000000..36264dc26 --- /dev/null +++ b/modules/ROOT/pages/indexes/index.adoc @@ -0,0 +1,15 @@ +:description: Information about using indexes in Neo4j. += Indexes + +An index is a copy of specified primary data in a Neo4j database, such as nodes, relationships, or properties. +The data stored in the index provides an access path to the data in the primary storage and allows users to evaluate query filters more efficiently (and, in some cases, semantically interpret query filters). +In short, much like indexes in a book, their function in a Neo4j graph database is to make data retrieval more efficient. + +Once an index has been created, it will be automatically populated and updated by the DBMS. + +Neo4j supports two categories of indexes: + +- xref:indexes/search-performance-indexes/overview.adoc[Search-performance indexes], for speeding up data retrieval based on _exact_ matches. +This category includes range, text, point, and token lookup indexes. +- xref:indexes/semantic-indexes/overview.adoc[Semantic indexes], for _approximate_ matches and to compute similarity scores between a query string and the matching data. +This category includes full-text and vector indexes. diff --git a/modules/ROOT/pages/planning-and-tuning/query-tuning/using.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/index-hints.adoc similarity index 99% rename from modules/ROOT/pages/planning-and-tuning/query-tuning/using.adoc rename to modules/ROOT/pages/indexes/search-performance-indexes/index-hints.adoc index c1a798d13..4944a7d77 100644 --- a/modules/ROOT/pages/planning-and-tuning/query-tuning/using.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/index-hints.adoc @@ -1,7 +1,7 @@ :description: A planner hint is used to influence the decisions of the planner when building an execution plan for a query. [[query-using]] -= Planner hints and the USING keyword += Index hints for the Cypher planner A planner hint is used to influence the decisions of the planner when building an execution plan for a query. Planner hints are specified in a query with the `USING` keyword. @@ -23,9 +23,9 @@ This is called giving a planner hint. There are three types of planner hints: -* xref:planning-and-tuning/query-tuning/using.adoc#query-using-index-hint[Index hints]. -* xref:planning-and-tuning/query-tuning/using.adoc#query-using-scan-hint[Scan hints]. -* xref:planning-and-tuning/query-tuning/using.adoc#query-using-join-hint[Join hints]. +* xref:indexes/search-performance-indexes/index-hints.adoc#query-using-index-hint[Index hints]. +* xref:indexes/search-performance-indexes/index-hints.adoc#query-using-scan-hint[Scan hints]. +* xref:indexes/search-performance-indexes/index-hints.adoc#query-using-join-hint[Join hints]. //// [source, cypher, role=test-setup] @@ -142,7 +142,7 @@ When no index type is specified, the hint can be fulfilled by any index types. ==== Using a hint must never change the result of a query. Therefore, a hint with a specified index type is only fulfillable when the planner knows that using an index of the specified type does not change the results. -Please refer to xref::planning-and-tuning/query-tuning/indexes.adoc[The use of indexes] for more details. +Please refer to xref:indexes/search-performance-indexes/using-indexes.adoc[The use of indexes] for more details. ==== It is possible to supply several index hints, but keep in mind that several starting points diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc new file mode 100644 index 000000000..acb4fcb33 --- /dev/null +++ b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc @@ -0,0 +1,1099 @@ +:description: This page explains how to manage indexes used for search performance. += Create, show, and delete indexes + +This page describes how to create, list, and delete search-performance indexes. +The following index types are included in this category: + +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-range-index[Range indexes] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-text-index[Text indexes] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-point-index[Point indexes] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-token-index[Token lookup indexes] + +For information about how search-performance indexes are used in Cypher queries, see xref:indexes/search-performance-indexes/using-indexes.adoc[Using search-performance indexes]. + +[[create-indexes]] +== +CREATE INDEX+ + +Creating an index is done with the `CREATE ... INDEX ...` command. +If no index type is specified in the create command a xref:indexes/search-performance-indexes/managing-indexes.adoc#create-range-index[range index] will be created. + +Best practice is to give the index a name when it is created. +If the index is not explicitly named, it gets an auto-generated name. + +[IMPORTANT] +==== +The index name must be unique among both indexes and constraints. +==== + +The `CREATE INDEX` command is optionally idempotent. +This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. +If `IF NOT EXISTS` is appended to the command, no error is thrown and nothing happens should an index with the same name or same schema and index type already exist. +It may still throw an error if conflicting constraints exist, such as constraints with the same name or schema and backing index type. + +Index providers and configuration settings can be specified using the `OPTIONS` clause.footnote:[Index providers are essentially different implementations of the same index type. +Different providers are only available for xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-text-index-specifying-the-index-provider[text indexes].] + +However, not all indexes have available configuration settings or more than one provider. +In those cases, nothing needs to be specified and the `OPTIONS` map should be omitted from the query. + +[TIP] +Creating an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `CREATE INDEX` privilege]. + +A newly created index is not immediately available but is created in the background. + +[[create-range-index]] +=== Creating a range index + +Creating a range index can be done with the `CREATE INDEX` command. +Note that the index name must be unique. + +Range indexes have only one index provider available, `range-1.0`, and no supported index configuration. + +[[range-indexes-supported-predicates]] +[discrete] +==== Supported predicates + +Range indexes support most types of predicates: + +[cols="2", options="header"] +|=== + +| Predicate | Syntax + +| Equality check. +a| +[source, syntax, role="noheader"] +---- +n.prop = value +---- + +| List membership check. +a| +[source, syntax, role="noheader"] +---- +n.prop IN list +---- + +| Existence check. +a| +[source, syntax, role="noheader"] +---- +n.prop IS NOT NULL +---- + +| Range search. +a| +[source, syntax, role="noheader"] +---- +n.prop > value +---- + +| Prefix search. +a| +[source, syntax, role="noheader"] +---- +STARTS WITH +---- + +|=== + +[[range-indexes-examples]] +[discrete] +==== Examples + +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-single-property-range-index-for-nodes[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-single-property-range-index-for-relationships[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-composite-range-index-for-nodes[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-composite-range-index-for-relationships[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-range-index-by-param[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-range-index-only-if-it-does-not-already-exist[] + +[discrete] +[[create-a-single-property-range-index-for-nodes]] +===== Create a single-property range index for nodes + +The following statement will create a named range index on all nodes labeled with `Person` and which have the `surname` property. + +.Creating a node range index on a single property +[source, cypher] +---- +CREATE INDEX node_range_index_name FOR (n:Person) ON (n.surname) +---- + +[discrete] +[[create-a-single-property-range-index-for-relationships]] +===== Create a single-property range index for relationships + +The following statement will create a named range index on all relationships with relationship type `KNOWS` and property `since`. + +.Creating a relationship range index on a single property +[source, cypher] +---- +CREATE INDEX rel_range_index_name FOR ()-[r:KNOWS]-() ON (r.since) +---- + +[discrete] +[[create-a-composite-range-index-for-nodes]] +===== Create a composite range index for nodes + +A range index on multiple properties is also called a composite index. +For node range indexes, only nodes with the specified label and that contain all the specified properties will be added to the index. + +The following statement will create a named composite range index on all nodes labeled with `Person` and which have both an `age` and `country` property. + +.Creating a composite node range index on multiple properties +[source, cypher] +---- +CREATE INDEX composite_range_node_index_name FOR (n:Person) ON (n.age, n.country) +---- + +[discrete] +[[create-a-composite-range-index-for-relationships]] +===== Create a composite range index for relationships + +A range index on multiple properties is also called a composite index. +For relationship range indexes, only relationships with the specified type and that contain all the specified properties will be added to the index. + +The following statement will create a named composite range index on all relationships labeled with `PURCHASED` and which have both a `date` and `amount` property. + +.Creating a composite relationship range index on multiple properties +[source, cypher] +---- +CREATE INDEX composite_range_rel_index_name FOR ()-[r:PURCHASED]-() ON (r.date, r.amount) +---- + +[discrete] +[[create-a-range-index-by-param]] +===== Create a range index using a parameter + +_This feature was introduced in Neo4j 5.16._ + +The following statement will create a named range index on all nodes with a `Person` label and a `firstname` property using a parameter for the index name. + +.Parameters +[source,javascript, indent=0] +---- +{ + "name": "range_index_param" +} +---- + +.Creating a node range index on a single property +[source, cypher] +---- +CREATE INDEX $name FOR (n:Person) ON (n.firstname) +---- + +[discrete] +[[create-a-range-index-only-if-it-does-not-already-exist]] +===== Create a range index only if it does not already exist + +If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. + +.Creating a range index with `IF NOT EXISTS` +[source, cypher] +---- +CREATE INDEX node_range_index_name IF NOT EXISTS +FOR (n:Person) ON (n.surname) +---- + +The index will not be created if there already exists an index with the same schema and type, same name or both. + +[[create-text-index]] +=== Creating a text index + +Creating a text index can be done with the `CREATE TEXT INDEX` command. +Note that the index name must be unique. + +Text indexes have no supported index configuration and, as of Neo4j 5.1, they have two index providers available, `text-2.0` (default) and `text-1.0` (deprecated). + +[[text-indexes-supported-predicates]] +[discrete] +==== Supported predicates + +Text indexes only solve predicates operating on `STRING` values. + +The following predicates that only operate on `STRING` values are always solvable by a text index: + +* `STARTS WITH` +* `ENDS WITH` +* `CONTAINS` + +However, other predicates are only used when it is known that the property is compared to a `STRING`: + +* `n.prop = "string"` +* `n.prop IN ["a", "b", "c"]` + +This means that a text index is not able to solve, for example, e.g. `a.prop = b.prop`, unless a xref:constraints/examples.adoc#constraints-examples-node-property-type[type constraint] also exists on the property. + +Text indexes support the following predicates: + +[cols="2", options="header"] +|=== +| Predicate | Syntax + +| Equality check. +a| +[source, syntax, role="noheader"] +---- +n.prop = 'example_string' +---- + +| List membership check. +a| +[source, syntax, role="noheader"] +---- +n.prop IN ['abc', 'example_string', 'neo4j'] +---- + +| Prefix search. +a| +[source, syntax, role="noheader"] +---- +STARTS WITH +---- + +| Suffix search. +a| +[source, syntax, role="noheader"] +---- +ENDS WITH +---- + +| Substring search. +a| +[source, syntax, role="noheader"] +---- +CONTAINS +---- + +|=== + +As of Neo4j 5.11, the above set of predicates can be extended with the use of type constraints. +See the section about xref:indexes/search-performance-indexes/using-indexes.adoc#type-constraints[index compatibility and type constraints] for more information. + +[TIP] +Text indexes are only used for exact query matches. To perform approximate matches (including, for example, variations and typos), and to compute a similarity score between `STRING` values, use semantic xref:indexes/semantic-indexes/full-text-indexes.adoc[full-text indexes] instead. + +[discrete] +[[text-indexes-examples]] +==== Examples + +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-node-text-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-relationship-text-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-text-index-by-param[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-text-index-only-if-it-does-not-already-exist[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-text-index-specifying-the-index-provider[] + +[discrete] +[[create-a-node-text-index]] +===== Create a node text index + +The following statement will create a named text index on all nodes labeled with `Person` and which have the `nickname` `STRING` property. + +.Creating a node text index on a single property +[source, cypher] +---- +CREATE TEXT INDEX node_text_index_nickname FOR (n:Person) ON (n.nickname) +---- + +[discrete] +[[create-a-relationship-text-index]] +===== Create a relationship text index + +The following statement will create a named text index on all relationships with relationship type `KNOWS` and `STRING` property `interest`. + +.Creating a relationship text index on a single property +[source, cypher] +---- +CREATE TEXT INDEX rel_text_index_name FOR ()-[r:KNOWS]-() ON (r.interest) +---- + +[discrete] +[[create-a-text-index-by-param]] +===== Create a text index using a parameter + +_This feature was introduced in Neo4j 5.16._ + +The following statement will create a named text index on all nodes with the `Person` label the `favoriteColor` `STRING` property using a parameter for the index name. + +.Parameters +[source,javascript, indent=0] +---- +{ + "name": "text_index_param" +} +---- + +.Creating a node text index on a single property +[source, cypher] +---- +CREATE TEXT INDEX $name FOR (n:Person) ON (n.favoriteColor) +---- + +[discrete] +[[create-a-text-index-only-if-it-does-not-already-exist]] +===== Create a text index only if it does not already exist + +If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. + +The following statement will attempt to create a named text index on all nodes labeled with `Person` and which have the `nickname` `STRING` property. + +.Creating a text index with `IF NOT EXISTS` +[source, cypher] +---- +CREATE TEXT INDEX node_index_name IF NOT EXISTS FOR (n:Person) ON (n.nickname) +---- + +Note that the index will not be created if there already exists an index with the same schema and type, same name or both. + +[discrete] +[[create-a-text-index-specifying-the-index-provider]] +===== Create a text index specifying the index provider + +To create a text index with a specific index provider, the `OPTIONS` clause is used. +The valid values for the index provider are `text-2.0` and `text-1.0` (deprecated). +The default provider is `text-2.0`. + +.Creating a text index with index provider +[source, cypher] +---- +CREATE TEXT INDEX text_index_with_indexprovider FOR ()-[r:TYPE]-() ON (r.prop1) +OPTIONS {indexProvider: 'text-2.0'} +---- + +There is no supported index configuration for text indexes. + +[[create-point-index]] +=== Creating a point index + +Creating a point index can be done with the `CREATE POINT INDEX` command. +Note that the index name must be unique. + +Point indexes have supported index configuration, but only one index provider available, `point-1.0`. + +[discrete] +[[point-indexes-supported-predicates]] +==== Supported predicates + +Point indexes only solve predicates operating on `POINT` values. + +Point indexes support the following predicates: + +[cols="2", options="header"] +|=== +| Predicate | Syntax + +| Property point value. +a| +[source, syntax, role="noheader"] +---- +n.prop = point({x: value, y: value}) +---- + +| Within bounding box. +a| +[source, syntax, role="noheader"] +---- +point.withinBBox(n.prop, lowerLeftCorner, upperRightCorner) +---- + +| Distance. +a| +[source, syntax, role="noheader"] +---- +point.distance(n.prop, center) < = distance +---- + +|=== + +As of Neo4j 5.11, the above set of predicates can be extended with the use of type constraints. +See xref:indexes/search-performance-indexes/using-indexes.adoc#index-compatibility-type-constraints[Index compatibility and type constraints] for more information. + +[TIP] +To learn more about the spatial data types supported by Cypher, see the page about xref:values-and-types/spatial.adoc[Spatial values]. + +[discrete] +[[point-indexes-examples]] +==== Examples + +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-node-point-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-relationship-point-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-point-index-by-param[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-point-index-only-if-it-does-not-already-exist[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-point-index-specifying-the-index-configuration[] + +[discrete] +[[create-a-node-point-index]] +===== Create a node point index + +The following statement will create a named point index on all nodes labeled with `Person` and which have the `sublocation` `POINT` property. + +.Creating a node point index on a single property +[source, cypher] +---- +CREATE POINT INDEX node_point_index_name FOR (n:Person) ON (n.sublocation) +---- + +[discrete] +[[create-a-relationship-point-index]] +===== Create a relationship point index + +The following statement will create a named point index on all relationships with relationship type `STREET` and `POINT` property `intersection`. + +.Creating a relationship point index on a single property +[source, cypher] +---- +CREATE POINT INDEX rel_point_index_name FOR ()-[r:STREET]-() ON (r.intersection) +---- + +[discrete] +[[create-a-point-index-by-param]] +===== Create a point index using a parameter + +_This feature was introduced in Neo4j 5.16._ + +The following statement will create a named point index on all relationships with relationship type `STREET` and `POINT` property `coordinate` using a parameter for the index name. + +.Parameters +[source,javascript, indent=0] +---- +{ + "name": "point_index_param" +} +---- + +.Creating a relationship point index on a single property +[source, cypher] +---- +CREATE POINT INDEX $name FOR ()-[r:STREET]-() ON (r.coordinate) +---- + +[discrete] +[[create-a-point-index-only-if-it-does-not-already-exist]] +===== Create a point index only if it does not already exist + +If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. + +.Creating a point index with `IF NOT EXISTS` +[source, cypher] +---- +CREATE POINT INDEX node_point_index IF NOT EXISTS +FOR (n:Person) ON (n.sublocation) +---- + +Note that the index will not be created if there already exists an index with the same schema and type, same name or both. + +[discrete] +[[create-a-point-index-specifying-the-index-configuration]] +===== Create a point index specifying the index configuration + +To create a point index with a specific index configuration, the `indexConfig` settings in the `OPTIONS` clause. +The valid configuration settings are: + +* `spatial.cartesian.min` (default value: [`-1000000.0`, `-1000000.0`]) +* `spatial.cartesian.max` (default value: [`1000000.0`, `1000000.0`]) +* `spatial.cartesian-3d.min` (default value: [`-1000000.0`, `-1000000.0`, `-1000000.0`]) +* `spatial.cartesian-3d.max` (default value: [`1000000.0`, `1000000.0`, `1000000.0``]) +* `spatial.wgs-84.min` (default value: [`-180.0`, `-90.0`]) +* `spatial.wgs-84.max` (default value: [`-180.0`, `-90.0`]) +* `spatial.wgs-84-3d.min` (default value: [`-180.0`, `-90.0`, `-1000000.0`]) +* `spatial.wgs-84-3d.max` (default value: [`180.0`, `90.0`, `1000000.0`]) + + +The following statement will create a point index specifying the `spatial.cartesian.min` and `spatial.cartesian.max` settings. + +.Creating a point index with index configuration +[source, cypher] +---- +CREATE POINT INDEX point_index_with_config +FOR (n:Label) ON (n.prop2) +OPTIONS { + indexConfig: { + `spatial.cartesian.min`: [-100.0, -100.0], + `spatial.cartesian.max`: [100.0, 100.0] + } +} +---- + +Note that the wgs-84 and 3D cartesian settings, which are not specified in this example, will be set with their respective default values. + +[[create-lookup-index]] +=== Creating a token lookup index + +Two token lookup indexes are created by default when creating a Neo4j database (one node label lookup index and one relationship type lookup index). +Only one node label and one relationship type lookup index can exist at the same time. + +If a token lookup index has been deleted, it can be recreated with the `CREATE LOOKUP INDEX` command. +Note that the index name must be unique. + +Token lookup indexes have only one index provider available, `token-lookup-1.0`, and no supported index configuration. + + +[discrete] +[[lookup-index-supported-predicates]] +==== Supported predicates + +Token lookup indexes are present by default and solve only node label and relationship type predicates: + +[cols="2, 2a", options="header"] +|=== +| Predicate | Syntax (example) + +| Node label predicate. +| +[source, syntax, role="noheader"] +---- +MATCH (n:Label) +---- + +[source, syntax, role="noheader"] +---- +MATCH (n) +WHERE n:Label +---- + +| Relationship type predicate. +| +[source, syntax, role="noheader"] +---- +MATCH ()-[r:REL]->() +---- + +[source, syntax, role="noheader"] +---- +MATCH ()-[r]->() +WHERE r:REL +---- + +|=== + +[WARNING] +==== +Token lookup indexes improve the performance of Cypher queries and the population of other indexes. Dropping these indexes may lead to severe performance degradation. +==== + + +[discrete] +[[lookup-index-examples]] +==== Examples + +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-node-label-lookup-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-relationship-type-lookup-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-lookup-index-only-if-it-does-not-already-exist[] + +[discrete] +[[create-a-node-label-lookup-index]] +===== Create a node label lookup index + +The following statement will create a named node label lookup index on all nodes with one or more labels: + +// Lookup indexes exist by default, recreating them would raise an error +.Creating a node label lookup index +[source, cypher, role=test-skip] +---- +CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) +---- + +[NOTE] +==== +Only one node label lookup index can exist at a time. +==== + +[discrete] +[[create-a-relationship-type-lookup-index]] +===== Create a relationship type lookup index + +The following statement will create a named relationship type lookup index on all relationships with any relationship type. + +// Lookup indexes exist by default, recreating them would raise an error +.Creating a relationship type lookup index +[source, cypher, role=test-skip] +---- +CREATE LOOKUP INDEX rel_type_lookup_index FOR ()-[r]-() ON EACH type(r) +---- + +[NOTE] +==== +Only one relationship type lookup index can exist at a time. +==== + +[discrete] +[[create-a-lookup-index-only-if-it-does-not-already-exist]] +===== Create a token lookup index only if it does not already exist + +If it is not known whether an index exists or not, add `IF NOT EXISTS` to ensure it does. + +.Creating a node label lookup index with `IF NOT EXISTS` +[source, cypher] +---- +CREATE LOOKUP INDEX node_label_lookup IF NOT EXISTS FOR (n) ON EACH labels(n) +---- + +The index will not be created if there already exists an index with the same schema and type, same name or both. + + +[[create-conflicting-index]] +=== Creating an index when a conflicting index or constraint exists + +* xref:indexes/search-performance-indexes/managing-indexes.adoc#failure-to-create-an-already-existing-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#failure-to-create-an-index-with-the-same-name-as-an-already-existing-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#failure-to-create-an-index-when-a-constraint-already-exists[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#failure-to-create-an-index-with-the-same-name-as-an-already-existing-constraint[] + +[discrete] +[[failure-to-create-an-already-existing-index]] +==== Failure to create an already existing index + +Create an index on the property `title` on nodes with the `Book` label, when that index already exists. + +//// +[source, cypher, role=test-setup] +---- +CREATE INDEX example_index FOR (n:Book) ON (n.title) +---- +//// + +.Creating a duplicate index +[source, cypher, role=test-fail] +---- +CREATE INDEX bookTitleIndex FOR (book:Book) ON (book.title) +---- + +In this case, the index can not be created because it already exists. + +.Error message +[source, error] +---- +There already exists an index (:Book {title}). +---- + +Using `IF NOT EXISTS` when creating the index would result in no error and would not create a new index. + +[discrete] +[[failure-to-create-an-index-with-the-same-name-as-an-already-existing-index]] +==== Failure to create an index with the same name as an already existing index + +Create a named index on the property `numberOfPages` on nodes with the `Book` label, when an index with the given name already exists. +The index type of the existing index does not matter. + +//// +[source, cypher, role=test-setup] +---- +CREATE TEXT INDEX indexOnBooks FOR (b:Label1) ON (b.prop1) +---- +//// + +.Creating an index with a duplicated name +[source, cypher, role=test-fail] +---- +CREATE INDEX indexOnBooks FOR (book:Book) ON (book.numberOfPages) +---- + +In this case, the index cannot be created because there already exists an index with the given name. + +.Error message +[source, error] +---- +There already exists an index called 'indexOnBooks'. +---- + +Using `IF NOT EXISTS` when creating the index would result in no error and would not create a new index. + +[discrete] +[[failure-to-create-an-index-when-a-constraint-already-exists]] +==== Failure to create an index when a constraint already exists + +Create an index on the property `isbn` on nodes with the `Book` label, when an index-backed constraint already exists on that schema. +This is only relevant for range indexes. + +//// +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT uniqueBookIsbn FOR (book:Book) REQUIRE (book.isbn) IS UNIQUE +---- +//// + +.Creating a range index on same schema as existing index-backed constraint +[source, cypher, role=test-fail] +---- +CREATE INDEX bookIsbnIndex FOR (book:Book) ON (book.isbn) +---- + +In this case, the index can not be created because an index-backed constraint already exists on that label and property combination. + +.Error message +[source, error] +---- +There is a uniqueness constraint on (:Book {isbn}), so an index is already created that matches this. +---- + +[discrete] +[[failure-to-create-an-index-with-the-same-name-as-an-already-existing-constraint]] +==== Failure to create an index with the same name as an already existing constraint + +Create a named index on the property `numberOfPages` on nodes with the `Book` label, when a constraint with the given name already exists. + +//// +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT bookRecommendations FOR (book:Book) REQUIRE (book.recommend) IS NOT NULL +---- +//// + +.Creating an index with same name as an existing constraint +[source, cypher, role=test-fail] +---- +CREATE INDEX bookRecommendations FOR (book:Book) ON (book.recommendations) +---- + +In this case, the index can not be created because there already exists a constraint with the given name. + +.Error message +[source, error] +---- +There already exists a constraint called 'bookRecommendations'. +---- + +[[list-indexes]] +== +SHOW INDEXES+ + +Listing indexes can be done with `SHOW INDEXES`. + +[TIP] +Listing indexes requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `SHOW INDEX` privilege]. + +[discrete] +[[listing-indexes-examples]] +=== Examples + +* xref:indexes/search-performance-indexes/managing-indexes.adoc#listing-all-indexes[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#listing-specific-columns[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#listing-indexes-with-filtering[] + + +[discrete] +[[listing-all-indexes]] +==== Listing all indexes + +To list all indexes with the default output columns, the `SHOW INDEXES` command can be used. +If all columns are required, use `SHOW INDEXES YIELD *`. + +.Showing all indexes +[source, cypher, role=test-result-skip] +---- +SHOW INDEXES +---- + +// SHOW INDEXES default outputs +// 4.4: id, name, state, populationPercent, uniqueness, type, entityType, labelsOrTypes, properties, indexProvider +// 5.0: id, name, state, populationPercent, type, entityType, labelsOrTypes, properties, indexProvider, owningConstraint +// 5.8: id, name, state, populationPercent, type, entityType, labelsOrTypes, properties, indexProvider, owningConstraint, lastRead, readCount + +.Result +[queryresult] +---- ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | NULL | 0 | +| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | 2023-03-13T11:41:44.537Z | 1 | +| 16 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | 2023-04-10T15:41:44.537Z | 2 | +| 17 | "indexOnBooks" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Label1"] | ["prop1"] | "text-2.0" | NULL | NULL | 0 | +| 14 | "node_label_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "NODE" | NULL | NULL | "token-lookup-1.0" | NULL | 2023-04-13T08:11:15.537Z | 10 | +| 10 | "node_point_index_name" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Person"] | ["sublocation"] | "point-1.0" | NULL | 2023-04-05T16:21:44.692Z | 1 | +| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | 2022-12-30T02:01:44.537Z | 6 | +| 6 | "node_text_index_nickname" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["nickname"] | "text-2.0" | NULL | 2023-04-13T11:41:44.537Z | 2 | +| 12 | "point_index_param" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["coordinate"] | "point-1.0" | NULL | NULL | 0 | +| 13 | "point_index_with_config" | "ONLINE" | 100.0 | "POINT" | "NODE" | ["Label"] | ["prop2"] | "point-1.0" | NULL | NULL | 0 | +| 5 | "range_index_param" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["firstname"] | "range-1.0" | NULL | 2023-12-13T08:23:53.338Z | 2 | +| 11 | "rel_point_index_name" | "ONLINE" | 100.0 | "POINT" | "RELATIONSHIP" | ["STREET"] | ["intersection"] | "point-1.0" | NULL | 2023-03-03T13:37:42.537Z | 2 | +| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | 2023-04-12T10:41:44.692Z | 5 | +| 7 | "rel_text_index_name" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["KNOWS"] | ["interest"] | "text-2.0" | NULL | 2023-04-01T10:40:44.537Z | 3 | +| 15 | "rel_type_lookup_index" | "ONLINE" | 100.0 | "LOOKUP" | "RELATIONSHIP" | NULL | NULL | "token-lookup-1.0" | NULL | 2023-04-12T21:41:44.537Z | 7 | +| 8 | "text_index_param" | "ONLINE" | 100.0 | "TEXT" | "NODE" | ["Person"] | ["favoriteColor"] | "text-2.0" | NULL | NULL | 0 | +| 9 | "text_index_with_indexprovider" | "ONLINE" | 100.0 | "TEXT" | "RELATIONSHIP" | ["TYPE"] | ["prop1"] | "text-2.0" | NULL | NULL | 0 | +| 18 | "uniqueBookIsbn" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["isbn"] | "range-1.0" | "uniqueBookIsbn" | 2023-04-13T11:41:44.692Z | 6 | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +18 rows +---- + +One of the output columns from `SHOW INDEXES` is the name of the index. +This can be used to drop the index with the xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-an-index[`DROP INDEX` command]. + + +[discrete] +[[listing-specific-columns]] +==== Listing specific columns + +It is possible to return only specific columns of the available indexes using the `YIELD` clause: + + +.Returning specific columns for all indexes +[source, cypher, role=test-result-skip] +---- +SHOW INDEXES +YIELD name, type, indexProvider AS provider, options, createStatement +RETURN name, type, provider, options.indexConfig AS config, createStatement +---- + +.Result +[queryresult] +---- ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | type | provider | config | createStatement | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| "composite_range_node_index_name" | "RANGE" | "range-1.0" | {} | "CREATE RANGE INDEX `composite_range_node_index_name` FOR (n:`Person`) ON (n.`age`, n.`country`)" | +| "composite_range_rel_index_name" | "RANGE" | "range-1.0" | {} | "CREATE RANGE INDEX `composite_range_rel_index_name` FOR ()-[r:`PURCHASED`]-() ON (r.`date`, r.`amount`)" | +| "example_index" | "RANGE" | "range-1.0" | {} | "CREATE RANGE INDEX `example_index` FOR (n:`Book`) ON (n.`title`)" | +| "indexOnBooks" | "TEXT" | "text-2.0" | {} | "CREATE TEXT INDEX `indexOnBooks` FOR (n:`Label1`) ON (n.`prop1`) OPTIONS {indexConfig: {}, indexProvider: 'text-2.0'}" | +| "index_343aff4e" | "LOOKUP" | "token-lookup-1.0" | {} | "CREATE LOOKUP INDEX `index_343aff4e` FOR (n) ON EACH labels(n)" | +| "index_f7700477" | "LOOKUP" | "token-lookup-1.0" | {} | "CREATE LOOKUP INDEX `index_f7700477` FOR ()-[r]-() ON EACH type(r)" | +| "node_point_index_name" | "POINT" | "point-1.0" | {`spatial.cartesian.min`: [-1000000.0, -1000000.0], `spatial.wgs-84.min`: [-180.0, -90.0], `spatial.wgs-84.max`: [180.0, 90.0], `spatial.cartesian.max`: [1000000.0, 1000000.0], `spatial.wgs-84-3d.max`: [180.0, 90.0, 1000000.0], `spatial.cartesian-3d.min`: [-1000000.0, -1000000.0, -1000000.0], `spatial.cartesian-3d.max`: [1000000.0, 1000000.0, 1000000.0], `spatial.wgs-84-3d.min`: [-180.0, -90.0, -1000000.0]} | "CREATE POINT INDEX `node_point_index_name` FOR (n:`Person`) ON (n.`sublocation`) OPTIONS {indexConfig: {`spatial.cartesian-3d.max`: [1000000.0, 1000000.0, 1000000.0],`spatial.cartesian-3d.min`: [-1000000.0, -1000000.0, -1000000.0],`spatial.cartesian.max`: [1000000.0, 1000000.0],`spatial.cartesian.min`: [-1000000.0, -1000000.0],`spatial.wgs-84-3d.max`: [180.0, 90.0, 1000000.0],`spatial.wgs-84-3d.min`: [-180.0, -90.0, -1000000.0],`spatial.wgs-84.max`: [180.0, 90.0],`spatial.wgs-84.min`: [-180.0, -90.0]}, indexProvider: 'point-1.0'}" | +| "node_range_index" | "RANGE" | "range-1.0" | {} | "CREATE RANGE INDEX `node_range_index` FOR (n:`Person`) ON (n.`surname`)" | +| "node_text_index_nickname" | "TEXT" | "text-2.0" | {} | "CREATE TEXT INDEX `node_text_index_nickname` FOR (n:`Person`) ON (n.`nickname`) OPTIONS {indexConfig: {}, indexProvider: 'text-2.0'}" | +| "point_index_with_config" | "POINT" | "point-1.0" | {`spatial.cartesian.min`: [-100.0, -100.0], `spatial.wgs-84.min`: [-180.0, -90.0], `spatial.wgs-84.max`: [180.0, 90.0], `spatial.cartesian.max`: [100.0, 100.0], `spatial.wgs-84-3d.max`: [180.0, 90.0, 1000000.0], `spatial.cartesian-3d.min`: [-1000000.0, -1000000.0, -1000000.0], `spatial.cartesian-3d.max`: [1000000.0, 1000000.0, 1000000.0], `spatial.wgs-84-3d.min`: [-180.0, -90.0, -1000000.0]} | "CREATE POINT INDEX `point_index_with_config` FOR (n:`Label`) ON (n.`prop2`) OPTIONS {indexConfig: {`spatial.cartesian-3d.max`: [1000000.0, 1000000.0, 1000000.0],`spatial.cartesian-3d.min`: [-1000000.0, -1000000.0, -1000000.0],`spatial.cartesian.max`: [100.0, 100.0],`spatial.cartesian.min`: [-100.0, -100.0],`spatial.wgs-84-3d.max`: [180.0, 90.0, 1000000.0],`spatial.wgs-84-3d.min`: [-180.0, -90.0, -1000000.0],`spatial.wgs-84.max`: [180.0, 90.0],`spatial.wgs-84.min`: [-180.0, -90.0]}, indexProvider: 'point-1.0'}" | +| "rel_point_index_name" | "POINT" | "point-1.0" | {`spatial.cartesian.min`: [-1000000.0, -1000000.0], `spatial.wgs-84.min`: [-180.0, -90.0], `spatial.wgs-84.max`: [180.0, 90.0], `spatial.cartesian.max`: [1000000.0, 1000000.0], `spatial.wgs-84-3d.max`: [180.0, 90.0, 1000000.0], `spatial.cartesian-3d.min`: [-1000000.0, -1000000.0, -1000000.0], `spatial.cartesian-3d.max`: [1000000.0, 1000000.0, 1000000.0], `spatial.wgs-84-3d.min`: [-180.0, -90.0, -1000000.0]} | "CREATE POINT INDEX `rel_point_index_name` FOR ()-[r:`STREET`]-() ON (r.`intersection`) OPTIONS {indexConfig: {`spatial.cartesian-3d.max`: [1000000.0, 1000000.0, 1000000.0],`spatial.cartesian-3d.min`: [-1000000.0, -1000000.0, -1000000.0],`spatial.cartesian.max`: [1000000.0, 1000000.0],`spatial.cartesian.min`: [-1000000.0, -1000000.0],`spatial.wgs-84-3d.max`: [180.0, 90.0, 1000000.0],`spatial.wgs-84-3d.min`: [-180.0, -90.0, -1000000.0],`spatial.wgs-84.max`: [180.0, 90.0],`spatial.wgs-84.min`: [-180.0, -90.0]}, indexProvider: 'point-1.0'}" | +| "rel_range_index_name" | "RANGE" | "range-1.0" | {} | "CREATE RANGE INDEX `rel_range_index_name` FOR ()-[r:`KNOWS`]-() ON (r.`since`)" | +| "rel_text_index_name" | "TEXT" | "text-2.0" | {} | "CREATE TEXT INDEX `rel_text_index_name` FOR ()-[r:`KNOWS`]-() ON (r.`interest`) OPTIONS {indexConfig: {}, indexProvider: 'text-2.0'}" | +| "text_index_with_indexprovider" | "TEXT" | "text-2.0" | {} | "CREATE TEXT INDEX `text_index_with_indexprovider` FOR ()-[r:`TYPE`]-() ON (r.`prop1`) OPTIONS {indexConfig: {}, indexProvider: 'text-2.0'}" | +| "uniqueBookIsbn" | "RANGE" | "range-1.0" | {} | "CREATE CONSTRAINT `uniqueBookIsbn` FOR (n:`Book`) REQUIRE (n.`isbn`) IS UNIQUE OPTIONS {indexConfig: {}, indexProvider: 'range-1.0'}" | ++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---- + +Note that `YIELD` is mandatory if the `RETURN` clause is used. +`RETURN` is not, however, mandatory when the YIELD clause is used. + + +[discrete] +[[listing-indexes-with-filtering]] +==== Listing indexes with filtering + +The `SHOW INDEX` command can be filtered in various ways. + +For example, to show only range indexes, use `SHOW RANGE INDEXES`. + +Another more flexible way of filtering the output is to use the `WHERE` clause. +An example is to only show indexes not belonging to constraints. + +To show only range indexes that does not belong to a constraint we can combine the filtering versions. + +.Showing range indexes +[source, cypher, role=test-result-skip] +---- +SHOW RANGE INDEXES WHERE owningConstraint IS NULL +---- + +.Result +[queryresult] +---- ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| id | name | state | populationPercent | type | entityType | labelsOrTypes | properties | indexProvider | owningConstraint | lastRead | readCount | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 3 | "composite_range_node_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["age", "country"] | "range-1.0" | NULL | NULL | 0 | +| 4 | "composite_range_rel_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["PURCHASED"] | ["date", "amount"] | "range-1.0" | NULL | 2023-03-13T11:41:44.537Z | 1 | +| 16 | "example_index" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Book"] | ["title"] | "range-1.0" | NULL | 2023-04-10T15:41:44.537Z | 2 | +| 1 | "node_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["surname"] | "range-1.0" | NULL | 2022-12-30T02:01:44.537Z | 6 | +| 5 | "range_index_param" | "ONLINE" | 100.0 | "RANGE" | "NODE" | ["Person"] | ["firstname"] | "range-1.0" | NULL | 2023-12-13T08:23:53.338Z | 2 | +| 2 | "rel_range_index_name" | "ONLINE" | 100.0 | "RANGE" | "RELATIONSHIP" | ["KNOWS"] | ["since"] | "range-1.0" | NULL | 2023-04-12T10:41:44.692Z | 5 | ++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +6 rows +---- + +This will only return the default output columns. + +To get all columns, use: + +[source, syntax, role="noheader"] +---- +SHOW RANGE INDEXES YIELD * WHERE owningConstraint IS NULL +---- + +[discrete] +[[listing-indexes-result-columns]] +==== Result columns for listing indexes + +The below table contains the full information about all columns returned by the `SHOW INDEXES YIELD *` command: + +.List indexes output +[options="header", cols="4,6,2"] +|=== +| Column | Description | Type + +| `id` +| The id of the index. label:default-output[] +| `INTEGER` + +| `name` +| Name of the index (explicitly set by the user or automatically assigned). label:default-output[] +| `STRING` + +| `state` +| Current state of the index. label:default-output[] +| `STRING` + +| `populationPercent` +| % of index population. label:default-output[] +| `FLOAT` + +| `type` +| The IndexType of this index (`FULLTEXT`, `LOOKUP`, `POINT`, `RANGE`, or `TEXT`). label:default-output[] +| `STRING` + +| `entityType` +| Type of entities this index represents (nodes or relationship). label:default-output[] +| `STRING` + +| `labelsOrTypes` +| The labels or relationship types of this index. label:default-output[] +| `LIST` + +| `properties` +| The properties of this index. label:default-output[] +| `LIST` + +| `indexProvider` +| The index provider for this index. label:default-output[] +| `STRING` + +// New in 5.0 +| `owningConstraint` +| The name of the constraint the index is associated with or `null` if the index is not associated with any constraint. label:default-output[] +| `STRING` + +| `lastRead` +| The last time the index was used for reading. +Returns `null` if the index has not been read since `trackedSince`, or if the statistics are not tracked. +label:default-output[] +label:new[Introduced in 5.8] +| `ZONED DATETIME` + +| `readCount` +| The number of read queries that have been issued to this index since `trackedSince`, or `null` if the statistics are not tracked. label:default-output[] +label:new[Introduced in 5.8] +| `INTEGER` + +| `trackedSince` +| The time when usage statistics tracking started for this index, or `null` if the statistics are not tracked. +label:new[Introduced in 5.8] +| `ZONED DATETIME` + +| `options` +| Information retrieved from the `OPTIONS` map about the provider and configuration settings for an index. +If neither is specified when creating the index, this column will return the default values. +| `MAP` + +| `failureMessage` +| The failure description of a failed index. +| `STRING` + +| `createStatement` +| Statement used to create the index. +| `STRING` + +|=== + + +[[drop-indexes]] +== +DROP INDEX+ + +An index can be dropped (removed) using the name with the `DROP INDEX index_name` command. +This command can drop indexes of any type, except those backing constraints. +The name of the index can be found using the xref:indexes/search-performance-indexes/managing-indexes.adoc#list-indexes[`SHOW INDEXES` command], given in the output column `name`. + +[source, syntax, role="noheader"] +---- +DROP INDEX index_name [IF EXISTS] +---- + +The `DROP INDEX` command is optionally idempotent. +This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. +With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. + +[TIP] +Dropping an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. + +[discrete] +[[drop-indexes-examples]] +=== Examples + +* xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-an-index[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-an-index-by-param[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-index-backing-constraint[] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-a-non-existing-index[] + + +[discrete] +[[drop-an-index]] +==== Drop an index + +The following statement will attempt to drop the index named `example_index`. + +.Dropping an index +[source, cypher] +---- +DROP INDEX example_index +---- + +If an index with that name exists it is removed, if not the command fails. + +[discrete] +[[drop-an-index-by-param]] +==== Drop an index using a parameter + +_This feature was introduced in Neo4j 5.16._ + +The following statement will attempt to drop the index named `node_range_index_name` using a parameter for the index name. + +.Parameters +[source,javascript, indent=0] +---- +{ + "name": "range_index_param" +} +---- + +.Dropping an index +[source, cypher] +---- +DROP INDEX $name +---- + +If an index with that name exists it is removed, if not the command fails. + +[discrete] +[[drop-index-backing-constraint]] +==== Failure to drop an index backing a constraint + +It is not possible to drop indexes that back constraints. + +.Dropping an index backing a constraint +[source, cypher,role=test-fail] +---- +DROP INDEX uniqueBookIsbn +---- + +.Error message +[source, error] +---- +Unable to drop index: Index belongs to constraint: `uniqueBookIsbn` +---- + +Dropping the index-backed constraint will also remove the backing index. +For more information, see xref:constraints/examples.adoc#constraints-examples-drop-constraint[Drop a constraint by name]. + +[discrete] +[[drop-a-non-existing-index]] +==== Drop a non-existing index + +If it is uncertain if an index exists and you want to drop it if it does but not get an error should it not, use `IF EXISTS`. + +The following statement will attempt to drop the index named `missing_index_name`. + +.Dropping an index with `IF EXISTS` +[source, cypher] +---- +DROP INDEX missing_index_name IF EXISTS +---- + +If an index with that name exists it is removed, if not the command does nothing. diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/overview.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/overview.adoc new file mode 100644 index 000000000..7b8429bd4 --- /dev/null +++ b/modules/ROOT/pages/indexes/search-performance-indexes/overview.adoc @@ -0,0 +1,24 @@ +:description: Overview of the search-performance indexes available in Neo4j. += Search-performance indexes + +Search-performance indexes enable quicker retrieval of exact matches between an index and the primary data storage. +There are four different search-performance indexes available in Neo4j: + +* *Range indexes*: Neo4j’s default index. +Supports most types of predicates. + +* *Text indexes*: solves predicates operating on `STRING` values. +Optimized for queries filtering with the `STRING` operators `CONTAINS` and `ENDS WITH`. + +* *Point indexes*: solves predicates on spatial `POINT` values. +Optimized for queries filtering on distance or within bounding boxes. + +* *Token lookup indexes*: only solves node label and relationship type predicates (i.e. they cannot solve any predicates filtering on properties). +Two token lookup indexes (one for node labels and one for relationship types) are present when a database is created in Neo4j. + +To learn more about creating, listing, and deleting these indexes, as well as more details about the predicates supported by each index type, see xref:indexes/search-performance-indexes/managing-indexes.adoc[]. + +For information about how indexes impact the performance of Cypher queries, as well as some heuristics for when to use (and not to use) a search-performance index, see xref:indexes/search-performance-indexes/using-indexes.adoc[]. + +Search-performance indexes are used automatically, and if several indexes are available, the xref:planning-and-tuning/execution-plans.adoc[Cypher planner] will try to use the index (or indexes) that can most efficiently solve a particular predicate. +It is, however, possible to explicitly force a query to use a particular index with the `USING` keyword. For more information, see xref:indexes/search-performance-indexes/index-hints.adoc[]. diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc new file mode 100644 index 000000000..aab706148 --- /dev/null +++ b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc @@ -0,0 +1,1011 @@ +:description: Information about how to use the search-performance indexes in Neo4j. +:test-skip: true += The impact of indexes on query performance + +Search-performance indexes enable quicker and more efficient pattern matching by solving a particular combination of node label/relationship type and property predicate. +They are used automatically by the Cypher planner in `MATCH` clauses, usually at the start of a query, to scan the graph for the most appropriate place to start the pattern-matching process. + +By examining xref:planning-and-tuning/execution-plans.adoc[query execution plans], this page will explain the scenarios in which the various search-performance indexes are used to improve the performance of Cypher queries. +It will also provide some general heuristics for when to use indexes, and advice about how to avoid over-indexing. + +[[graph-example]] +== Example graph + +The examples on this page center around finding routes and points of interest in Central Park, New York, based on data provided by link:https://www.openstreetmap.org/[OpenStreetMap]. +The data model contains two node labels: + +* `OSMNode` (Open Street Map Node) -- a junction node with geo-spatial properties linking together routes from specific points. +* `PointOfInterest` -- a subcategory of `OSMNode`. +In addition to geospatial properties, these nodes also contain information about specific points of interest, such as statues, baseball courts, etc. in Central Park. + +The data model also contains one relationship type: `ROUTE`, which specifies the distance in meters between the nodes in the graph. + +image::using_indexes_example_graph.svg[width="600",role="middle"] + +In total, the graph contains 69165 nodes (of which 188 have the label `PointOfInterest`) and 152077 `ROUTE` relationships. + +To recreate the graph, download and import the link:https://github.com/neo4j-graph-examples/openstreetmap/blob/main/data/openstreetmap-50.dump[5.0 dump file] to an empty Neo4j database. +Dump files can be imported for both link:{neo4j-docs-base-uri}/aura/auradb/importing/import-database/[Aura] and link:{neo4j-docs-base-uri}/operations-manual/{page-version}/backup-restore/restore-dump/[on-prem] instances. + +[[token-lookup-indexes]] +== Token lookup indexes + +Two token lookup indexes are present by default when creating a Neo4j database. +They store copies of all node labels and relationship types in the database and only solve node label and relationship type predicates. + +The following query footnote:[The example queries on this page are prepended with `PROFILE`. This both runs the query and generates its execution plan. +For more information, see xref:planning-and-tuning/index.adoc#profile-and-explain[Execution plans and query tuning -> Note on PROFILE and EXPLAIN].], which counts the number of `PointOfInterest` nodes that have value `baseball` for the `type` property, will access the node label lookup index: + +.Query +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.type = 'baseball' +RETURN count(n) +---- + +.Result +[role="queryresult",options="header,footer",cols="m"] +|=== +| count(n) +| 26 +d|Rows:1 +|=== + +.Execution plan +---- ++-------------------+----+------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | `count(n)` | 1 | 1 | 0 | 0 | 0/0 | 0.075 | In Pipeline 1 | +| | +----+------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(n) AS `count(n)` | 1 | 1 | 0 | 32 | | | | +| | +----+------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | n.type = $autostring_0 | 9 | 26 | 376 | | | | | +| | +----+------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 3 | n:PointOfInterest | 188 | 188 | 189 | 376 | 116/0 | 8.228 | Fused in Pipeline 0 | ++-------------------+----+------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 565, total allocated memory: 472 +---- + +The following details are worth highlighting in the execution plan: + +* The xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-by-label-scan[`NodeByLabelScan`] operator accesses the node label lookup index and produces 188 rows, representing the 188 nodes with the `PointOfInterest` label in the database. +* The query required 565 DB hits (each xref:planning-and-tuning/operators/index.adoc#operators-dbhits[DB hit] represents an instance when the query required access to the database). +* The query completed in just over 8 milliseconds. + +Token lookup indexes are very important because they improve the performance of Cypher queries and the population of other indexes, and *deleting them will lead to severe performance degradation*. +In the above example, had a node label lookup index not existed, the `NodeByLabelScan` operator would have been replaced with xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-all-nodes-scan[`AllNodesScan`], which would have had to read all 69165 nodes from the database before returning a result. + +While useful, token lookup indexes will rarely be sufficient for applications querying databases of a non-trivial size because they cannot solve any property-related predicates. + +For more information about the predicates supported by token lookup indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#lookup-index-supported-predicates[Managing search-performance indexes -> Token lookup indexes: supported predicates]. + +[[range-indexes]] +== Range indexes + +Range indexes solve most types of predicates, and they are used for efficiently retrieving data based on a range of values. +They are particularly useful for dealing with properties that have ordered, comparable values. + +The following example first creates a relevant index on the type property for `PointOfInterest` nodes, and then runs the above query again, counting the number of `PointOfInterest` nodes that have a `baseball` `type` value: + +.Create a range index +[source,cypher] +---- +CREATE INDEX range_index_type FOR (n:PointOfInterest) ON (n.type) +---- + +[TIP] +If no index type is specified when creating an index, Neo4j will default to create a range index. +For more information about creating indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-indexes[Managing search-performance indexes -> CREATE INDEX]. + +.Rerun query after the creation of a relevant index +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.type = 'baseball' +RETURN count(n) +---- + +.Execution plan +---- ++-------------------+----+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | `count(n)` | 1 | 1 | 0 | 0 | 0/0 | 0.057 | In Pipeline 1 | +| | +----+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(n) AS `count(n)` | 1 | 1 | 0 | 32 | | | | +| | +----+----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | 2 | RANGE INDEX n:PointOfInterest(type) WHERE type = $autostring_0 | 5 | 26 | 27 | 376 | 0/1 | 0.945 | Fused in Pipeline 0 | ++-------------------+----+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 27, total allocated memory: 472 +---- + +Comparing this query plan with the plan generated before the creation of a relevant range index, the following has changed: + +* NodeByLabelScan has been replaced by xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[NodeIndexSeek]. +This only produces 26 rows (representing the 26 `PointOfInterest` nodes in the database with a `type` value set to `baseball`). +* The query now only requires 27 DB hits. +* The query completed in less than 1 millisecond - almost 8 times faster than it took the query to complete without a range index. + +These points all illustrate the fundamental point that search-performance indexes can significantly improve the performance of Cypher queries. + +For more information about the predicates supported by range indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#range-indexes-supported-predicates[Managing search-performance indexes -> Range indexes: supported predicates]. + +[[text-indexes]] +== Text indexes + +Text indexes are used for queries filtering on `STRING` properties. + +If there exists both a range and a text index on a given `STRING` property, the text index will only be used by the Cypher planner for queries filtering with the `CONTAINS` or `ENDS WITH` operators. +In all other cases, the range index will be used. + +To show this behavior, it is necessary to create a text index and a range index on the same property: + +.Create a text index +[source,cypher] +---- +CREATE TEXT INDEX text_index_name FOR (n:PointOfInterest) ON (n.name) +---- + +.Create a range index +[source,cypher] +---- +CREATE INDEX range_index_name FOR (n:PointOfInterest) ON (n.name) +---- + +The following query filters all `PointOfInterest` nodes with a `name` property that `CONTAINS` `'William'`: + +.Query filtering on what a `STRING` property `CONTAINS` +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.name CONTAINS 'William' +RETURN n.name AS name, n.type AS type +---- + +.Result +[role="queryresult",options="header,footer",cols="2*m"] +|=== +| name | type +| "William Shakespeare" | "statue" +| "William Tecumseh Sherman" | "equestrian statue" + +2+d|Rows:2 +|=== + +.Execution plan +---- ++------------------------+----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name, type | 1 | 2 | 0 | 0 | | | | +| | +----+----------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[n.name] AS name, cache[n.type] AS type | 1 | 2 | 0 | | | | | +| | +----+----------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +CacheProperties | 2 | cache[n.type], cache[n.name] | 1 | 2 | 6 | | | | | +| | +----+----------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexContainsScan | 3 | TEXT INDEX n:PointOfInterest(name) WHERE name CONTAINS $autostring_0 | 1 | 2 | 3 | 248 | 4/0 | 53.297 | Fused in Pipeline 0 | ++------------------------+----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 9, total allocated memory: 312 +---- + +The plan shows that the query uses the text index to find all relevant nodes. +If, however, the query is changed to use the `STARTS WITH` operator instead of `CONTAINS`, the query will use the range index instead: + +.Query filtering on what a `STRING` property `STARTS WITH` +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.name STARTS WITH 'William' +RETURN n.name, n.type +---- + +.Execution plan +---- ++-----------------------+----+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------+----+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | `n.name`, `n.type` | 1 | 2 | 0 | 0 | | | | +| | +----+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[n.name] AS `n.name`, n.type AS `n.type` | 1 | 2 | 4 | | | | | +| | +----+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeekByRange | 2 | RANGE INDEX n:PointOfInterest(name) WHERE name STARTS WITH $autostring_0, cache[n.name] | 1 | 2 | 3 | 248 | 4/1 | 1.276 | Fused in Pipeline 0 | ++-----------------------+----+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 7, total allocated memory: 312 +---- + +This is because range indexes store `STRING` values alphabetically. +This means that, while they are very efficient for retrieving exact matches of a `STRING`, or for prefix matching, they are less efficient for suffix and contains searches, where they have to scan all relevant properties to filter any matches. +Text indexes do not store `STRING` properties alphabetically, and are instead optimized for suffix and contains searches. +That said, if no range index had been present on the name property, the previous query would still have been able to utilize the text index. +It would have done so less efficiently than a range index, but it still would have been useful. + + +For more information about range index ordering, see the section on xref:indexes/search-performance-indexes/using-indexes.adoc#range-index-backed-order-by[Range index-backed ORDER BY]. + +[TIP] +Text indexes are only used for exact query matches. To perform approximate matches (including, for example, variations and typos), and to compute a similarity score between `STRING` values, use semantic xref:indexes/semantic-indexes/full-text-indexes.adoc[full-text indexes] instead. + +For more information about the predicates supported by text indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#text-indexes-supported-predicates[Managing search-performance indexes -> Text indexes: supported predicates]. + + +[[text-index-string-size]] +=== Text indexes and `STRING` sizes + +The size of the indexed `STRING` properties is also relevant to the planner’s selection between range and text indexes. + +Range indexes have a maximum key size limit of around 8 kb. +This means that range indexes cannot be used to index `STRING` values larger than 8 kb. +Text indexes, on the other hand, have a maximum key size limit of around 32 kb. +As a result, they can be used to index `STRING` values up to that size. + +For information about calculating the size of indexes, see link:https://neo4j.com/developer/kb/a-method-to-calculate-index-size/[Neo4j Knowledge Base -> A method to calculate the size of an index in Neo4j]. + +[[point-indexes]] +== Point indexes + +Point indexes solve predicates operating on spatial xref:values-and-types/spatial.adoc#spatial-values-point-type[`POINT`] values. +Point indexes are optimized for queries filtering for the xref:functions/spatial.adoc#functions-distance[distance] between property values, or for property values within a xref:functions/spatial.adoc#functions-withinBBox[bounding box]. + +The following example creates a point index which is then accessed through the `point.distance()` function to return the `name` and `type` of all `PointOfInterest` nodes within 100 meters of the `William Shakespeare` statue: + +.Create a point index +[source,cypher] +---- +CREATE POINT INDEX point_index_location FOR (n:PointOfInterest) ON (n.location) +---- + +.Query using the `point.distance()` function +[source,cypher] +---- +PROFILE +MATCH (p1:PointOfInterest {name:'William Shakespeare'}),(p2:PointOfInterest) +WHERE p1<>p2 AND point.distance(p1.location, p2.location) < 100 +RETURN p2.name AS name, p2.type AS type +---- + +.Result +[role="queryresult",options="header,footer",cols="2*m"] +|=== +| name | type +| "Walter Scott" | "statue" +| "Robert Burns" | "statue" +| "Christopher Columbus" | "statue" +| "Fitz-Greene Halleck" | "statue" + +2+d|Rows:4 +|=== + +.Execution plan +---- ++-------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name, type | 8 | 4 | 0 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[p2.name] AS name, cache[p2.type] AS type | 8 | 4 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +CacheProperties | 2 | cache[p2.type], cache[p2.name] | 8 | 4 | 12 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 3 | NOT p1 = p2 AND p1.name = $autostring_0 AND point.distance(cache[p1.location], cache[p2.location]) < | 8 | 4 | 2891 | | | | | +| | | | $autoint_1 | | | | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 4 | | 1060 | 1448 | 0 | | | | | +| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeekByRange | 5 | POINT INDEX p1:PointOfInterest(location) WHERE point.distance(location, cache[p2.location]) < $autoi | 1060 | 1448 | 1638 | 16616 | 1529/1 | 125.886 | Fused in Pipeline 1 | +| | | | nt_1, cache[p1.location] | | | | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | 6 | p2:PointOfInterest | 188 | 188 | 189 | 376 | 2/0 | 0.533 | In Pipeline 0 | ++-------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 4730, total allocated memory: 16952 +---- + +For more information about the predicates supported by text indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#point-indexes-supported-predicates[Managing search-performance indexes -> Point indexes: supported predicates]. + +[[point-index-config-settings]] +=== Point index configuration settings + +It is possible to xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-point-index-specifying-the-index-configuration[configure point indexes] to only index properties within a specific geographical area. +This is done by specifying either of the following settings in the `indexConfig` part of the `OPTIONS` clause when creating a point index: + +* `spatial.cartesian.min` and `spatial.cartesian.max`: used for xref:values-and-types/spatial.adoc#spatial-values-crs-cartesian[Cartesian 2D] coordinate systems. +* `spatial.cartesian-3d.min` and `spatial.cartesian-3d.max`: used for xref:values-and-types/spatial.adoc#spatial-values-crs-cartesian[Cartesian 3D] coordinate systems. +* `spatial.wgs-84.min` and `spatial.wgs-84.max`: used for xref:values-and-types/spatial.adoc#spatial-values-crs-geographic[WGS-84 2D] coordinate systems. +* `spatial.wgs-84-3d.min` and `spatial.wgs-84-3d.max`: used for xref:values-and-types/spatial.adoc#spatial-values-crs-geographic[WGS-84 3D] coordinate systems. + +The `min` and `max` of each setting define the minimum and maximum bounds for the spatial data in each coordinate system. + +For example, the following index would only store `OSMNodes` in the northern half of Central Park: + +.Create point index with configuration settings +[source, cypher] +---- +CREATE POINT INDEX central_park_north +FOR (o:OSMNode) ON (o.location) +OPTIONS { + indexConfig: { + `spatial.wgs-84.min`:[40.7714, -73.9743], + `spatial.wgs-84.max`:[40.7855, -73.9583] + } +} +---- + +Restricting the geographic area of a point index improves the performance of spatial queries. +This is especially beneficial when dealing with complex, large geo-spatial data, and when spatial queries are a significant part of an application’s functionality. + +[[composite-indexes]] +== Composite indexes + +It is possible to create an index on a single property or multiple properties. +The latter are called composite indexes and can be useful if queries against a database frequently filter on _all_ the properties indexed by the composite index. + +The following example first creates a composite index on `PointOfInterest` nodes for the properties `name` and `type`, and then queries the graph using the xref:patterns/concepts.adoc#shortest-path[shortestPath function] to determine both the path length (in terms of traversed relationships in the graph) and geographical distance between the `Zoo School` and its nearest `tennis pitch` (note that there are 32 unique `PointOfInterest` `tennis pitch` nodes in the graph): + +.Create composite index +[source,cypher] +---- +CREATE INDEX composite_index FOR (n:PointOfInterest) ON (n.name, n.type) +---- + +.Query with a filter on both properties indexed by the composite index +[source,cypher] +---- +MATCH (tennisPitch: PointOfInterest {name: 'tennis', type: 'pitch'}) +WITH tennisPitch +MATCH path = shortestPath((tennisPitch)-[:ROUTE*]-(:PointOfInterest {name: 'Zoo School'})) +WITH path, relationships(path) AS relationships +ORDER BY length(path) ASC +LIMIT 1 +UNWIND relationships AS rel +RETURN length(path) AS pathLength, sum(rel.distance) AS geographicalDistance +---- + + +.Result +[role="queryresult",options="header,footer",cols="2*m"] +|=== +|pathLength | geographicalDistance + +| 25 | 2410.4495689536334 + +2+d|Rows:1 +|=== + +.Execution plan +---- ++---------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++---------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------------+---------------------+ +| +ProduceResults | 0 | pathLength, geographicalDistance | 1 | 1 | 0 | 0 | 0/0 | 0.065 | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +OrderedAggregation | 1 | length(path) AS pathLength, sum(rel.distance) AS geographicalDistance | 1 | 1 | 50 | 5140 | 31/0 | 4.097 | pathLength ASC | In Pipeline 3 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------------+---------------------+ +| +Unwind | 2 | relationships AS rel | 1 | 25 | 0 | 3112 | 0/0 | 0.180 | | In Pipeline 2 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ +---------------------+ +| +Projection | 3 | relationships(path) AS relationships | 0 | 1 | 0 | | 0/0 | 0.050 | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Top | 4 | `length(path)` ASC LIMIT 1 | 0 | 1 | 0 | 57472 | 0/0 | 1.763 | length(path) ASC | In Pipeline 1 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------------+---------------------+ +| +Projection | 5 | length(path) AS `length(path)` | 0 | 32 | 0 | | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +------------------+ | +| +ShortestPath | 6 | path = (tennisPitch)-[anon_0:ROUTE*]-(anon_1) | 0 | 32 | 181451 | 70080 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +------------------+ | +| +MultiNodeIndexSeek | 7 | RANGE INDEX tennisPitch:PointOfInterest(name, type) WHERE name = $autostring_0 AND type = $autostrin | 0 | 31 | 0 | 376 | 131215/1 | 188.723 | | Fused in Pipeline 0 | +| | | g_1, RANGE INDEX anon_1:PointOfInterest(name) WHERE name = $autostring_2 | | | | | | | | | ++---------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------------+---------------------+ + +Total database accesses: 181501, total allocated memory: 116040 +---- + +The query plan shows the composite index being used, and not the previously created xref:indexes/search-performance-indexes/using-indexes.adoc#range-indexes[range index] on the `type` property. +This is because the composite index solves the queried predicate simultaneously, while the single propertied index would only be able to solve part of the predicate. + +[[composite-indexes-property-order]] +=== Property order and query planning + +Like single-property range indexes, composite indexes support all predicates: + +* Equality check: `n.prop = value` +* List membership check: `n.prop IN [value, ...]` +* Existence check: `n.prop IS NOT NULL` +* Range search: `n.prop > value` +* Prefix search: `n.prop STARTS WITH value` + +However, the order in which properties are defined when creating a composite index impacts how the planner will use the index to solve predicates. +For example, a composite index on `(n.prop1, n.prop2, n.prop3)` will generate a different query plan than a composite index created on `(n.prop3, n.prop2, n.prop1)`. + +The following example shows how composite indexes on the same properties defined in a different order will generate different execution plans: + +.Create a composite index on three properties +[source,cypher] +---- +CREATE INDEX composite_2 FOR (n:PointOfInterest) ON (n.lat, n.name, n.type) +---- + +Note the order in which the properties are defined when creating the index, with `lat` first, `name` second, and `type` last. + +.Query with a filter on the three indexed properties +[source, cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.lat = 40.769798 AND n.name STARTS WITH 'William' AND n.type IS NOT NULL +RETURN n.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1*m"] +|=== +| name +| "William Shakespeare" +1+d|Rows:1 +|=== + +.Execution plan +---- ++-----------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name | 0 | 0 | 0 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[n.name] AS name | 0 | 0 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | 2 | RANGE INDEX n:PointOfInterest(lat, name, type) WHERE lat = $autodouble_0 AND name STARTS WITH $autos | 0 | 0 | 1 | 248 | 0/2 | 1.276 | Fused in Pipeline 0 | +| | | tring_1 AND type IS NOT NULL, cache[n.name] | | | | | | | | ++-----------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 1, total allocated memory: 312 +---- + +The plan shows the recently created composite index is used. +It also shows that the predicates are filtered as specified in the query (i.e. an equality check on the `lat` property, a prefix search on the `name` property, and an existence check on the `type` property). + +However, if the order of the properties is altered when creating the index, a different query plan will be generated. +To demonstrate this behavior, it is first necessary to drop the recently created `composite_2` index and create a new composite index on the same properties defined in a different order: + +.Drop index +[source,cypher] +---- +DROP INDEX composite_2 +---- + +.Create a composite index on same three properties defined in a different order +[source,cypher] +---- +CREATE INDEX composite_3 FOR (n:PointOfInterest) ON (n.name, n.type, n.lat) +---- + +Note that the order of the properties has changed: the `name` property is now the first property defined in the composite index, and the `lat` property is indexed last. + +.Rerun query after the creation of a different composite index +[source, cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.lat = 40.769798 AND n.name STARTS WITH 'William' AND n.type IS NOT NULL +RETURN n.name AS name +---- + +.Execution plan +---- ++-----------------+----+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name | 0 | 0 | 0 | 0 | | | | +| | +----+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[n.name] AS name | 0 | 0 | 0 | | | | | +| | +----+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | cache[n.lat] = $autodouble_0 | 0 | 0 | 0 | | | | | +| | +----+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | 3 | RANGE INDEX n:PointOfInterest(name, type, lat) WHERE name STARTS WITH $autostring_1 AND type IS NOT | 0 | 2 | 3 | 248 | 2/0 | 0.807 | Fused in Pipeline 0 | +| | | NULL AND lat IS NOT NULL, cache[n.name], cache[n.lat] | | | | | | | | ++-----------------+----+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 3, total allocated memory: 312 +---- + +This plan now shows that, while a prefix search has been used to solve the `name` property predicate, the `lat` property predicate is no longer solved with an equality check, but rather with an existence check and an explicit xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-filter[filter] operation afterward. +Note that if the `composite_2` index had not been dropped before the query was rerun, the planner would have used it instead of the `composite_3` index. + +This is because, when using composite indexes, any predicate after a prefix search will automatically be planned as an existence check predicate. + +[[composite-index-rules]] +=== Composite index rules + +* If a query contains an equality check or a list membership check predicates, they need to be for the first properties defined when creating the composite index. + +* Queries utilizing a composite index can contain up to one range search or prefix search predicate. + +* There can be any number of existence check predicates. + +* Any predicates following a prefix search or an existence check will be planned as existence checks. + +* Suffix and substring search predicates can utilize composite indexes. +However, they are always planned as an existence check and any subsequent query predicates will accordingly also be planned as such. +Note that if these predicates are used, and a text index also exists on any of the indexed (`STRING`) properties, the planner will use the text index instead of a composite index. + +These rules can be important when creating composite indexes, as some checks are more efficient than others. +For instance, it is generally more efficient for the planner to perform an equality check on a property than an existence check. +Depending on the queries and the application, it may, therefore, be cost-effective to consider the order in which properties are defined when creating a composite index. + +Additionally, it bears repeating that composite indexes can only be used if a predicate filters on all the properties indexed by the composite index, and that composite indexes can only be created for range indexes (point and text indexes are single-property only). + +[[range-index-backed-order-by]] +== Range index-backed ORDER BY + +Range indexes store properties in ascending order (alphabetically for `STRING` values, and numerically for other `FLOAT` and `INTEGER` values). +This can have important implications for query performance, because the planner may be able to take advantage of a pre-existing index order and therefore not have to perform an expensive xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-sort[`Sort`] operation later in the query. + +To demonstrate this behavior, the following query will filter out any `ROUTE` relationships with a `distance` property less than `30`, and return the `distance` property of the matched relationships in ascending numerical order using the xref:clauses/order-by.adoc[ORDER BY] clause. + +.Query to return order of results without a relevant index +[source,syntax] +---- +PROFILE +MATCH ()-[r:ROUTE]-() +WHERE r.distance < 30 +RETURN r.distance AS distance +ORDER BY distance +---- + +.Execution plan +---- ++-----------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ +| +ProduceResults | 0 | distance | 3013 | 6744 | 0 | 0 | 0/0 | 12.784 | | | +| | +----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | | +| +Sort | 1 | distance ASC | 3013 | 6744 | 0 | 540472 | 0/0 | 50.600 | distance ASC | In Pipeline 1 | +| | +----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ +| +Projection | 2 | cache[r.distance] AS distance | 3013 | 6744 | 0 | | | | | | +| | +----+--------------------------------+----------------+-------+---------+----------------+ | +--------------+ | +| +Filter | 3 | cache[r.distance] < $autoint_0 | 3013 | 6744 | 10041 | | | | | | +| | +----+--------------------------------+----------------+-------+---------+----------------+ | +--------------+ | +| +Expand(All) | 4 | (anon_0)-[r:ROUTE]-(anon_1) | 10044 | 10041 | 151992 | | | | | | +| | +----+--------------------------------+----------------+-------+---------+----------------+ | +--------------+ | +| +AllNodesScan | 5 | anon_0 | 69165 | 69165 | 69166 | 376 | 31116/0 | 200.706 | | Fused in Pipeline 0 | ++-----------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ + +Total database accesses: 231199, total allocated memory: 540808 +---- + +This plan shows two important points about indexes and the ordering of results: + +* No index was used in this query. +* As a result, the planner has to perform a `Sort` operation to order the results by the distance property (in this case, it required 540472 bytes of memory). + +To see how an index could impact the query plan, it is first necessary to create a range index on the `distance` property: + +.Create a range index on a relationship type property +[source,cypher] +---- +CREATE INDEX range_index_relationships FOR ()-[r:ROUTE]-() ON (r.distance) +---- + +Re-running the query, it now generates a different plan: + +.Rerun query after the creation of a relevant index +[source,syntax] +---- +PROFILE +MATCH ()-[r:ROUTE]-() +WHERE r.distance < 30 +RETURN r.distance AS distance +ORDER BY distance +---- + +.Execution plan +---- + ++-----------------------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+----------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+----------------+---------------------+ +| +ProduceResults | 0 | distance | 301 | 6744 | 0 | 0 | | | | | +| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | | +| +Projection | 1 | cache[r.distance] AS distance | 301 | 6744 | 0 | | | | distance ASC | | +| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +----------------+ | +| +UndirectedRelationshipIndexSeekByRange | 2 | RANGE INDEX (anon_0)-[r:ROUTE(distance)]-(anon_1) WHERE distance < $autoint_0, cache[r.distance] | 301 | 6744 | 3373 | 248 | 2361/10 | 76.542 | r.distance ASC | Fused in Pipeline 0 | ++-----------------------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+----------------+---------------------+ + +Total database accesses: 3373, total allocated memory: 312 +---- + +Focusing on the same two points in the plan, the following has changed: + +* The recently created range index on the relationship type property `distance` is now used. +* As a result, the plan no longer needs to perform a `Sort` operation to order the results (because the `distance` property is already ordered by the index), and this substantially reduces the cost of the query (the total memory cost of the query is now 312 bytes). + +In the same way, the order of a range index can significantly improve queries using the xref:functions/aggregating.adoc#functions-max[`max()`] and xref:functions/aggregating.adoc#functions-min[`min()`] functions. + +[[multiple-index-use]] +== Multiple index use + +Indexes are principally used to find the starting points of patterns. +If a query contains one `MATCH` clause, then, as a general rule, only the index that best suits the predicates in that clause will be selected by the planner. +If, however, a query contains two or more `MATCH` clauses, it is possible to use several indexes. + +To show multiple indexes used in one query, the following example will first create a new index on the `lon` (longitude) property for `PointOfInterest` nodes. +It then uses a query that finds all `PointOfInterest` nodes north of the `William Shakespeare` statue in Central Park. + +.Create a range index on the longitude property +[source,cypher] +---- +CREATE INDEX range_index_lon FOR (n:PointOfInterest) ON (n.lon) +---- + +.Query to find all `PointOfInterest` nodes north of the `William Shakespeare` statue +[source,cypher] +---- +PROFILE +MATCH (ws:PointOfInterest {name:'William Shakespeare'}) +WITH ws +MATCH (poi:PointOfInterest) +WHERE poi.lon > ws.lon +RETURN poi.name AS name +---- + +.Execution plan +---- ++-------------------------+----+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------+----+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name | 9 | 143 | 0 | 0 | | | | +| | +----+-----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | poi.name AS name | 9 | 143 | 283 | | | | | +| | +----+-----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 2 | | 9 | 143 | 0 | | | | | +| |\ +----+-----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeekByRange | 3 | RANGE INDEX poi:PointOfInterest(lon) WHERE lon > ws.lon | 9 | 143 | 146 | 2280 | 233/1 | 1.460 | Fused in Pipeline 1 | +| | +----+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeIndexSeek | 4 | RANGE INDEX ws:PointOfInterest(name) WHERE name = $autostring_0 | 2 | 1 | 2 | 376 | 1/0 | 0.635 | In Pipeline 0 | ++-------------------------+----+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 431, total allocated memory: 2616 +---- + +This plan shows that a separate index is used to improve the performance of each `MATCH` clause (first by utilizing the index on the `name` property to find the `William Shakespeare` node, and then by using the index on the `lon` property to find all nodes with a greater longitudinal value). + + +[[indexes-and-null]] +== Indexes and `null` values + +Neo4j indexes do not store xref:values-and-types/working-with-null.adoc[`null`] values. +This means that the planner must be able to rule out the possibility of `null` values in order for queries to use an index. + +The following query demonstrates the incompatibility between `null` values and indexes by counting all `PointOfInterest` nodes with an unset `name` property: + +.Query to count nodes with a `null` `name` value +[source,cypher] +---- +PROFILE +ATCH (n:PointOfInterest) +WHERE n.name IS NULL +RETURN count(n) AS nodes +---- + +.Result +[role="queryresult",options="header,footer",cols="1*m"] +|=== +| nodes +| 3 +1+d|Rows:1 +|=== + +.Execution plan +---- ++-------------------+----+-------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+-------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | nodes | 1 | 1 | 0 | 0 | 0/0 | 0.012 | In Pipeline 1 | +| | +----+-------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(n) AS nodes | 1 | 1 | 0 | 32 | | | | +| | +----+-------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | n.name IS NULL | 141 | 3 | 373 | | | | | +| | +----+-------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 3 | n:PointOfInterest | 188 | 188 | 189 | 376 | 115/0 | 0.769 | Fused in Pipeline 0 | ++-------------------+----+-------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 562, total allocated memory: 472 +---- + +The plan shows that neither of the two available indexes (range and text) on the `name` property is used to solve the predicate. + +However, if a query predicate is added which is able to exclude the presence of any `null` values, then an index can be used. +The following query shows this by adding a substring predicate to the above query: + +.Query to count nodes with a `null` `name` value or nodes with a `name` property containing `'William'` +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.name IS NULL OR n.name CONTAINS 'William' +RETURN count(n) AS nodes +---- + +.Result +[role="queryresult",options="header,footer",cols="1*m"] +|=== +| nodes +| 5 +1+d|Rows:1 +|=== + +The query result now includes both the three nodes with an unset `name` value found in the previous query and the two nodes with a `name` value containing `William` (`William Shakespeare` and `William Tecumseh Sherman`). + +.Execution plan +---- ++--------------------------+----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------+----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | nodes | 1 | 1 | 0 | 0 | 0/0 | 0.010 | In Pipeline 3 | +| | +----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(n) AS nodes | 1 | 1 | 0 | 32 | | | | +| | +----+----------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Distinct | 2 | n | 141 | 5 | 0 | 352 | | | | +| | +----+----------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Union | 3 | | 142 | 5 | 0 | 352 | 0/0 | 0.220 | Fused in Pipeline 2 | +| |\ +----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +NodeIndexContainsScan | 4 | TEXT INDEX n:PointOfInterest(name) WHERE name CONTAINS $autostring_0 | 1 | 2 | 3 | 376 | 4/0 | 0.456 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Filter | 5 | n.name IS NULL | 141 | 3 | 373 | | | | | +| | +----+----------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 6 | n:PointOfInterest | 188 | 188 | 189 | 376 | 115/0 | 0.673 | Fused in Pipeline 0 | ++--------------------------+----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 565, total allocated memory: 1352 +---- + +This plan shows that an index is only used to solve the second part of the `WHERE` clause, which excludes the presence of `null` values. + +The presence of `null` values within an indexed property therefore does not negate the use of an index. +Index use is only negated if the planner is unable to rule out the inclusion of any unset properties in the matching process. + +The presence of `null` values may not be known in advance, and this can cause unexpected instances of indexes not being used. There are, however, a few strategies to ensure that an index will be used. + +[[property-existence-check]] +=== Property existence checks + +One method to ensure an index is used is to explicitly filter out any `null` values by appending `IS NOT NULL` to the queried property. +The following example uses the same query as above but exchanges `IS NULL` with `IS NOT NULL` in the `WHERE` clause: + +.Query to count `PointOfInterest` nodes without a `null` `name` value +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.name IS NOT NULL +RETURN count(n) AS nodes +---- + +.Result +[role="queryresult",options="header,footer",cols="1*m"] +|=== +| nodes +| 185 +1+d|Rows:1 +|=== + +.Execution plan +---- ++-------------------+----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | nodes | 1 | 1 | 0 | 0 | 0/0 | 0.013 | In Pipeline 1 | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(n) AS nodes | 1 | 1 | 0 | 32 | | | | +| | +----+------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexScan | 2 | RANGE INDEX n:PointOfInterest(name) WHERE name IS NOT NULL | 185 | 185 | 186 | 376 | 0/1 | 0.691 | Fused in Pipeline 0 | ++-------------------+----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 186, total allocated memory: 472 +---- + +This plan shows that the previously created range index on the `name` property is now used to solve the predicate. + +[[text-indexes-type-predicate-expressions]] +=== Text indexes and type predicate expressions + +Text indexes require that predicates only include `STRING` properties. + +To use text indexes in situations where any of the queried properties may be either of an incompatible type or `null` rather than a `STRING` value, add the type predicate expression `IS {two-colons} STRING NOT NULL` (or its alias, introduced in Neo4j 5.14, `IS {two-colons} STRING!`) to the query. +This will enforce both the existence of a property and its `STRING` type, discarding any rows where the property is missing or not of type `STRING`, and thereby enable the use of text indexes. + +For example, if the `WHERE` predicate in the previous query is altered to instead append `IS {two-colons} STRING NOT NULL`, then the text index rather than the range index is used (range indexes do not support type predicate expressions): + +.Query using a type predicate expression +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.name IS :: STRING NOT NULL +RETURN count(n) AS nodes +---- + +.Execution plan +---- ++-------------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | nodes | 1 | 1 | 0 | 0 | 0/0 | 0.009 | In Pipeline 1 | +| | +----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(n) AS nodes | 1 | 1 | 0 | 32 | | | | +| | +----+-----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexScan | 2 | TEXT INDEX n:PointOfInterest(name) WHERE name IS NOT NULL | 185 | 185 | 186 | 376 | 0/0 | 0.343 | Fused in Pipeline 0 | ++-------------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 186, total allocated memory: 472 +---- + +[TIP] +While type predicate expressions were introduced in Neo4j 5.9, the `IS {two-colons} STRING NOT NULL` syntax only became an index-compatible predicate in Neo4j 5.15. +For more information, see the page about xref:values-and-types/type-predicate.adoc[type predicate expressions]. + +The xref:functions/string.adoc#functions-tostring[`toString`] function can also be used to convert an expression to `STRING` values, and thereby help the planner to select a text index. + +[[type-constraints]] +=== Type constraints + +_This feature was introduced in Neo4j 5.11._ + +For indexes that are compatible only with specific types (i.e. text and point indexes), the Cypher planner needs to deduce that a predicate will evaluate to `null` for non-compatible values in order to use the index. +If a predicate is not explicitly defined as the required type (`STRING` or `POINT`), this can lead to situations where a text or point index is not used. + +Since xref:constraints/examples.adoc#constraints-examples-node-property-type[type constraints] guarantee that a property is always of the same type, they can be used to extend the scenarios in which text and point indexes are compatible with a predicate. + +To show this, the following example will first drop the existing range index on the `name` property (this is necessary because type constraints only extend the compatibility of type-specific indexes - range indexes are not limited by a value type). +It will then run the same query with a `WHERE` predicate on the `name` property (for which there exists a previously created text index) before and after creating a type constraint, and compare the resulting execution plans. + +.Drop range index +[source,cypher] +---- +DROP INDEX range_index_name +---- + +.Query to count `PointOfInterest` nodes without a `null` `name` value +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.name IS NOT NULL +RETURN count(n) AS nodes +---- + +.Execution plan +---- ++------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name | 187 | 185 | 0 | 0 | | | | +| | +----+---------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[n.name] AS name | 187 | 185 | 0 | | | | | +| | +----+---------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | cache[n.name] IS NOT NULL | 187 | 185 | 373 | | | | | +| | +----+---------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 3 | n:PointOfInterest | 188 | 188 | 189 | 248 | 119/0 | 1.004 | Fused in Pipeline 0 | ++------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 562, total allocated memory: 312 +---- + +This plan shows that the available text index on the `name` property was not used to solve the predicate. +This is because the planner was not able to deduce that all `name` values are of type `STRING`. + +However, if a type constraint is created to ensure that all `name` properties have a `STRING` value, a different query plan is generated. + +.Create `STRING` type constraint on the `name` property +[source,cypher] +---- +CREATE CONSTRAINT type_constraint +FOR (n:PointOfInterest) REQUIRE n.name IS :: STRING +---- + +.Rerun the query after the creation of a type constraint +[source,cypher] +---- +PROFILE +MATCH (n:PointOfInterest) +WHERE n.name IS NOT NULL +RETURN count(n) AS nodes +---- + +.Execution plan +---- ++-----------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | name | 187 | 185 | 0 | 0 | | | | +| | +----+-----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | n.name AS name | 187 | 185 | 370 | | | | | +| | +----+-----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexScan | 2 | TEXT INDEX n:PointOfInterest(name) WHERE name IS NOT NULL | 187 | 185 | 186 | 248 | 115/0 | 1.671 | Fused in Pipeline 0 | ++-----------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 556, total allocated memory: 312 +---- + +Because of the type constraint on the `name` property, the planner is now able to deduce that all `name` properties are of type `STRING`, and therefore use the available text index. + +Point indexes can be extended in the same way if a type constraint is created to ensure that all properties are `POINT` values. + +Note that xref:constraints/examples.adoc#constraints-examples-node-property-existence[property existence constraints] do not currently leverage index use in the same way. + +[[Heuristics]] +== Heuristics: deciding what to index + +While it is impossible to give exact directions on when a search-performance index might be beneficial for a particular use-case, the following points provide some useful heuristics for when creating an index might improve query performance: + +* *Frequent property-based queries*: if some properties are used frequently for filtering or matching, consider creating an index on them. +* *Performance optimization*: If certain queries are too slow, re-examine the properties that are filtered on, and consider creating indexes for those properties that may cause bottlenecking. +* *High cardinality properties*: high cardinality properties have many distinct values (e.g., unique identifiers, timestamps, or user names). Queries that seek to retrieve such properties will likely benefit from indexing. +* *Complex queries*: if queries traverse complex paths in a graph (for example, by involving multiple hops and several layers of filtering), adding indexes to the properties used in those queries can improve query performance. +* *Experiment and test*: It is good practice to experiment with different indexes and query patterns, and to measure the performance of critical queries with and without different indexes to evaluate their effectiveness. + +[[over-indexing]] +== Over-indexing: considerations and solutions + +Search-performance indexes can significantly improve query performance. +They should, however, be used judiciously for the following reasons: + +* *Storage space*: because each index is a secondary copy of the data in the primary database, each index essentially doubles the amount of storage space occupied by the indexed data. +* *Slower write queries*: adding indexes impacts the performance of write queries. +This is because indexes are updated with each write query. If a system needs to perform a lot of writes quickly, it may be counterproductive to have an index on the affected data entities. +In other words, if write performance is crucial for a particular use case, it may be beneficial to only add indexes where they are necessary for read purposes. + +As a result of these two points, deciding what to index (and what not to index) is an important and non-trivial task. + +[[tracking-index-use]] +=== Keeping track of index-use: `lastRead`, `readCount`, and `trackedSince` + +Unused indexes take up unnecessary storage space and it may be beneficial to remove them. +Knowing which indexes are most frequently used by the queries against a database can, however, be difficult. +As of Neo4j 5.8, there are three relevant columns returned by the xref:indexes/search-performance-indexes/managing-indexes.adoc#list-indexes[`SHOW INDEX`] command which can help identify redundant indexes: + +* *`lastRead`*: returns the last time the index was used for reading. +* *`readCount`*: returns the number of read queries issued to the index. +* *`trackedSince`* returns the time when usage statistics tracking started for an index.footnote:[The `trackedSince` column is not part of the default return columns for the `SHOW INDEXES` command. To return this and all other non-default columns, use `SHOW INDEXES YIELD *`. +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#listing-indexes-result-columns[Managing search-performance indexes -> Result columns for listing indexes].] + +To return these values (along with other relevant information) for the indexes in a database, run the following query: + +.Query to identify redundant indexes +[source,cypher] +---- +SHOW INDEX YIELD name, type, entityType, labelsOrTypes, properties, lastRead, readCount, trackedSince +---- + +If any unused indexes are identified, it may be beneficial to delete them using the xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-indexes[`DROP INDEX`] command. + +[[summary]] +== Summary + +* Range indexes can be used to solve most predicates. + +* Text indexes are used over range indexes for `CONTAINS` and `ENDS WITH` predicates on `STRING` properties, and if the queried `STRING` properties exceed 8 kb. + +* Point indexes are used when queries filter on distances and bounding boxes. + +* Token lookup indexes only solve node label and relationship type predicates. +They do not solve any property predicates. +Deleting token lookup indexes will negatively impact query performance. + +* Composite indexes are only used if the query filters on all properties indexed by the composite index. +The order in which the properties are defined when creating a composite index impacts how the planner solves query predicates. + +* Queries ordering results using `ORDER BY` can leverage the pre-existing order in range indexes and thereby improve query performance. + +* A Cypher query can use several indexes if the planner deems it beneficial to the performance of a query. + +* * Neo4j indexes do not store `null` values, and the planner must be able to rule out any entities with properties containing `null` values in order to use an index. +There are several strategies to ensure the use of indexes. + +* The columns `lastRead`, `readCount`, and `trackedSince` returned by the `SHOW INDEX` command can be used to identify redundant indexes that take up unnecessary space. diff --git a/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc b/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc new file mode 100644 index 000000000..769ed3cf6 --- /dev/null +++ b/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc @@ -0,0 +1,394 @@ +:description: Information about using full-text indexes in Neo4j. += Full-text indexes + +A full-text index is used to index nodes and relationships by `STRING` properties. +Unlike xref:indexes/search-performance-indexes/managing-indexes.adoc#indexes-create-range-index[range] and xref:indexes/search-performance-indexes/managing-indexes.adoc#indexes-create-text-index[text] indexes, which can only perform limited `STRING` matching (exact, prefix, substring, or suffix matches), full-text indexes stores individual words in any given `STRING` property. +This means that full-text indexes can be used to match within the _content_ of a `STRING` property. +Full-text indexes also return a score of proximity between a given query string and the `STRING` values stored in the database, thus enabling them to semantically interpret data. + +Full-text indexes are powered by the link:https://lucene.apache.org/[Apache Lucene] indexing and search library. + +[[example-graph]] +== Example graph + +The following graph is used for the examples below: + +image::full_text_graph.svg[width="700",role="middle"] + +To recreate it, run the following query against an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE (nilsE:Employee {name: "Nils-Erik Karlsson", position: "Engineer", team: "Kernel", peerReviews: ['Nils-Erik is difficult to work with.', 'Nils-Erik is often late for work.']}), +(lisa:Manager {name: "Lisa Danielsson", position: "Engineering manager"}), +(nils:Employee {name: "Nils Johansson", position: "Engineer", team: "Operations"}), +(maya:Employee {name: "Maya Tanaka", position: "Senior Engineer", team:"Operations"}), +(lisa)-[:REVIEWED {message: "Nils-Erik is reportedly difficult to work with."}]->(nilsE), +(maya)-[:EMAILED {message: "I have booked a team meeting tomorrow."}]->(nils) +---- + +[[create-full-text-indexes]] +== Create full-text indexes + +Full-text indexes are created with the `CREATE FULLTEXT INDEX` command. +An index can be given a unique name when created, which is used to reference the index when querying or dropping it. +If no name is given when created, a random name will be assigned to the full-text index. +When creating a full-text index, you need to specify the labels/relationship types and property names it should apply to. + +[TIP] +Creating a full-text index requires the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[`CREATE INDEX` privilege]. + +This statement creates a full-text index named `namesAndTeams` on each `name` and `team` property for nodes with the label `Employee` or `Manager`: + +.Create a full-text index on a node label and property combination +[source, cypher] +---- +CREATE FULLTEXT INDEX namesAndTeams FOR (n:Employee|Manager) ON EACH [n.name, n.team] +---- + +This query highlights two key differences between full-text and xref:indexes/search-performance-indexes/managing-indexes.adoc[search-performance indexes]: + +* Full-text indexes can be applied to more than one node label. +* Full-text indexes can be applied to more than one property, but unlike xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-composite-range-index-for-nodes[composite search-performance indexes], a full-text index stores entities that have _at least_ one of the indexed labels or relationship types, and _at least_ one of the indexed properties. + +Similarly, though a relationship can have only one type, a full-text index can store multiple relationship types. +In that case, all types matching _at least one_ of the relationship types and _at least one_ of the indexed properties will be included. + +This statement creates a full-text index named `communications` on the `message` property for the relationship types `REVIEWED` and `EMAILED`: + +.Create a full-text index on a relationship type and property combination +[source, cypher] +---- +CREATE FULLTEXT INDEX communications FOR ()-[r:REVIEWED|EMAILED]-() ON EACH [r.message] +---- + +Full-text indexes follow the same xref:indexes/index.adoc#naming-rules-and-recommendations[naming rules and best-practices] as search-performance indexes. + +[[tokenization-analyzers]] +=== Tokenization and analyzers + +Full-text indexes store individual words in a `STRING` property. +This is achieved by _tokenizer_, which breaks up a stream of characters into individual tokens (usually individual words). +How a `STRING` is tokenized is determined by what _analyzer_ the full-text index is configured with. +The default analyzer (`standard-no-stop-words`) analyzes both the indexed values and the query string. + +[NOTE] +==== +Stop words are common words in a language that can be filtered out during +information retrieval tasks since they are considered to be of little use when determining the meaning of a string. These words are typically short and frequently used across various contexts. + +For example, the following stop words are included in Lucene’s english analyzer: "a", "an", "and", "are", "as", "at", "be", "but”, and so on. + +Removing stop words can help reduce the size of stored data and thereby improve the efficiency of data retrieval. +==== + +In some cases, using different analyzers for the indexed values and query string is more appropriate. +For example, if handling `STRING` values written in Swedish, it may be beneficial to select the _swedish_ analyzer, which knows how to tokenize Swedish words, and will avoid indexing Swedish stop words. + +A complete list of all available analyzers is included in the result of the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_fulltext_listavailableanalyzers[`db.index.fulltext.listAvailableAnalyzers`] procedure. + +Neo4j also supports the use of custom analyzers. +For more information, see the link:{neo4j-docs-base-uri}/java-reference/{page-version}/extending-neo4j/full-text-analyzer-provider[Java Reference Manual -> Full-text index analyzer providers]. + +[[configuration-settings]] +=== Configuration settings + +The `CREATE FULLTEXT INDEX` command takes an optional `OPTIONS` clause, where the `indexConfig` can be specified. +The following statement creates a full-text index using a parameter for nodes with the label `Employee` or `Manager`. +Creating and dropping indexes using parameters was introduced in Neo4j 5.16. + +.Parameters +[source,javascript, indent=0] +---- +{ + "name": "peerReviews" +} +---- + +.Create a full-text index using `OPTIONS` +[source, cypher] +---- +CREATE FULLTEXT INDEX $name FOR (n:Employee|Manager) ON EACH [n.peerReviews] +OPTIONS { + indexConfig: { + `fulltext.analyzer`: 'english', // <1> + `fulltext.eventually_consistent`: true // <2> + } +} +---- + +<1> The `fulltext.analyzer` setting can be used to configure an index-specific analyzer. +In this case, it is set to the `english` analyzer. +The possible values for the `fulltext.analyzer` setting can be listed with the `db.index.fulltext.listAvailableAnalyzers` procedure. +<2> The `fulltext.eventually_consistent` setting, if set to `true`, will put the index in an _eventually consistent_ update mode. +This means that updates will be applied in a background thread "as soon as possible", instead of during a transaction commit, which is true for other indexes. + +For more information on how to configure full-text indexes, refer to the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/performance/index-configuration#index-configuration-fulltext[Operations Manual -> Indexes to support full-text search]. + +[[query-full-text-indexes]] +== Query full-text indexes + +To query a full-text index, use either the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_fulltext_querynodes[`db.index.fulltext.queryNodes`] or the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/reference/procedures/#procedure_db_index_fulltext_relationships[`db.index.fulltext.queryRelationships`] procedure. + +[NOTE] +==== +Unlike other xref:indexes/search-performance-indexes/managing-indexes.adoc[search-performance indexes], full-text indexes are not automatically used by the xref:planning-and-tuning/execution-plans.adoc[Cypher query planner]. +To access full-text indexes, they must be explicitly called with the above-mentioned procedures. +==== + +This query uses the `db.index.fulltext.queryNodes` to look for `nils` in the previously created full-text index `namesAndTeams`: + +.Query a full-text index for a node property +[source, cypher] +---- +CALL db.index.fulltext.queryNodes("namesAndTeams", "nils") YIELD node, score +RETURN node.name, score +---- + +.Result +[role="queryresult",options="header,footer",cols="2*:` to the query string. + +.Query a full-text index for specific properties +[source, cypher] +---- +CALL db.index.fulltext.queryNodes("namesAndTeams", 'team:"Operations"') YIELD node, score +RETURN node.name, node.team, score +---- + +.Result +[role="queryresult",options="header,footer",cols="3* Result columns for listing indexes]. + + +[[drop-full-text-indexes]] +== Drop full-text indexes + +A full-text node index is dropped by using the xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-an-index[same command as for other indexes], `DROP INDEX`. + +In the following example, the previously created `communications` full-text index is deleted from the database: + +.Drop a full-text index +[source, cypher] +---- +DROP INDEX communications +---- + +As of Neo4j 5.16, the index name can also be given as a parameter when dropping an index: `DROP INDEX $name`. + +[[full--text-index-procedures]] +== List of full-text index procedures + +The procedures for full-text indexes are listed in the table below: + +[options="header"] +|=== +| Usage | Procedure/Command | Description + +| Eventually consistent indexes. +| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_awaiteventuallyconsistentindexrefresh[`db.index.fulltext.awaitEventuallyConsistentIndexRefresh`] +| Wait for the updates from recently committed transactions to be applied to any eventually-consistent full-text indexes. + +| List available analyzers. +| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_listavailableanalyzers[`db.index.fulltext.listAvailableAnalyzers`] +| List the available analyzers that the full-text indexes can be configured with. + +| Use full-text node index. +| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_querynodes[`db.index.fulltext.queryNodes`] +| Query the given full-text index. Returns the matching nodes and their Lucene query score, ordered by score. + +| Use full-text relationship index. +| https://neo4j.com/docs/operations-manual/current/reference/procedures/#procedure_db_index_fulltext_queryrelationships[`db.index.fulltext.queryRelationships`] +| Query the given full-text index. Returns the matching relationships and their Lucene query score, ordered by score. + +|=== + +[[summary]] +== Summary + +* Full-text indexes support the indexing of both nodes and relationships. +* Full-text indexes include only property values of types `STRING` or `LIST`. +* Full-text indexes are accessed via Cypher procedures. +* Full-text indexes return the _score_ for each result from a query. +* Full-text indexes support configuring custom analyzers, including analyzers that are not included with Lucene itself. +* Full-text indexes can be queried using the Lucene query language. +* Full-text indexes are kept up to date automatically, as nodes and relationships are added, removed, and modified. +* Full-text indexes will automatically populate newly created indexes with the existing data in a store. +* Full-text indexes can be checked by the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/tools/neo4j-admin/consistency-checker[consistency checker], and they can be rebuilt if there is a problem with them. +* Newly created full-text indexes get automatically populated with the existing data in the database. +* Full-text indexes can support any number of properties in a single index. +* Full-text indexes are created, dropped, and updated transactionally, and are automatically replicated throughout a cluster. +* Full-text indexes can be configured to be _eventually consistent_, in which index updating is moved from the commit path to a background thread. +Using this feature, it is possible to work around the slow Lucene writes from the performance-critical commit process, thus removing the main bottlenecks for Neo4j write performance. \ No newline at end of file diff --git a/modules/ROOT/pages/indexes/semantic-indexes/overview.adoc b/modules/ROOT/pages/indexes/semantic-indexes/overview.adoc new file mode 100644 index 000000000..f9d2b262f --- /dev/null +++ b/modules/ROOT/pages/indexes/semantic-indexes/overview.adoc @@ -0,0 +1,14 @@ +:description: Overview of the semantic indexes available in Neo4j. += Semantic indexes + +Unlike search-performance indexes, semantic indexes capture the semantic meaning or context of the data in a database. +This is done by returning an approximation score, which indicates the similarity between a query string and the data in a database. + +Two semantic indexes are available in Neo4j: + +* xref:indexes/semantic-indexes/full-text-indexes.adoc[Full-text indexes]: enables searching within the content of `STRING` properties and for similarity comparisons between query strings and `STRING` values stored in the database. +* xref:indexes/semantic-indexes/vector-indexes.adoc[Vector indexes]: enables similarity searches and complex analytical queries by representing nodes or properties as vectors in a multidimensional space. + +[NOTE] +Unlike search-performance indexes, semantic indexes are not automatically used by the xref:planning-and-tuning/execution-plans.adoc[Cypher planner]. +To use semantic indexes, they must be explicitly called with specific procedures. diff --git a/modules/ROOT/pages/indexes-for-vector-search.adoc b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc similarity index 94% rename from modules/ROOT/pages/indexes-for-vector-search.adoc rename to modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc index c2af0708b..a37e482a8 100644 --- a/modules/ROOT/pages/indexes-for-vector-search.adoc +++ b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc @@ -1,4 +1,4 @@ -:description: This chapter describes how to use vector indexes to perform approximate nearest neighbor search. +:description: Information about how to use vector indexes to perform approximate nearest neighbor search in Neo4j. :fn-hnsw: footnote:hnsw[http://dx.doi.org/10.1109/TPAMI.2018.2889473[Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs] -- Yury A. Malkov and Dmitry A. Yashunin (preprint: https://arxiv.org/abs/1603.09320:[arXiv:1603.09320])] :fn-ieee-754: footnote:ieee-754[https://doi.org/10.1109/IEEESTD.2019.8766229[IEEE Standard for Floating-Point Arithmetic]] @@ -15,8 +15,6 @@ _Vector search indexes were released as a public beta in Neo4j 5.11 and general availability in Neo4j 5.13._ -This chapter describes how to use vector indexes to perform an approximate nearest neighbor search. - Vector indexes allow users to query vector embeddings from large datasets. An embedding is a numerical representation of a data object, such as text, image, audio, or document. @@ -49,13 +47,12 @@ The procedures and commands for vector indexes are listed in the following table | Create vector index. | `+CREATE VECTOR INDEX ...+` | label:new[Introduced in 5.15] Create a vector index for the specified label and property with the given vector dimensionality using the given similarity function. -See the xref:indexes-for-search-performance.adoc#indexes-create-indexes[`CREATE INDEX`] command for more details. Replaces {link-procedures-reference}#procedure_db_index_vector_createNodeIndex[`db.index.vector.createNodeIndex`]. +See the xref:indexes/search-performance-indexes/managing-indexes.adoc#create-indexes[`CREATE INDEX`] command for more details. Replaces {link-procedures-reference}#procedure_db_index_vector_createNodeIndex[`db.index.vector.createNodeIndex`]. | Create vector index. | {link-procedures-reference}#procedure_db_index_vector_createNodeIndex[`db.index.vector.createNodeIndex`] | It is replaced by `CREATE VECTOR INDEX ...`. - | Use vector index. | {link-procedures-reference}#procedure_db_index_vector_queryNodes[`db.index.vector.queryNodes`] | Query the given vector index. @@ -63,11 +60,11 @@ Returns the requested number of approximate nearest neighbor nodes and their sim | Drop vector index. | `+DROP INDEX index_name+` -| Drop the specified index, see the xref:indexes-for-search-performance.adoc#indexes-drop-indexes[`DROP INDEX`] command for details. +| Drop the specified index, see the xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-indexes[`DROP INDEX`] command for details. | Listing all vector indexes. | `SHOW VECTOR INDEXES` -| label:new[Introduced in 5.15] Lists all vector indexes, see the xref:indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES`] command for details. +| label:new[Introduced in 5.15] Lists all vector indexes, see the xref:indexes/search-performance-indexes/managing-indexes.adoc#indexes-list-indexes[`SHOW INDEXES`] command for details. | Set vector property. | {link-procedures-reference}#procedure_db_create_setNodeVectorProperty[`db.create.setNodeVectorProperty`] @@ -295,7 +292,9 @@ If the query vector itself is not wanted, you can use `WHERE score < 1` to remov [[indexes-vector-drop]] == Drop vector indexes -A vector index is dropped by using the xref:indexes-for-search-performance.adoc#indexes-drop-an-index[same command as for other indexes], `DROP INDEX`. +A vector index is dropped by using the xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-an-index[same command as for other indexes], `DROP INDEX`. + +Dropping a vector index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. Dropping a vector index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. @@ -473,7 +472,7 @@ db.index.vector.createNodeIndex(indexName :: STRING, label :: STRING, propertyKe ==== | Neo4j 5.15 -| The standard index type filtering for xref:indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES`] command is missing. +| The standard index type filtering for xref:indexes/search-performance-indexes/managing-indexes.adoc#list-indexes[`SHOW INDEXES`] command is missing. [TIP] ==== @@ -544,7 +543,7 @@ It is _recommended_ to normalize your vectors (if needed), and use a xref:indexe ==== | Neo4j 5.12 -| The vector index `createStatement` field from xref:indexes-for-search-performance.adoc#indexes-list-indexes[`SHOW INDEXES`] does not correctly escape single quotes in index names, labels, and property keys. +| The vector index `createStatement` field from xref:indexes/search-performance-indexes/managing-indexes.adoc#list-indexes[`SHOW INDEXES`] does not correctly escape single quotes in index names, labels, and property keys. | Neo4j 5.12 | {link-operations-manual}/backup-restore/copy-database/[Copying a database store] with a vector index does not log the recreation command, and instead logs an error: diff --git a/modules/ROOT/pages/indexes/syntax.adoc b/modules/ROOT/pages/indexes/syntax.adoc new file mode 100644 index 000000000..777ef49de --- /dev/null +++ b/modules/ROOT/pages/indexes/syntax.adoc @@ -0,0 +1,277 @@ +:description: Syntax for creating, listing, querying and dropping indexes in Neo4j. += Syntax + +This page contains the syntax for creating, listing, and dropping the indexes available in Neo4j. +It also contains the signatures for the procedures necessary to call in order to use full-text and vector indexes. + +[[create-index]] +== CREATE INDEX + +The general structure of the `CREATE INDEX` command is: + +[source, syntax] +---- +CREATE [index_type] INDEX [index_name] [IF NOT EXISTS] +FOR {node_pattern | relationship_pattern} +ON property_or_token_lookup_pattern +[OPTIONS “{“ option: value[, …] “}”] +---- + +The `CREATE … INDEX …` command is optionally idempotent. +This means that its default behavior is to throw an error if an attempt is made to create an index with the same name twice. +With `IF NOT EXISTS`, no error is thrown and nothing happens should an index with the same name or same schema and index type already exist (it may still throw an error if conflicting constraints exist, such as constraints with the same name or with the same schema and backing index). + +The index name must be unique among both indexes and constraints. +A random name will be assigned if no name is explicitly given when an index is created. + +Index providers and configuration settings can be specified using the `OPTIONS` clause. +However, not all indexes have available configuration settings or multiple providers. +In those cases, nothing needs to be specified and the `OPTIONS` map should be omitted from the query. + +[TIP] +Creating an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `CREATE INDEX` privilege]. + +[[create-range-index]] +=== Range indexes + +Range indexes have only one index provider, `range-1.0`, and no supported index configuration. +Since the index provider will be assigned by default, the `OPTIONS` map has been omitted from the syntax below. + +.Create a range index for a node label, either on a single property or composite +[source,syntax] +---- +CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName_1[, + n.propertyName_2, + ... + n.propertyName_n]) +---- + +.Create a range index for a relationship type, either on a single property or composite +[source,syntax] +---- +CREATE [RANGE] INDEX [index_name] [IF NOT EXISTS] +FOR ()-”[“r:TYPE_NAME”]”-() +ON (r.propertyName_1[, + r.propertyName_2, + ... + r.propertyName_n]) +---- + +[[create-text-index]] +=== Text indexes + +.Create a text index for a node label on a single property +[source,syntax] +---- +CREATE TEXT INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName_1) +[OPTIONS “{“ option: value[, …] “}”] +---- + +.Create a text index for a relationship type on a single property +[source,syntax] +---- +CREATE TEXT INDEX [index_name] [IF NOT EXISTS] +FOR ()-”[“r:TYPE_NAME”]”-() +ON (r.propertyName_1) +[OPTIONS “{“ option: value[, …] “}”] +---- + +Text indexes have no supported index configuration and, as of Neo4j 5.1, they have two index providers available, `text-2.0` (default) and `text-1.0` (deprecated). + +[NOTE] +It is not possible to create composite text indexes on multiple properties. + +[[create-point-index]] +=== Point indexes + +.Create a point index for a node label on a single property +[source, syntax] +---- +CREATE POINT INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName_1) +[OPTIONS “{“ option: value[, …] “}”] +---- + +[source, syntax] +.Create a point index for a relationship type on a single property +---- +CREATE POINT INDEX [index_name] [IF NOT EXISTS] +FOR ()-”[“r:TYPE_NAME”]”-() +ON (r.propertyName_1) +[OPTIONS “{“ option: value[, …] “}”] +---- + +Point indexes have only one index provider available, `point-1.0`. +The following settings can be specified for point indexes: + +* `spatial.cartesian.min` +* `spatial.cartesian.max` +* `spatial.cartesian-3d.min` +* `spatial.cartesian-3d.max` +* `spatial.wgs-84.min` +* `spatial.wgs-84.max` +* `spatial.wgs-84-3d.min` +* `spatial.wgs-84-3d.max` + +[NOTE] +It is not possible to create composite point indexes on multiple properties. + + +[[create-lookup-index]] +=== Token lookup indexes + +Token lookup indexes have only one index provider, `token-lookup-1.0`, and no supported index configuration. +Since the index provider will be assigned by default, the `OPTIONS` map has been omitted from the syntax below. + +.Create a node label lookup index +[source,syntax] +---- +CREATE LOOKUP INDEX [index name] [IF NOT EXISTS] +FOR (n) +ON EACH labels(n) +---- + +.Create a relationship type lookup index +[source,syntax] +---- +CREATE LOOKUP INDEX [index name] [IF NOT EXISTS] +FOR ()-”[“r”]”-() +ON [EACH] type(r) +---- + +Two token lookup indexes are present by default when creating a Neo4j database, and only one node label lookup index and one relationship type lookup index can exist at the same time. + +[[create-full-text-index]] +=== Full-text indexes + +.Create a full-text index for one or more node labels, either on a single property or multiple properties +[source,syntax] +---- +CREATE FULLTEXT INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName[“|” …]) +ON EACH “[“ n.propertyName[, ...] “]” +[OPTIONS “{“ option: value[, …] “}”] +---- + +.Create a full-text index for one or more relationship types, either on a single property or multiple properties +[source,syntax] +---- +CREATE FULLTEXT INDEX [index_name] [IF NOT EXISTS] +FOR ()-”[“r:TYPE_NAME[“|” ...]”]”-() +ON EACH “[“ r.propertyName[, ...] “]” +[OPTIONS “{“ option: value[, …] “}”] +---- + +Full-text indexes have only one index provider available, `fulltext-1.0`. +The following settings can be specified for full-text indexes: + +* `fulltext.analyzer` - specifies what analyzer to use (the `db.index.fulltext.listAvailableAnalyzers` procedure lists what analyzers are available). +* `fulltext.eventually_consistent` - specifies whether a full-text index is eventually consistent. +If set to `true`, it will ensure that updates from committing transactions are applied in a background thread. + +[[create-vector-index]] +=== Vector indexes + +.Create a vector index for a node label on a single property +[source,syntax] +---- +CREATE VECTOR INDEX [index_name] [IF NOT EXISTS] +FOR (n:LabelName) +ON (n.propertyName) +[OPTIONS “{“ option: value[, …] “}”] +---- + +Vector indexes have only one index provider available, `vector-1.0`. +The `OPTIONS` clause is mandatory when creating a vector index, because it is necessary to configure the `vector.dimensions` and `vector.similarity_function` settings: + +[source,syntax] +---- +OPTIONS { + indexConfig: { + `vector.dimensions`: $dimension, + `vector.similarity_function`: $similarityFunction + } +} +---- + +[NOTE] +It is not possible to create composite vector indexes on multiple properties. + +[[list-index]] +== SHOW INDEX + +[TIP] +Listing indexes requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `SHOW INDEX` privilege]. + +.List indexes in the database (either all or filtered on index type) +[source, syntax] +---- +SHOW [ALL | FULLTEXT | LOOKUP | POINT | RANGE | TEXT | VECTOR] INDEX[ES] +[YIELD { * | field[,...] } [ORDER BY field[,...]] [SKIP n] [LIMIT n]] +[WHERE expression] +[RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] +---- + +When using the `RETURN` clause, the `YIELD` clause is mandatory. + +[[query-semantic-indexes]] +== Query semantic indexes + +Neo4j’s semantic indexes are not used automatically in Cypher queries. +To use them, specific procedures must be called. Their signatures can be seen below. + +[[query-full-text-index]] +=== Full-text indexes + +.Query full-text index on nodes: db.index.fulltext.queryNodes +[source,syntax] +---- +CALL db.index.fulltext.queryNodes(indexName :: STRING, queryString :: STRING, options = {} :: MAP) +---- + +.Query full-text index on relationships: db.index.fulltext.queryRelationships +[source, syntax] +---- +CALL db.index.fulltext.queryRelationships(indexName :: STRING, queryString :: STRING, options = {} :: MAP) +---- + +The valid _key: value_ pairs for the `options` map are: + +* skip:  — skip the top N results. +* limit:  — limit the number of results returned. +* analyzer:  — use the specified analyzer as a search analyzer for this query. + +The `options` map and all of the keys are optional. + +[[query-vector-index]] +=== Vector indexes + +.Query vector-text index on nodes: db.index.vector.queryNodes +[source,syntax] +---- +CALL db.index.vector.queryNodes(indexName :: STRING, numberOfNearestNeighbours :: INTEGER, query :: LIST) +---- + +The `numberOfNearestNeighbours` refers to the number of nearest neighbors to return as the neighborhood. +The `query` vector refers to the `LIST` in which to search for the neighborhood. + +[[drop-index]] +== DROP INDEX + +The `DROP INDEX` command is optionally idempotent. +This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. +With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. + +[TIP] +Dropping indexes requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. + +.Drop an index of any index type +[source,syntax] +---- +DROP INDEX index_name [IF EXISTS] +---- diff --git a/modules/ROOT/pages/planning-and-tuning/index.adoc b/modules/ROOT/pages/planning-and-tuning/index.adoc index c52566ed2..f62aad7eb 100644 --- a/modules/ROOT/pages/planning-and-tuning/index.adoc +++ b/modules/ROOT/pages/planning-and-tuning/index.adoc @@ -9,8 +9,9 @@ More information about each of these topics can be found in the following sectio * xref:planning-and-tuning/execution-plans.adoc[] * xref:planning-and-tuning/operators/index.adoc[] * xref:planning-and-tuning/runtimes/index.adoc[] -* xref:planning-and-tuning/query-tuning/index.adoc[] +* xref:planning-and-tuning/query-tuning.adoc[] +[[profile-and-explain]] == Note on `PROFILE` and `EXPLAIN` The queries in this section are often prepended with either `PROFILE` or `EXPLAIN`. diff --git a/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc b/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc new file mode 100644 index 000000000..46497fa9a --- /dev/null +++ b/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc @@ -0,0 +1,304 @@ +:description: Query tuning for the Cypher query language. +[[query-tuning]] += Query tuning + +Neo4j aims to execute queries as fast as possible. +However, when optimizing for maximum query execution performance, it may be helpful to rephrase queries using knowledge about the domain and the application. + +This page contains information about how to tune queries using different strategies. + +For information about changing the runtime of a query, see the page about xref:planning-and-tuning/runtimes/concepts.adoc[Cypher runtime concepts]. + +== General recommendations + +The overall goal of manual query performance optimization is to ensure that only necessary data is retrieved from the graph. + +Queries should aim to filter data as early as possible in order to reduce the amount of work that has to be done in the later stages of query execution. +This also applies to what gets returned: returning whole nodes and relationships ought to be avoided in favour of selecting and returning only the data that is needed. +You should also make sure to set an upper limit on variable length patterns, so they don't cover larger portions of the dataset than needed. + +Each Cypher query gets optimized and transformed into an xref::planning-and-tuning/execution-plans.adoc[execution plan] by the Cypher query planner. +To minimize the resources used for this, try to use parameters instead of literals when possible. +This allows Cypher to re-use queries instead of having to parse and build new execution plans. + +To read more about the execution plan operators mentioned in this section, see xref::planning-and-tuning/operators/index.adoc[]. + +== Query options + +Query execution can be fine-tuned through the use of query options. + +In order to use one or more of these options, the query must be prepended with `CYPHER`, followed by the query option(s), as exemplified thus: + +[source, syntax, role="noheader"] +---- +CYPHER query-option [further-query-options] query +---- + +For information about the various runtimes available in Cypher, see xref:planning-and-tuning/runtimes/index.adoc[]. + +[[cypher-planner]] +=== Cypher planner + +The Cypher planner takes a Cypher query and computes an execution plan that solves it. +For any given query there is likely a number of execution plan candidates that each solve the query in a different way. +The planner uses a search algorithm to find the execution plan with the lowest estimated execution cost. + +This table describes the available planner options: + +[options="header",cols="2m,3a,^1a"] +|=== +| Query option | Description | Default + +| planner=cost +| Use cost based planning with default limits on plan search space and time. +| {check-mark} + +| planner=idp +| Synonym for `planner=cost`. +| + +| planner=dp +| +Use cost based planning without limits on plan search space and time to perform an exhaustive search for the best execution plan. + +[NOTE] +==== +Using this option can significantly _increase_ the planning time of the query. +==== + +| + +|=== + + +[[cypher-connect-components-planner]] +=== Cypher connect-components planner label:deprecated[] + +One part of the Cypher planner is responsible for combining sub-plans for separate patterns into larger plans - a task referred to as _connecting components_. + +This table describes the available query options for the connect-components planner: + +[options="header",cols="2m,3a,^1a"] +|=== +| Query option | Description | Default + +| connectComponentsPlanner=greedy +| +Use a greedy approach when combining sub-plans. + +[NOTE] +==== +Using this option can significantly _reduce_ the planning time of the query. +==== +| + +| connectComponentsPlanner=idp +| +Use the cost based IDP search algorithm when combining sub-plans. + +[NOTE] +==== +Using this option can significantly _increase_ the planning time of the query but usually finds better plans. +==== + +| {check-mark} + +|=== + +[IMPORTANT] +==== +The Cypher query option `connectComponentsPlanner` is deprecated and will be removed without a replacement. +The product's default behavior of using a cost-based IDP search algorithm when combining sub-plans will be kept. +==== + + +[[cypher-update-strategy]] +=== Cypher update strategy +This option affects the eagerness of updating queries. + +The possible values are: + +[options="header",cols="2m,3a,^1a"] +|=== +| Query option | Description | Default + +| updateStrategy=default +| Update queries are executed eagerly when needed. +| {check-mark} + +| updateStrategy=eager +| Update queries are always executed eagerly. +| + +|=== + + +[[cypher-expression-engine]] +=== Cypher expression engine + +This option affects how the runtime evaluates expressions. + +The possible values are: + +[options="header",cols="2m,3a,^1a"] +|=== +| Query option | Description | Default + +| expressionEngine=default +| Compile expressions and use the compiled expression engine when needed. +| {check-mark} + +| expressionEngine=interpreted +| Always use the _interpreted_ expression engine. +| + +| expressionEngine=compiled +| +Always compile expressions and use the _compiled_ expression engine. + +| + +|=== + + +[[cypher-operator-engine]] +=== Cypher operator engine + +This query option affects whether the pipelined runtime attempts to generate compiled code for groups of operators. + +The possible values are: + +[options="header",cols="2m,3a,^1a"] +|=== +| Query option | Description | Default + +| operatorEngine=default +| Attempt to generate compiled operators when applicable. +| {check-mark} + +| operatorEngine=interpreted +| Never attempt to generate compiled operators. +| + +| operatorEngine=compiled +| +Always attempt to generate _compiled_ operators. + +Cannot be used together with `runtime=slotted`. + +| + +|=== + + +[[cypher-interpreted-pipes-fallback]] +=== Cypher interpreted pipes fallback + +This query option affects how the pipelined runtime behaves for operators it does not directly support. + +The available options are: + +[options="header",cols="2m,3a,^1a"] +|=== +| Query option | Description | Default + +| interpretedPipesFallback=default +| Equivalent to `interpretedPipesFallback=whitelisted_plans_only`. +| {check-mark} + +| interpretedPipesFallback=disabled +| +If the plan contains any operators not supported by the pipelined runtime then another runtime is chosen to execute the entire plan. + +Cannot be used together with `runtime=slotted`. + +| + +| interpretedPipesFallback=whitelisted_plans_only +| +Parts of the execution plan can be executed on another runtime. +Only certain operators are allowed to execute on another runtime. + +Cannot be used together with `runtime=slotted`. + +| + +| interpretedPipesFallback=all +| +Parts of the execution plan may be executed on another runtime. +Any operator is allowed to execute on another runtime. +Queries with this option set might produce incorrect results, or fail. + +Cannot be used together with or `runtime=slotted`. + +[WARNING] +==== +This setting is experimental, and using it in a production environment is discouraged. +==== + +| + +|=== + + +[[cypher-replanning]] +=== Cypher replanning + +Cypher replanning occurs in the following circumstances: + +* When the query is not in the cache. +This can either be when the server is first started or restarted, if the cache has recently been cleared, or if link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings#config_server.db.query_cache_size[server.db.query_cache_size] was exceeded. +* When the time has past the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings#config_dbms.cypher.min_replan_interval[dbms.cypher.min_replan_interval] value, and the database statistics have changed more than the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/configuration-settings#config_dbms.cypher.statistics_divergence_threshold[dbms.cypher.statistics_divergence_threshold] value. + +There may be situations where xref::planning-and-tuning/execution-plans.adoc[Cypher query planning] can occur at a non-ideal time. +For example, when a query must be as fast as possible and a valid plan is already in place. + +[NOTE] +==== +Replanning is not performed for all queries at once; it is performed in the same thread as running the query, and can block the query. +However, replanning one query does not replan any other queries. +==== + +There are three different replan options available: + +[options="header",cols="2m,3a,^1a"] +|=== +| Option | Description | Default + +| replan=default +| This is the planning and replanning option as described above. +| {check-mark} + +| replan=force +| This will force a replan, even if the plan is valid according to the planning rules. +Once the new plan is complete, it replaces the existing one in the query cache. +| + +| replan=skip +| If a valid plan already exists, it will be used even if the planning rules would normally dictate that it should be replanned. +| + +|=== + +The replan option is prepended to queries. + +For example: + +[source, syntax] +---- +CYPHER replan=force MATCH ... +---- + +In a mixed workload, you can force replanning by using the Cypher `EXPLAIN` commands. +This can be useful to schedule replanning of queries which are expensive to plan, at known times of low load. +Using `EXPLAIN` will make sure the query is only planned, but not executed. + +For example: + +[source, syntax] +---- +CYPHER replan=force EXPLAIN MATCH ... +---- + +During times of known high load, `replan=skip` can be useful to not introduce unwanted latency spikes. + diff --git a/modules/ROOT/pages/planning-and-tuning/query-tuning/index.adoc b/modules/ROOT/pages/planning-and-tuning/query-tuning/index.adoc deleted file mode 100644 index 0a64acc03..000000000 --- a/modules/ROOT/pages/planning-and-tuning/query-tuning/index.adoc +++ /dev/null @@ -1,29 +0,0 @@ -:description: Query tuning for the Cypher query language. -[[query-tuning]] -= Query tuning - -Neo4j aims to execute queries as fast as possible. -However, when optimizing for maximum query execution performance, it may be helpful to rephrase queries using knowledge about the domain and the application. - -This section contains information about how to tune queries using different strategies. - -* xref:planning-and-tuning/query-tuning/indexes.adoc[] -* xref:planning-and-tuning/query-tuning/using.adoc[] -* xref:planning-and-tuning/query-tuning/query-options.adoc[] - -For information about changing the runtime of a query, see the page about xref:planning-and-tuning/runtimes/concepts.adoc[Cypher runtime concepts]. - -== General recommendations - -The overall goal of manual query performance optimization is to ensure that only necessary data is retrieved from the graph. - -Queries should aim to filter data as early as possible in order to reduce the amount of work that has to be done in the later stages of query execution. -This also applies to what gets returned: returning whole nodes and relationships ought to be avoided in favour of selecting and returning only the data that is needed. -You should also make sure to set an upper limit on variable length patterns, so they don't cover larger portions of the dataset than needed. - -Each Cypher query gets optimized and transformed into an xref::planning-and-tuning/execution-plans.adoc[execution plan] by the Cypher query planner. -To minimize the resources used for this, try to use parameters instead of literals when possible. -This allows Cypher to re-use queries instead of having to parse and build new execution plans. - -To read more about the execution plan operators mentioned in this section, see xref::planning-and-tuning/operators/index.adoc[]. - diff --git a/modules/ROOT/pages/planning-and-tuning/query-tuning/indexes.adoc b/modules/ROOT/pages/planning-and-tuning/query-tuning/indexes.adoc deleted file mode 100644 index 34ee4b2af..000000000 --- a/modules/ROOT/pages/planning-and-tuning/query-tuning/indexes.adoc +++ /dev/null @@ -1,1443 +0,0 @@ -:description: The query plans when indexes are used in various scenarios. -[[query-tuning-indexes]] -= The use of indexes - -It is important to have a fundamental understanding of how indexes operate before using them to tune your Cypher queries. -This section describes the query plans that result from different index scenarios. - -Node indexes and relationship indexes operate in the same way. -Therefore, node and relationship indexes are used interchangeably in this section. - -For instructions on how to create and maintain indexes, refer to xref::indexes-for-search-performance.adoc[]. - -== Index types and predicate compatibility - -Generally, an index solves some combination of a label/relationship type predicate and property predicates at the same time. -There are different types of indexes available in Neo4j and these are compatible with different property predicates. - -Indexes are most often used for `MATCH` and `OPTIONAL MATCH` clauses that combine a label/relationship type predicate with a property predicate. -Therefore, it is important to know what kind of predicates can be solved by the different indexes. - -The different index types used for search performance are: - -* `LOOKUP` -* `RANGE` -* `POINT` -* `TEXT` -* `BTREE` label:removed[] - -[NOTE] -==== -The `RANGE` and `TEXT` indexes can only perform limited matching on strings - exact, prefix, substring, or suffix matches. -A `FULLTEXT` index will instead tokenize the indexed string values, so it can match terms anywhere within the strings. -See xref::indexes-for-full-text-search.adoc[]. -==== - - -== LOOKUP indexes - -`LOOKUP` indexes are present by default and solve only node label and relationship type predicates: - -[cols="2, 2a", options="header"] -|=== -| Predicate | Syntax (example) - -| Node label predicate. -| -[source, syntax, role="noheader"] ----- -MATCH (n:Label) ----- - -| Node label predicate. -| -[source, syntax, role="noheader"] ----- -MATCH (n) -WHERE n:Label ----- - -| Relationship type predicate. -| -[source, syntax, role="noheader"] ----- -MATCH ()-[r:REL]->() ----- - -| Relationship type predicate. -| -[source, syntax, role="noheader"] ----- -MATCH ()-[r]->() -WHERE r:REL ----- - -|=== - -[WARNING] -==== -`LOOKUP` indexes are the most important index type in the database because they improve the performance of the Cypher queries and the population of other indexes. -Dropping these indexes may lead to severe performance degradation. -Therefore, carefully consider the consequences before doing so. -==== - - -== RANGE indexes - -In combination with node label and relationship type predicates, `RANGE` indexes support most types of predicates: - -[cols="2", options="header"] -|=== - -| Predicate | Syntax - -| Equality check. -a| -[source, syntax, role="noheader"] ----- -n.prop = value ----- - -| List membership check. -a| -[source, syntax, role="noheader"] ----- -n.prop IN list ----- - -| Existence check. -a| -[source, syntax, role="noheader"] ----- -n.prop IS NOT NULL ----- - -| Range search. -a| -[source, syntax, role="noheader"] ----- -n.prop > value ----- - -| Prefix search. -a| -[source, syntax, role="noheader"] ----- -STARTS WITH ----- - -|=== - - -== POINT indexes - -In combination with node label and relationship type predicates, `POINT` indexes only solve predicates operating on points. -Therefore, `POINT` indexes are only used when it is known that the predicate evaluates to `null` for all non-point values. - -`POINT` indexes only support point type predicates: - -[cols="2", options="header"] -|=== -| Predicate | Syntax - -| Property point value. -a| -[source, syntax, role="noheader"] ----- -n.prop = point({x: value, y: value}) ----- - -| Within bounding box. -a| -[source, syntax, role="noheader"] ----- -point.withinBBox(n.prop, lowerLeftCorner, upperRightCorner) ----- - -| Distance. -a| -[source, syntax, role="noheader"] ----- -point.distance(n.prop, center) < = distance ----- - -|=== - -[NOTE] -==== -As of Neo4j 5.11, the above set of predicates can be extended with the use of type constraints. -For more information, see xref::planning-and-tuning/query-tuning/indexes.adoc#extending-index-compatibility-with-type-constraints[Extending index compatibility with type constraints]. -==== - -== TEXT indexes - -In combination with node label and relationship type predicates, `TEXT` indexes only solve predicates operating on strings. - -Predicates that only operate on strings are always solvable by a `TEXT` index: - -* `STARTS WITH` -* `ENDS WITH` -* `CONTAINS` - -However, other predicates are only used when it is known that the property is compared to a string: - -* `n.prop = "string"` -* `n.prop IN ["a", "b", "c"]` - -This means that a `TEXT` index is not able to solve e.g. `a.prop = b.prop`, unless a type constraint also exists on the property. - -`TEXT` indexes support the following predicates: - -[cols="2", options="header"] -|=== -| Predicate | Syntax - -| Equality check. -a| -[source, syntax, role="noheader"] ----- -n.prop = 'example_string' ----- - -| List membership check. -a| -[source, syntax, role="noheader"] ----- -n.prop IN ['abc', 'example_string', 'neo4j'] ----- - -| Prefix search. -a| -[source, syntax, role="noheader"] ----- -STARTS WITH ----- - -| Suffix search. -a| -[source, syntax, role="noheader"] ----- -ENDS WITH ----- - -| Substring search. -a| -[source,syntax,role="noheader"] ----- -CONTAINS ----- - -| -xref:values-and-types/type-predicate.adoc[`STRING` type predicates with existence check]. -For more information, see xref:planning-and-tuning/query-tuning/indexes.adoc#text-index-type-predicate-expressions[TEXT indexes and type predicate expressions]. -a| -[source,syntax,role="noheader"] ----- -IS :: STRING NOT NULL ----- - -|=== - - -[[text-index-type-predicate-expressions]] -=== TEXT indexes and type predicate expressions - -`TEXT` indexes only solve predicates if it is known that the predicate evaluates to `false` or `null` for all non-`STRING` values. -However, in Cypher, `null IS {two-colons} STRING` is `true`, and indexes do not store `null` values. -This means that `TEXT` indexes cannot be used if any of the queried properties are `null` instead of a `STRING` value. - -To use `TEXT` indexes in situations where any of the queried properties may be `null` rather than a `STRING` value, add the type predicate expression `IS {two-colons} STRING NOT NULL` (or its alias, introduced in Neo4j 5.14, `IS {two-colons} STRING!`) to the query. -This will enforce both the existence of a property and its `STRING` type, discarding any rows where the property is missing or not of type `STRING`, and thereby enable the use of `TEXT` indexes - -For example, the following `MATCH` clause may not be solvable with a `TEXT` index because some of the queried properties may be `null`: - -[source, cypher, role="noheader", role=test-skip] ----- -MATCH (n:Label) WHERE n.prop = $param ----- - -The same clause, with `IS {two-colons} STRING NOT NULL` added, would, however, be solvable with an existing `TEXT` index: - -[source, cypher, role="noheader", role=test-skip] ----- -MATCH (n:Label) WHERE $param IS :: STRING NOT NULL AND n.prop = $param ----- - -[NOTE] -While type predicate expressions were introduced in Neo4j 5.9, the `IS {two-colons} STRING NOT NULL` syntax only became an index-compatible predicate in Neo4j 5.15. - -For more information, see the page about xref:values-and-types/type-predicate.adoc[type predicate expressions]. - -There are two further methods which can be used to ensure that an expression is of type `STRING`: - -* It is possible to use the xref:functions/string.adoc#functions-tostring[`toString()`] function to convert an expression to `STRING` values. -* If it is known that the property is always of type `STRING`, then it is also possible to xref::planning-and-tuning/query-tuning/indexes.adoc#extending-index-compatibility-with-type-constraints[add a type constraint to help the planner]. - -[[extending-index-compatibility-with-type-constraints]] -== Extending index compatibility with type constraints - -_This feature was introduced in Neo4j 5.11._ - -For indexes that are compatible only with specific types (i.e. `TEXT` and `POINT` indexes), the planner needs to be able to deduce that a predicate will evaluate to `null` for non-compatible values. -Since xref::constraints/index.adoc#types-of-constraint[type constraints] guarantee that a property is always of the same type, they can be used to extend the scenarios for which `TEXT` indexes and `POINT` indexes are compatible with a predicate. - -For example, if the property `prop` in the below query has been constrained to have type `STRING`, then a `TEXT` index can also be planned for the `IS NOT NULL` predicate: - -[source] ----- -MATCH (n: Label) WHERE n.prop IS NOT NULL ----- - -Similarly, if the property had been constrained to have the type `POINT`, then a `POINT` index could have been used. - -== Index preference - -When multiple indexes are available and able to solve a predicate, there is an order defined that decides which index to use. - -It is defined as such: - -* `TEXT` indexes are used over `RANGE` and `POINT` indexes for `CONTAINS` and `ENDS WITH`. -* `POINT` indexes are used over `RANGE` and `TEXT` indexes for distance and within a bounding box. -* `RANGE` indexes are preferred over `TEXT` and `POINT` indexes in all other cases. - -`LOOKUP` indexes are not defined in this order since they never solve the same set of predicates as the other indexes. - -*Examples:* - -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-node-label-lookup-index-example[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-relationship-type-lookup-index-example[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-node-range-index-example[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-relationship-range-index-example[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-node-text-index-example[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-relationship-text-index-example[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-multiple-available-index-types[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-equality-check-using-where-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-equality-check-using-where-composite-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-range-comparisons-using-where-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-range-comparisons-using-where-composite-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-multiple-range-comparisons-using-where-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-multiple-range-comparisons-using-where-composite-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-list-membership-check-using-in-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-list-membership-check-using-in-composite-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-prefix-search-using-starts-with-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-prefix-search-using-starts-with-composite-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-suffix-search-using-ends-with-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-suffix-search-using-ends-with-composite-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-substring-search-using-contains-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-substring-search-using-contains-composite-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-existence-check-using-is-not-null-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-existence-check-using-is-not-null-composite-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-spatial-distance-searches-single-property-index[] -* xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-spatial-bounding-box-searches-single-property-index[] - - -[discrete] -[[administration-indexes-node-label-lookup-index-example]] -=== Node label LOOKUP index example - -In the example below, a node `LOOKUP` index is available. - -//// -// Lookup indexes exist by default -CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n) -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person) -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 42 | 42 | 0 | | | | | -| | +---------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | person:Person | 42 | 42 | 43 | 120 | 2/1 | 0.565 | Fused in Pipeline 0 | -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 43, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-relationship-type-lookup-index-example]] -=== Relationship type LOOKUP index example - -In the example below, a relationship `LOOKUP` index is available. - -//// -// Lookup indexes exist by default -CREATE LOOKUP INDEX rel_type_lookup_index FOR ()-[r]-() ON EACH type(r) -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH ()-[r:KNOWS]->() -RETURN r ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-------------------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 22 | 22 | 0 | | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipTypeScan | (anon_0)-[r:KNOWS]->(anon_1) | 22 | 22 | 23 | 120 | 3/1 | 0.915 | Fused in Pipeline 0 | -+-------------------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 23, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-node-range-index-example]] -=== Node RANGE index example - -In the example below, a `Person(firstname)` node `RANGE` index is available. - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX node_range_index_name FOR (n:Person) ON (n.firstname) ----- -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person {firstname: 'Andy'}) -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 1 | 1 | 0 | | | | | -| | +----------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX person:Person(firstname) WHERE firstname = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.635 | Fused in Pipeline 0 | -+-----------------+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - -[discrete] -[[administration-indexes-relationship-range-index-example]] -=== Relationship RANGE index example - -In this example, a `KNOWS(since)` relationship `RANGE` index is available. - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX rel_range_index_name FOR ()-[r:KNOWS]-() ON (r.since) ----- -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH (person)-[relationship:KNOWS {since: 1992}]->(friend) -RETURN person, friend ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+--------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person, friend | 1 | 1 | 0 | | | | | -| | +-------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeek | RANGE INDEX (person)-[relationship:KNOWS(since)]->(friend) WHERE since = $autoint_0 | 1 | 1 | 2 | 120 | 2/1 | 0.413 | Fused in Pipeline 0 | -+--------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - -[discrete] -[[administration-indexes-node-text-index-example]] -=== Node TEXT index - -//// -[source, cypher, role=test-setup] ----- -CREATE TEXT INDEX node_text_person_surname FOR (n:Person) ON (n.surname) ----- -//// - -In the example below, a `Person(surname)` node `TEXT` index is available. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person {surname: 'Smith'}) -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 1 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | TEXT INDEX person:Person(surname) WHERE surname = $autostring_0 | 1 | 1 | 2 | 120 | 2/0 | 0.446 | Fused in Pipeline 0 | -+-----------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-relationship-text-index-example]] -=== Relationship TEXT index - -//// -[source, cypher, role=test-setup] ----- -CREATE TEXT INDEX rel_text_index_name FOR ()-[r:KNOWS]-() ON (r.metIn) ----- -//// - -In this example, a `KNOWS(metIn)` relationship `TEXT` index is available. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person)-[relationship:KNOWS {metIn: 'Malmo'} ]->(friend) -RETURN person, friend ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+--------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person, friend | 1 | 1 | 0 | | | | | -| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeek | TEXT INDEX (person)-[relationship:KNOWS(metIn)]->(friend) WHERE metIn = $autostring_0 | 1 | 1 | 2 | 120 | 2/0 | 0.691 | Fused in Pipeline 0 | -+--------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-multiple-available-index-types]] -=== Multiple available index types - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX node_range_person_middlename FOR (p:Person) ON (p.middlename); -CREATE TEXT INDEX node_text_person_middlename FOR (p:Person) ON (p.middlename); ----- -//// - -In the example below, both a `Person(middlename)` node `TEXT` index and a `Person(middlename)` node `RANGE` index are available. -The `RANGE` node index is chosen. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person {middlename: 'Ron'}) -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 1 | 1 | 0 | | | | | -| | +------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX person:Person(middlename) WHERE middlename = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.423 | Fused in Pipeline 0 | -+-----------------+------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-equality-check-using-where-single-property-index]] -=== Equality check using `WHERE` (single-property index) - -A query containing equality comparisons of a single indexed property in the `WHERE` clause is backed automatically by the index. -It is also possible for a query with multiple `OR` predicates to use multiple indexes, if indexes exist on the properties. -For example, if indexes exist on both `:Label(p1)` and `:Label(p2)`, `MATCH (n:Label) WHERE n.p1 = 1 OR n.p2 = 2 RETURN n` will use both indexes. - - -.Query -[source, cypher] ----- -MATCH (person:Person) -WHERE person.firstname = 'Andy' -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 1 | 1 | 0 | | | | | -| | +----------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX person:Person(firstname) WHERE firstname = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.292 | Fused in Pipeline 0 | -+-----------------+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-equality-check-using-where-composite-index]] -=== Equality check using `WHERE` (composite index) - -A query containing equality comparisons for all the properties of a composite index will automatically be backed by the same index. -However, the query does not need to have equality on all properties. -It can have ranges and existence predicates as well. -But in these cases rewrites might happen depending on which properties have which predicates, see xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. - -//// -[source, cypher, role=test-setup] ----- -CREATE (p0:`Person` {`age`:35, `country`:"UK", `firstname`:"John", `highScore`:54321, `middlename`:"Ron", `name`:"john", `surname`:"Smith"}); -CREATE RANGE INDEX node_range_age_country FOR (n:Person) ON (n.age, n.country); ----- -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH (n:Person) -WHERE n.age = 35 AND n.country = 'UK' -RETURN n ----- - -However, the query `MATCH (n:Person) WHERE n.age = 35 RETURN n` will not be backed by the composite index, as the query does not contain a predicate on the `country` property. -It will only be backed by an index on the `Person` label and `age` property defined thus: `:Person(age)`; i.e. a single-property index. - -[role="queryresult"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version 5.5 - -Batch size 128 - -+-----------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | n | 0 | 3 | 24 | | | | | -| | +----+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | 1 | RANGE INDEX n:Person(age, country) WHERE age = $autoint_0 AND country = $autostring_1 | 0 | 3 | 4 | 120 | 3/0 | 0.476 | Fused in Pipeline 0 | -+-----------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 28, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-range-comparisons-using-where-single-property-index]] -=== Range comparisons using `WHERE` (single-property index) - -Single-property indexes are also automatically used for inequality (range) comparisons of an indexed property in the `WHERE` clause. - -.Query -[source, cypher] ----- -PROFILE -MATCH (friend)<-[r:KNOWS]-(person) -WHERE r.since < 2011 -RETURN friend, person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+---------------------------------------+--------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------------+--------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | friend, person | 1 | 1 | 0 | | | | | -| | +--------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeekByRange | RANGE INDEX (person)-[r:KNOWS(since)]->(friend) WHERE since < $autoint_0 | 1 | 1 | 2 | 120 | 2/1 | 0.943 | Fused in Pipeline 0 | -+---------------------------------------+--------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-range-comparisons-using-where-composite-index]] -=== Range comparisons using `WHERE` (composite index) - -Composite indexes are also automatically used for inequality (range) comparisons of indexed properties in the `WHERE` clause. -Equality or list membership check predicates may precede the range predicate. -However, predicates after the range predicate may be rewritten as an existence check predicate and a filter as described in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. - -//// -[source, cypher, role=test-setup] ----- -MERGE (a:Person {name: 'a'}) -MERGE (b:Person {name: 'b'}) -MERGE (c:Person {name: 'c'}) -MERGE (d:Person {name: 'd'}) -MERGE (a)-[:KNOWS {since: 1900, lastMet: 2020}]->(b) -MERGE (a)-[:KNOWS {since: 2017, lastMet: 2021}]->(c) -MERGE (b)-[:KNOWS {since: 2017, lastMet: 2021}]->(d) -MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) -MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) -MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) -MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) -MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y) -MERGE (x)-[:KNOWS {since: 2017, lastMet: 2021}]->(y); -CREATE RANGE INDEX rel_range_since_lastmet FOR ()-[r:KNOWS]-() ON (r.since, r.lastMet); ----- -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH ()-[r:KNOWS]-() -WHERE r.since < 2011 AND r.lastMet > 2019 -RETURN r.since ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+----------------------------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `r.since` | 2 | 2 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | cache[r.since] AS `r.since` | 2 | 2 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | cache[r.lastMet] > $autoint_1 | 2 | 2 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexSeek | RANGE INDEX (anon_0)-[r:KNOWS(since, lastMet)]-(anon_1) WHERE since < $autoint_0 AND lastMet IS NOT | 2 | 2 | 2 | 120 | 1/1 | 0.525 | Fused in Pipeline 0 | -| | NULL, cache[r.since], cache[r.lastMet] | | | | | | | | -+----------------------------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-multiple-range-comparisons-using-where-single-property-index]] -=== Multiple range comparisons using `WHERE` (single-property index) - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX node_range_highscore FOR (p:Person) ON p.highScore ----- -//// - -When the `WHERE` clause contains multiple inequality (range) comparisons for the same property, these can be combined in a single index range seek. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person) -WHERE 10000 < person.highScore < 20000 -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------------+----------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+----------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 1 | 1 | 0 | | | | | -| | +----------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeekByRange | RANGE INDEX person:Person(highScore) WHERE highScore > $autoint_0 AND highScore < $autoint_1 | 1 | 1 | 2 | 120 | 2/1 | 0.286 | Fused in Pipeline 0 | -+-----------------------+----------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-multiple-range-comparisons-using-where-composite-index]] -=== Multiple range comparisons using `WHERE` (composite index) - -When the `WHERE` clause contains multiple inequality (range) comparisons for the same property, these can be combined in a single index range seek. -That single range seek created in the following query will then use the composite index `Person(highScore, name)` if it exists. - -//// -[source, cypher, role=test-setup] ----- -CREATE INDEX FOR (p:Person) ON (p.highScore, p.name); -CREATE (:Person {name: 'Tom', highScore: 15000}); ----- -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person) -WHERE 10000 < person.highScore < 20000 AND person.name IS NOT NULL -RETURN person ----- - - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 1 | 1 | 0 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX person:Person(highScore, name) WHERE highScore > $autoint_0 AND highScore < $autoint_1 A | 1 | 1 | 2 | 120 | 2/1 | 4.498 | Fused in Pipeline 0 | -| | ND name IS NOT NULL | | | | | | | | -+-----------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-list-membership-check-using-in-single-property-index]] -=== List membership check using `IN` (single-property index) - -The `IN` predicate on `r.since` in the following query will use the single-property index `KNOWS(lastMetIn)` if it exists. - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX rel_range_lastmetin FOR ()-[r:KNOWS]-() ON r.lastMetIn ----- -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH (person)-[r:KNOWS]->(friend) -WHERE r.lastMetIn IN ['Malmo', 'Stockholm'] -RETURN person, friend ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+--------------------------------+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person, friend | 1 | 1 | 0 | | | | | -| | +------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeek | RANGE INDEX (person)-[r:KNOWS(lastMetIn)]->(friend) WHERE lastMetIn IN $autolist_0 | 1 | 1 | 3 | 120 | 3/1 | 0.614 | Fused in Pipeline 0 | -+--------------------------------+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 3, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-list-membership-check-using-in-composite-index]] -=== List membership check using `IN` (composite index) - -The `IN` predicates on `r.since` and `r.lastMet` in the following query will use the composite index `KNOWS(since, lastMet)` if it exists. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person)-[r:KNOWS]->(friend) -WHERE r.since IN [1992, 2017] AND r.lastMet IN [2002, 2021] -RETURN person, friend ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+--------------------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person, friend | 1 | 1 | 0 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeek | RANGE INDEX (person)-[r:KNOWS(since, lastMet)]->(friend) WHERE since IN $autolist_0 AND lastMet IN $ | 1 | 1 | 5 | 120 | 5/1 | 1.864 | Fused in Pipeline 0 | -| | autolist_1 | | | | | | | | -+--------------------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 5, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-prefix-search-using-starts-with-single-property-index]] -=== Prefix search using `STARTS WITH` (single-property index) - -The `STARTS WITH` predicate on `person.firstname` in the following query will use the `Person(firstname)` index, if it exists. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person) -WHERE person.firstname STARTS WITH 'And' -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 2 | 1 | 0 | | | | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeekByRange | RANGE INDEX person:Person(firstname) WHERE firstname STARTS WITH $autostring_0 | 2 | 1 | 2 | 120 | 3/0 | 0.387 | Fused in Pipeline 0 | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-prefix-search-using-starts-with-composite-index]] -=== Prefix search using `STARTS WITH` (composite index) - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX node_text_firstname_surname FOR (p:Person) ON (p.firstname, p.surname) ----- -//// - -The `STARTS WITH` predicate on `person.firstname` in the following query will use the `Person(firstname,surname)` index, if it exists. -Any (non-existence check) predicate on `person.surname` will be rewritten as existence check with a filter. -However, if the predicate on `person.firstname` is a equality check then a `STARTS WITH` on `person.surname` would also use the index (without rewrites). -More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person) -WHERE person.firstname STARTS WITH 'And' AND person.surname IS NOT NULL -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 1 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX person:Person(firstname, surname) WHERE firstname STARTS WITH $autostring_0 AND surname | 1 | 1 | 2 | 120 | 3/0 | 0.534 | Fused in Pipeline 0 | -| | IS NOT NULL | | | | | | | | -+-----------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-suffix-search-using-ends-with-single-property-index]] -=== Suffix search using `ENDS WITH` (single-property index) - -The `ENDS WITH` predicate on `r.metIn` in the following query uses the `KNOWS(metIn)` index, if it exists. -Text indexes are optimized for `CONTAINS` and `ENDS WITH` and they are the only indexes that can solve those predicates. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person)-[r:KNOWS]->(friend) -WHERE r.metIn ENDS WITH 'mo' -RETURN person, friend ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version 5.5 - -Batch size 128 - -+----------------------------------------+----+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------------+----+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | person, friend | 0 | 0 | 0 | | | | | -| | +----+------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexEndsWithScan | 1 | TEXT INDEX (person)-[r:KNOWS(metIn)]->(friend) WHERE metIn ENDS WITH $autostring_0 | 0 | 0 | 1 | 120 | 0/0 | 1.135 | Fused in Pipeline 0 | -+----------------------------------------+----+------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 1, total allocated memory: 184 - ----- - -Text indexes only index String values and therefore do not find other values. - - -[discrete] -[[administration-indexes-suffix-search-using-ends-with-composite-index]] -=== Suffix search using `ENDS WITH` (composite index) - -The `ENDS WITH` predicate on `r.metIn` in the following query uses the `KNOWS(metIn, lastMetIn)` index, if it exists. -However, it is rewritten as existence check and a filter due to the index not supporting actual suffix searches for composite indexes, this is still faster than not using an index in the first place. -Any (non-existence check) predicate on `KNOWS.lastMetIn` is also rewritten as existence check with a filter. -More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. - -//// -[source, cypher, role=test-setup] ----- -DROP INDEX rel_text_index_name; // TEXT indexes would take precedence over composite RANGE index -CREATE RANGE INDEX rel_text_metin_lastmetin FOR ()-[r:KNOWS]-() ON (r.metIn, r.lastMetIn) ----- -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH (person)-[r:KNOWS]->(friend) -WHERE r.metIn ENDS WITH 'mo' AND r.lastMetIn IS NOT NULL -RETURN person, friend ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+--------------------------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person, friend | 0 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | cache[r.metIn] ENDS WITH $autostring_0 | 0 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexScan | RANGE INDEX (person)-[r:KNOWS(metIn, lastMetIn)]->(friend) WHERE metIn IS NOT NULL AND lastMetIn IS | 1 | 1 | 2 | 120 | 2/1 | 0.317 | Fused in Pipeline 0 | -| | NOT NULL, cache[r.metIn] | | | | | | | | -+--------------------------------+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-substring-search-using-contains-single-property-index]] -=== Substring search using `CONTAINS` (single-property index) - -The `CONTAINS` predicate on `person.firstname` in the following query will use the `Person(firstname)` index, if it exists. -Text indexes are optimized for `CONTAINS` and `ENDS WITH` and they are the only indexes that can solve those predicates. -Composite indexes are currently not able to support `CONTAINS`. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person) -WHERE person.firstname CONTAINS 'h' -RETURN person ----- - -// TODO: this should output a query plan - -//// -.Query Plan -[source, query plan, role="noheader"] ----- -+------------------------------------------------------------------------------------------------------------+ -| person | -+------------------------------------------------------------------------------------------------------------+ -| Node[0]{country:"UK",highScore:54321,firstname:"John",surname:"Smith",name:"john",middlename:"Ron",age:35} | -+------------------------------------------------------------------------------------------------------------+ -1 row ----- -//// - -Text indexes only index String values and therefore do not find other values. - - -[discrete] -[[administration-indexes-substring-search-using-contains-composite-index]] -=== Substring search using `CONTAINS` (composite index) - -The `CONTAINS` predicate on `person.country` in the following query will use the `Person(country,age)` index, if it exists. -However, it will be rewritten as existence check and a filter due to the index not supporting actual suffix searches for composite indexes, this is still faster than not using an index in the first place. -Any (non-existence check) predicate on `person.age` will also be rewritten as existence check with a filter. -More information about how the rewriting works can be found in xref::indexes-for-search-performance.adoc#indexes-single-vs-composite-index[composite index limitations]. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person) -WHERE person.country CONTAINS '300' AND person.age IS NOT NULL -RETURN person ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 15 | 1 | 0 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | cache[person.country] CONTAINS $autostring_0 | 15 | 1 | 0 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexScan | RANGE INDEX person:Person(country, age) WHERE country IS NOT NULL AND age IS NOT NULL, cache[person. | 303 | 303 | 304 | 120 | 5/0 | 2.309 | Fused in Pipeline 0 | -| | country] | | | | | | | | -+-----------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 304, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-existence-check-using-is-not-null-single-property-index]] -=== Existence check using `IS NOT NULL` (single-property index) - -The `r.since IS NOT NULL` predicate in the following query uses the `KNOWS(since)` index, if it exists. - -.Query -[source, cypher] ----- -PROFILE -MATCH (person)-[r:KNOWS]->(friend) -WHERE r.since IS NOT NULL -RETURN person, friend ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+--------------------------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person, friend | 1 | 1 | 0 | | | | | -| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexScan | RANGE INDEX (person)-[r:KNOWS(since)]->(friend) WHERE since IS NOT NULL | 1 | 1 | 2 | 120 | 2/1 | 1.046 | Fused in Pipeline 0 | -+--------------------------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 2, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-existence-check-using-is-not-null-composite-index]] -=== Existence check using `IS NOT NULL` (composite index) - -The `p.firstname IS NOT NULL` and `p.surname IS NOT NULL` predicates in the following query will use the `Person(firstname,surname)` index, if it exists. -Any (non-existence check) predicate on `person.surname` will be rewritten as existence check with a filter. - -.Query -[source, cypher] ----- -PROFILE -MATCH (p:Person) -WHERE p.firstname IS NOT NULL AND p.surname IS NOT NULL -RETURN p ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+----------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 1 | 2 | 0 | | | | | -| | +----------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexScan | RANGE INDEX p:Person(firstname, surname) WHERE firstname IS NOT NULL AND surname IS NOT NULL | 1 | 2 | 3 | 120 | 2/1 | 0.310 | Fused in Pipeline 0 | -+-----------------+----------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 3, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-spatial-distance-searches-single-property-index]] -=== Spatial distance searches (single-property index) - -//// -[source, cypher, role=test-setup] ----- -CREATE POINT INDEX FOR ()-[r:KNOWS]-() ON (r.lastMetPoint) ----- -//// - -If a property with point values is indexed, the index is used for spatial distance searches as well as for range queries. - -.Query -[source, cypher] ----- -PROFILE -MATCH ()-[r:KNOWS]->() -WHERE point.distance(r.lastMetPoint, point({x: 1, y: 2})) < 2 -RETURN r.lastMetPoint ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+---------------------------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `r.lastMetPoint` | 13 | 9 | 0 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | cache[r.lastMetPoint] AS `r.lastMetPoint` | 13 | 9 | 0 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | point.distance(cache[r.lastMetPoint], point({x: $autoint_0, y: $autoint_1})) < $autoint_2 | 13 | 9 | 0 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeekByRange | POINT INDEX (anon_0)-[r:KNOWS(lastMetPoint)]->(anon_1) WHERE point.distance(lastMetPoint, point($aut | 13 | 9 | 10 | 120 | 5/3 | 1.417 | Fused in Pipeline 0 | -| | oint_0, $autoint_1)) < $autoint_2, cache[r.lastMetPoint] | | | | | | | | -+---------------------------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 10, total allocated memory: 184 ----- - - -[discrete] -[[administration-indexes-spatial-bounding-box-searches-single-property-index]] -=== Spatial bounding box searches (single-property index) - -The ability to do index seeks on bounded ranges works even with the 2D and 3D spatial `POINT` types. - -//// -[source, cypher, role=test-setup] ----- -CREATE POINT INDEX FOR (p:Person) ON (p.location) ----- -//// - -.Query -[source, cypher] ----- -PROFILE -MATCH (person:Person) -WHERE point.withinBBox(person.location, point({x: 1.2, y: 5.4}), point({x: 1.3, y: 5.5})) -RETURN person.firstname ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `person.firstname` | 0 | 1 | 0 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | person.firstname AS `person.firstname` | 0 | 1 | 2 | | | | | -| | +------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeekByRange | POINT INDEX person:Person(location) WHERE point.withinBBox(location, point($autodouble_0, $autodoubl | 0 | 1 | 2 | 120 | 6/0 | 7.910 | Fused in Pipeline 0 | -| | e_1), point($autodouble_2, $autodouble_3)) | | | | | | | | -+-----------------------+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 4, total allocated memory: 184 ----- - diff --git a/modules/ROOT/pages/values-and-types/spatial.adoc b/modules/ROOT/pages/values-and-types/spatial.adoc index efe24e20a..0120009f2 100644 --- a/modules/ROOT/pages/values-and-types/spatial.adoc +++ b/modules/ROOT/pages/values-and-types/spatial.adoc @@ -27,7 +27,7 @@ Values with the `POINT` type have the following characteristics: This means it contains either 2 or 3 64-bit `FLOAT` values, which together are called the _Coordinate_. * Each point will also be associated with a specific xref::values-and-types/spatial.adoc#spatial-values-crs[Coordinate Reference System] (CRS) that determines the meaning of the values in the _Coordinate_. * Instances of `POINT` and `LIST` can be assigned to node and relationship properties. -* Nodes and relationships with `POINT` or `LIST` properties can be indexed using a xref::indexes-for-search-performance.adoc#indexes-create-a-node-point-index[point index]. +* Nodes and relationships with `POINT` or `LIST` properties can be indexed using a xref:indexes/search-performance-indexes/managing-indexes.adoc#indexes-create-a-node-point-index[point index]. This is true for all CRSs (and for both 2D and 3D). * The xref::functions/spatial.adoc#functions-distance[distance function] will work on points in all CRS and in both 2D and 3D, but only if the two points have the same CRS (and therefore also same dimension). @@ -253,11 +253,14 @@ RETURN == Spatial values and indexes // POINT INDEX new in Neo4j 5.0 -If there is a RANGE or POINT index on a particular node or relationship property, and a spatial point is assigned to that property on a node or relationship, the node or relationship will be indexed. +If there is a range or point index on a particular node or relationship property, and a spatial point is assigned to that property on a node or relationship, the node or relationship will be indexed. -In a POINT index, Neo4j uses space filling curves in 2D or 3D over an underlying generalized B+Tree. This index has support for xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-equality-check-using-where-single-property-index[equality], xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-spatial-distance-searches-single-property-index[distance], and xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-spatial-bounding-box-searches-single-property-index[bounding box] queries. +In a point index, Neo4j uses space filling curves in 2D or 3D over an underlying generalized B+Tree. +Point indexes are optimized for distance and bounding box queries. +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-point-index[Managing indexes -> Point indexes]. -In a RANGE index, the points will be sorted according to their lexicographic ordering per coordinate reference system. For point values, this index has support for xref::planning-and-tuning/query-tuning/indexes.adoc#administration-indexes-equality-check-using-where-single-property-index[equality]. +In a range index, the points will be sorted according to their lexicographic ordering per coordinate reference system. For point values, this index has support for equality checks. +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-range-index[Managing indexes -> Range indexes]. [[spatial-values-comparability-orderability]] == Comparability and orderability From 7631aed5567c2a727192df4645b60d22af494095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 11 Jan 2024 10:29:27 +0100 Subject: [PATCH 350/383] Update elementId() (#831) --- modules/ROOT/pages/functions/index.adoc | 13 +++++- modules/ROOT/pages/functions/scalar.adoc | 58 +++++++++++++----------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/modules/ROOT/pages/functions/index.adoc b/modules/ROOT/pages/functions/index.adoc index 55450b53c..14bcb7c5c 100644 --- a/modules/ROOT/pages/functions/index.adoc +++ b/modules/ROOT/pages/functions/index.adoc @@ -104,6 +104,12 @@ label:new[Introduced in 5.13] | `coalesce(input :: ANY) :: ANY` | Returns the first non-null value in a list of expressions. +1.2+| xref::functions/scalar.adoc#functions-elementid[`elementId()`] +| `elementId(input :: NODE) :: STRING` +| Returns a node identifier, unique within a specific transaction and DBMS. +| `elementId(input :: RELATIONSHIP) :: STRING` +| Returns a relationship identifier, unique within a specific transaction and DBMS. + 1.1+| xref::functions/scalar.adoc#functions-endnode[`endNode()`] | `endNode(input :: RELATIONSHIP) :: NODE` | Returns the end `NODE` of a `RELATIONSHIP`. @@ -114,9 +120,12 @@ label:new[Introduced in 5.13] 1.2+| xref::functions/scalar.adoc#functions-id[`id()`] | `id(input :: NODE) :: INTEGER` -| label:deprecated[] Returns the id of a `NODE`. Replaced by `elementId()` +| label:deprecated[] Returns the id of a `NODE`. +Replaced by xref:functions/scalar.adoc#functions-elementid[`elementId()`]. | `id(input :: RELATIONSHIP) :: INTEGER` -| label:deprecated[] Returns the id of a `RELATIONSHIP`. Replaced by `elementId()`. +| label:deprecated[] Returns the id of a `RELATIONSHIP`. +Replaced by xref:functions/scalar.adoc#functions-elementid[`elementId()`]. + 1.1+| xref::functions/scalar.adoc#functions-last[`last()`] | `last(list :: LIST) :: ANY` diff --git a/modules/ROOT/pages/functions/scalar.adoc b/modules/ROOT/pages/functions/scalar.adoc index 52953fd76..1200f01c5 100644 --- a/modules/ROOT/pages/functions/scalar.adoc +++ b/modules/ROOT/pages/functions/scalar.adoc @@ -282,46 +282,50 @@ elementId(expression) |=== -.+elementId()+ +.+elementId() for nodes+ ====== -//// -CREATE - (alice:Developer {name:'Alice', age: 38, eyes: 'brown'}), - (bob {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel {name: 'Daniel', age: 54, eyes: 'brown'}), - (eskil {name: 'Eskil', age: 41, eyes: 'blue', liked_colors: ['pink', 'yellow', 'black']}), - (alice)-[:KNOWS]->(bob), - (alice)-[:KNOWS]->(charlie), - (bob)-[:KNOWS]->(daniel), - (charlie)-[:KNOWS]->(daniel), - (bob)-[:MARRIED]->(eskil) -//// - .Query [source, cypher] ---- -MATCH (a) -RETURN elementId(a) +MATCH (n:Developer) +RETURN elementId(n) ---- -The node identifier for each of the nodes is returned. +The identifier for each `Developer` node is returned. .Result [role="queryresult",options="header,footer",cols="1* Date: Thu, 11 Jan 2024 09:46:32 +0000 Subject: [PATCH 351/383] Bump the prod-dependencies group with 2 updates (#830) Bumps the prod-dependencies group with 2 updates: [@antora/cli](https://gitlab.com/antora/antora) and [@antora/site-generator-default](https://gitlab.com/antora/antora). Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Neil Dewhurst --- package-lock.json | 326 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 165 insertions(+), 165 deletions(-) diff --git a/package-lock.json b/package-lock.json index 459a3ce06..88f1c92b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "5.0.0", "license": "ISC", "dependencies": { - "@antora/cli": "^3.1.6", - "@antora/site-generator-default": "^3.1.6", + "@antora/cli": "^3.1.7", + "@antora/site-generator-default": "^3.1.7", "@neo4j-antora/antora-add-notes": "^0.3.1", "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", "@neo4j-antora/antora-page-roles": "^0.3.1", @@ -26,11 +26,11 @@ } }, "node_modules/@antora/asciidoc-loader": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.6.tgz", - "integrity": "sha512-Tqy4QFuZiKe/yX+3H8+vTLE6HH+VDm9OkKwq3G769jcC+40wz6y3poV4r4t1XJFAWwa/AKGM99ZcnJcJ3rtW+A==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.7.tgz", + "integrity": "sha512-sM/poPtAi8Cx0g2oLGHoEYjTdE9pvIYLgbHW07fGf6c9wQYMd2DMsevtbhNKWp+xqxj/QinToz4JOaNLoy1nfg==", "dependencies": { - "@antora/logger": "3.1.6", + "@antora/logger": "3.1.7", "@antora/user-require-helper": "~2.0", "@asciidoctor/core": "~2.2" }, @@ -39,12 +39,12 @@ } }, "node_modules/@antora/cli": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.6.tgz", - "integrity": "sha512-aJLN6JyXYUfMfMVr/6JEenxnnG8evKfyp01wd/Cj7mkJXD/lj/Gqws2bXyP/hjUEl/HuX4/P9AcIfMLT/vfQJw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.7.tgz", + "integrity": "sha512-yHo30VmiLLsZU4JW8VZR6fql9m5lIxocA2d0tduKQ+r4YSD1kt+bbwX3You3iMwW7oLoNo62zfU76F8CQBnm2g==", "dependencies": { - "@antora/logger": "3.1.6", - "@antora/playbook-builder": "3.1.6", + "@antora/logger": "3.1.7", + "@antora/playbook-builder": "3.1.7", "@antora/user-require-helper": "~2.0", "commander": "~10.0" }, @@ -56,18 +56,18 @@ } }, "node_modules/@antora/content-aggregator": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.6.tgz", - "integrity": "sha512-kOKWn/1gBvd9XOp00/wFzH4lb3yCa5u65ZKmfe9VwC7uOl5Tg9zT0lxMa7miEbPAmfhcOr0zRYXa2ybsoKBWNw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.7.tgz", + "integrity": "sha512-2gBbxaDxqY4QRw9Vut0IbKNqI9zAAohxjT0zcA5Am10kE3ywvzXaBa3tvb+A7vUl/vRcCC0LPM7pO37RVrbsGA==", "dependencies": { "@antora/expand-path-helper": "~2.0", - "@antora/logger": "3.1.6", + "@antora/logger": "3.1.7", "@antora/user-require-helper": "~2.0", "braces": "~3.0", "cache-directory": "~2.0", "glob-stream": "~7.0", "hpagent": "~1.2", - "isomorphic-git": "~1.21", + "isomorphic-git": "~1.25", "js-yaml": "~4.1", "multi-progress": "~4.0", "picomatch": "~2.3", @@ -81,12 +81,12 @@ } }, "node_modules/@antora/content-classifier": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.6.tgz", - "integrity": "sha512-e5Fs38Cfbl2kecxpRLFftflphbjg2wPfWlwjLZjs8d0R5ISSCM38q8ecDKCQHQlrYJkSrxiSpWqg0irNqAHnLw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.7.tgz", + "integrity": "sha512-94XwJ35pkWJU6dJqQg5oreJRCkaXwU6yw9XSyB731o4i/0romkazKnu7deHugqdgvzqn92AlMRG6R0clhJLEsw==", "dependencies": { - "@antora/asciidoc-loader": "3.1.6", - "@antora/logger": "3.1.6", + "@antora/asciidoc-loader": "3.1.7", + "@antora/logger": "3.1.7", "mime-types": "~2.1", "vinyl": "~2.2" }, @@ -95,11 +95,11 @@ } }, "node_modules/@antora/document-converter": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.6.tgz", - "integrity": "sha512-bdzlkwq1WMnfdc6FUZNcO58LwjMqYmv3m9dI/eAJryGiKa9ChBFskwA1ob7rB87Qxjzu6UHcNucbt910hjEOAw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.7.tgz", + "integrity": "sha512-cRVJf7QyclxjWbA0gWz7hncZYThIREikkwaxaa26LsRCfBNlw7wxs7lWejvIvEl1LVshupbinJwKUPPQPOsHhw==", "dependencies": { - "@antora/asciidoc-loader": "3.1.6" + "@antora/asciidoc-loader": "3.1.7" }, "engines": { "node": ">=16.0.0" @@ -114,9 +114,9 @@ } }, "node_modules/@antora/file-publisher": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.6.tgz", - "integrity": "sha512-UPTyFWY7lrG/Qj6SBxgoVBg1fW3JemJzW62y6pKuGHF59TEKJiaVTUlHEaVgkbpkCngn2os+VOX7fHK0jsBU9A==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.7.tgz", + "integrity": "sha512-UH2o0DJuv9BJvWgn+QSE3MhtHB3oN3lG5lJkV3fQi1jAV+qPIHoiTIYhbw02mj5KQ3Qbt7YWWAKZKuGl3rEdjg==", "dependencies": { "@antora/expand-path-helper": "~2.0", "@antora/user-require-helper": "~2.0", @@ -129,9 +129,9 @@ } }, "node_modules/@antora/logger": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.6.tgz", - "integrity": "sha512-36kU8gMbPslcPu8u9EbXsz6+9G9+LWPKhO7n8mEQqxlcCqmChwgetK6VbsL102rynpgPsstX6IAKg2wbptJK5g==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.7.tgz", + "integrity": "sha512-Z2tfNIi9G4BnAZq26Kp30974FxCVCtvH46FOi6ClnkJg6Uf2gTrVlJERmtsDTsHjWsf1qKbnj/4b99/AU31iQg==", "dependencies": { "@antora/expand-path-helper": "~2.0", "pino": "~8.14", @@ -143,22 +143,22 @@ } }, "node_modules/@antora/navigation-builder": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.6.tgz", - "integrity": "sha512-0iqktzBKQ4kgOM+pbm1bYdGUlN6Blw5zAxURr+W7X96b45mUHCTNz1nZrsDvBLbVXpSTk//ih85Ioh0RU4RJTw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.7.tgz", + "integrity": "sha512-QvMPb0qY1zfgyLCfuEhJOpO5qSVjaVe5X/bQjSii9vDGgpIEiC2yt/hgqER37E/3zsBGEZvCH5lSLk1c7x0+EQ==", "dependencies": { - "@antora/asciidoc-loader": "3.1.6" + "@antora/asciidoc-loader": "3.1.7" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/page-composer": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.6.tgz", - "integrity": "sha512-zl2UXVmHEy23zWsGzz3ZpnqtzTVfYvAVS7osPZpSyto3unwtQUI0dR+JpWndElsEDt71JJedbsXa/y/tNC2ZOQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.7.tgz", + "integrity": "sha512-zJMzYznPT6Vd59nEXio/2rolkX070Nup6g4a8d4RCz0WoE8dmMidw6XFgjAHr0Lyh14/FHgBPlYXfhkDFR16Mw==", "dependencies": { - "@antora/logger": "3.1.6", + "@antora/logger": "3.1.7", "handlebars": "~4.7", "require-from-string": "~2.0" }, @@ -167,9 +167,9 @@ } }, "node_modules/@antora/playbook-builder": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.6.tgz", - "integrity": "sha512-bZcDastZViAgPVPNvvbbw7ci63gL5YnyG5X7NuHJoORgzyGQAsMYEjzfa9yfNfXubUmXv/oSteUSxbACjdjzWg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.7.tgz", + "integrity": "sha512-lU80S1BqUy9DvqziEzRwpYTaWhOshxgrGAjf/F5VjAIaHCGVx0rZgfoI2rgFFkbVaH94kauOngdtTXDPXC1fPQ==", "dependencies": { "@iarna/toml": "~2.2", "convict": "~6.2", @@ -181,9 +181,9 @@ } }, "node_modules/@antora/redirect-producer": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.6.tgz", - "integrity": "sha512-tzlrJa2vny0HPBtIAgEM/xCMTfOi4z2CYUt4Ctz7rV8PBv6NOjlLkbu7Goc57CpR9wmJ3C4AGJcVHN0tah0FmA==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.7.tgz", + "integrity": "sha512-6zAHfcOb0v0829nAbn/3HMilbactjbjU7zBT9Iy6JHQfbqXT/g/mUT9N13Lj/wbq/nm0qKQJweB0Mi6BS2fbMw==", "dependencies": { "vinyl": "~2.2" }, @@ -192,23 +192,23 @@ } }, "node_modules/@antora/site-generator": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.6.tgz", - "integrity": "sha512-NKRTVDGB7CuzEBx68iQGweSTDXLvqccExEfBiudPEhyagcn4U+fwef+0LjE4A2imcpD/QQC7l5U8VaNObQYnRQ==", - "dependencies": { - "@antora/asciidoc-loader": "3.1.6", - "@antora/content-aggregator": "3.1.6", - "@antora/content-classifier": "3.1.6", - "@antora/document-converter": "3.1.6", - "@antora/file-publisher": "3.1.6", - "@antora/logger": "3.1.6", - "@antora/navigation-builder": "3.1.6", - "@antora/page-composer": "3.1.6", - "@antora/playbook-builder": "3.1.6", - "@antora/redirect-producer": "3.1.6", - "@antora/site-mapper": "3.1.6", - "@antora/site-publisher": "3.1.6", - "@antora/ui-loader": "3.1.6", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.7.tgz", + "integrity": "sha512-39KWip9bLdQ+4ssyqjI+O0COquKHxoeznxY2/3w5pNZEoeTg8cD7kOsnWfbocw0R3Rj+kJV5MnqICFNq0nuPeA==", + "dependencies": { + "@antora/asciidoc-loader": "3.1.7", + "@antora/content-aggregator": "3.1.7", + "@antora/content-classifier": "3.1.7", + "@antora/document-converter": "3.1.7", + "@antora/file-publisher": "3.1.7", + "@antora/logger": "3.1.7", + "@antora/navigation-builder": "3.1.7", + "@antora/page-composer": "3.1.7", + "@antora/playbook-builder": "3.1.7", + "@antora/redirect-producer": "3.1.7", + "@antora/site-mapper": "3.1.7", + "@antora/site-publisher": "3.1.7", + "@antora/ui-loader": "3.1.7", "@antora/user-require-helper": "~2.0" }, "engines": { @@ -216,22 +216,22 @@ } }, "node_modules/@antora/site-generator-default": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.6.tgz", - "integrity": "sha512-HXnqYYQdQHwjg6NAvEr3oolt4Kmb3cEVqMsR9BoN8ZPfVpnx+kM9r1/qqpgx9BZxY2oTDYPhFFeygdq2Fu5rIg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.7.tgz", + "integrity": "sha512-m9UbejttKzp8MKJTEc+aKXi5SNb864QO7lQiQzSR0fiWnIR8WIM73CPPwkVeOXdKqaJvQp5IF9rlXXTkkC19fw==", "dependencies": { - "@antora/site-generator": "3.1.6" + "@antora/site-generator": "3.1.7" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/site-mapper": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.6.tgz", - "integrity": "sha512-Jc5AqY2uS3wO21iwEFyJuXOspTLN6UdNnZP/Os2oguR+cSsjwUx+l6+X7lquIndq+dXUQS3tMQkwNkhLgfcsrw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.7.tgz", + "integrity": "sha512-x++89btbwk8FxyU2+H/RHQMnsC9sdvQvXcwXwNt11eXN1qj7t8TPiQZTalg7gkf0/osY4l7JRpGBY5JJfOJVig==", "dependencies": { - "@antora/content-classifier": "3.1.6", + "@antora/content-classifier": "3.1.7", "vinyl": "~2.2" }, "engines": { @@ -239,20 +239,20 @@ } }, "node_modules/@antora/site-publisher": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.6.tgz", - "integrity": "sha512-AOpM12gmMJeucebEGneHvOJAXQgco0lAg7Vx9CH7slHVeJy6mM74Mcut7KkKlv3AOJJKgYfdYkJndvq9dqbWmQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.7.tgz", + "integrity": "sha512-zHaJc7UeBfFSIhBbQ5U5Ud4u671M84oqSJb3pPxlUiJDP7iVJlSl+0GNm0NAIoDizjPtVksUI88fzqCy5rfSUQ==", "dependencies": { - "@antora/file-publisher": "3.1.6" + "@antora/file-publisher": "3.1.7" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@antora/ui-loader": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.6.tgz", - "integrity": "sha512-IivfKW7fCaV7GpXIOxyk8X2mJiYoM6U0CDaFzWiKerJeDikW1Oi9KGxCMe2+onYBJrgzQxAZsIzjr9fXUcDZWw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.7.tgz", + "integrity": "sha512-79QuZB0c91dveoESa3RcE18ZZFJo0rDZX9aJKHVc20dInQBGCgfURPqB2OytkzaXD3lNtDJ41yjKNYZqsAQy1Q==", "dependencies": { "@antora/expand-path-helper": "~2.0", "@vscode/gulp-vinyl-zip": "~2.5", @@ -456,9 +456,9 @@ } }, "node_modules/async-lock": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.0.tgz", - "integrity": "sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" }, "node_modules/atomic-sleep": { "version": "1.0.0", @@ -1573,9 +1573,9 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/isomorphic-git": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.21.0.tgz", - "integrity": "sha512-ZqCAUM63CYepA3fB8H7NVyPSiOkgzIbQ7T+QPrm9xtYgQypN9JUJ5uLMjB5iTfomdJf3mdm6aSxjZwnT6ubvEA==", + "version": "1.25.3", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.25.3.tgz", + "integrity": "sha512-iUaDB5kObupWpwjQ+EGkDQmaYQzbq1XaPqo+xJCCfbPViGZL94rU2RvK4EDJKpYbyiwbq5OSLEjkXHa8r5I1aw==", "dependencies": { "async-lock": "^1.1.0", "clean-git-ref": "^2.0.1", @@ -3062,39 +3062,39 @@ }, "dependencies": { "@antora/asciidoc-loader": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.6.tgz", - "integrity": "sha512-Tqy4QFuZiKe/yX+3H8+vTLE6HH+VDm9OkKwq3G769jcC+40wz6y3poV4r4t1XJFAWwa/AKGM99ZcnJcJ3rtW+A==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/asciidoc-loader/-/asciidoc-loader-3.1.7.tgz", + "integrity": "sha512-sM/poPtAi8Cx0g2oLGHoEYjTdE9pvIYLgbHW07fGf6c9wQYMd2DMsevtbhNKWp+xqxj/QinToz4JOaNLoy1nfg==", "requires": { - "@antora/logger": "3.1.6", + "@antora/logger": "3.1.7", "@antora/user-require-helper": "~2.0", "@asciidoctor/core": "~2.2" } }, "@antora/cli": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.6.tgz", - "integrity": "sha512-aJLN6JyXYUfMfMVr/6JEenxnnG8evKfyp01wd/Cj7mkJXD/lj/Gqws2bXyP/hjUEl/HuX4/P9AcIfMLT/vfQJw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/cli/-/cli-3.1.7.tgz", + "integrity": "sha512-yHo30VmiLLsZU4JW8VZR6fql9m5lIxocA2d0tduKQ+r4YSD1kt+bbwX3You3iMwW7oLoNo62zfU76F8CQBnm2g==", "requires": { - "@antora/logger": "3.1.6", - "@antora/playbook-builder": "3.1.6", + "@antora/logger": "3.1.7", + "@antora/playbook-builder": "3.1.7", "@antora/user-require-helper": "~2.0", "commander": "~10.0" } }, "@antora/content-aggregator": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.6.tgz", - "integrity": "sha512-kOKWn/1gBvd9XOp00/wFzH4lb3yCa5u65ZKmfe9VwC7uOl5Tg9zT0lxMa7miEbPAmfhcOr0zRYXa2ybsoKBWNw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/content-aggregator/-/content-aggregator-3.1.7.tgz", + "integrity": "sha512-2gBbxaDxqY4QRw9Vut0IbKNqI9zAAohxjT0zcA5Am10kE3ywvzXaBa3tvb+A7vUl/vRcCC0LPM7pO37RVrbsGA==", "requires": { "@antora/expand-path-helper": "~2.0", - "@antora/logger": "3.1.6", + "@antora/logger": "3.1.7", "@antora/user-require-helper": "~2.0", "braces": "~3.0", "cache-directory": "~2.0", "glob-stream": "~7.0", "hpagent": "~1.2", - "isomorphic-git": "~1.21", + "isomorphic-git": "~1.25", "js-yaml": "~4.1", "multi-progress": "~4.0", "picomatch": "~2.3", @@ -3105,22 +3105,22 @@ } }, "@antora/content-classifier": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.6.tgz", - "integrity": "sha512-e5Fs38Cfbl2kecxpRLFftflphbjg2wPfWlwjLZjs8d0R5ISSCM38q8ecDKCQHQlrYJkSrxiSpWqg0irNqAHnLw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/content-classifier/-/content-classifier-3.1.7.tgz", + "integrity": "sha512-94XwJ35pkWJU6dJqQg5oreJRCkaXwU6yw9XSyB731o4i/0romkazKnu7deHugqdgvzqn92AlMRG6R0clhJLEsw==", "requires": { - "@antora/asciidoc-loader": "3.1.6", - "@antora/logger": "3.1.6", + "@antora/asciidoc-loader": "3.1.7", + "@antora/logger": "3.1.7", "mime-types": "~2.1", "vinyl": "~2.2" } }, "@antora/document-converter": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.6.tgz", - "integrity": "sha512-bdzlkwq1WMnfdc6FUZNcO58LwjMqYmv3m9dI/eAJryGiKa9ChBFskwA1ob7rB87Qxjzu6UHcNucbt910hjEOAw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/document-converter/-/document-converter-3.1.7.tgz", + "integrity": "sha512-cRVJf7QyclxjWbA0gWz7hncZYThIREikkwaxaa26LsRCfBNlw7wxs7lWejvIvEl1LVshupbinJwKUPPQPOsHhw==", "requires": { - "@antora/asciidoc-loader": "3.1.6" + "@antora/asciidoc-loader": "3.1.7" } }, "@antora/expand-path-helper": { @@ -3129,9 +3129,9 @@ "integrity": "sha512-CSMBGC+tI21VS2kGW3PV7T2kQTM5eT3f2GTPVLttwaNYbNxDve08en/huzszHJfxo11CcEs26Ostr0F2c1QqeA==" }, "@antora/file-publisher": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.6.tgz", - "integrity": "sha512-UPTyFWY7lrG/Qj6SBxgoVBg1fW3JemJzW62y6pKuGHF59TEKJiaVTUlHEaVgkbpkCngn2os+VOX7fHK0jsBU9A==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/file-publisher/-/file-publisher-3.1.7.tgz", + "integrity": "sha512-UH2o0DJuv9BJvWgn+QSE3MhtHB3oN3lG5lJkV3fQi1jAV+qPIHoiTIYhbw02mj5KQ3Qbt7YWWAKZKuGl3rEdjg==", "requires": { "@antora/expand-path-helper": "~2.0", "@antora/user-require-helper": "~2.0", @@ -3141,9 +3141,9 @@ } }, "@antora/logger": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.6.tgz", - "integrity": "sha512-36kU8gMbPslcPu8u9EbXsz6+9G9+LWPKhO7n8mEQqxlcCqmChwgetK6VbsL102rynpgPsstX6IAKg2wbptJK5g==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/logger/-/logger-3.1.7.tgz", + "integrity": "sha512-Z2tfNIi9G4BnAZq26Kp30974FxCVCtvH46FOi6ClnkJg6Uf2gTrVlJERmtsDTsHjWsf1qKbnj/4b99/AU31iQg==", "requires": { "@antora/expand-path-helper": "~2.0", "pino": "~8.14", @@ -3152,27 +3152,27 @@ } }, "@antora/navigation-builder": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.6.tgz", - "integrity": "sha512-0iqktzBKQ4kgOM+pbm1bYdGUlN6Blw5zAxURr+W7X96b45mUHCTNz1nZrsDvBLbVXpSTk//ih85Ioh0RU4RJTw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/navigation-builder/-/navigation-builder-3.1.7.tgz", + "integrity": "sha512-QvMPb0qY1zfgyLCfuEhJOpO5qSVjaVe5X/bQjSii9vDGgpIEiC2yt/hgqER37E/3zsBGEZvCH5lSLk1c7x0+EQ==", "requires": { - "@antora/asciidoc-loader": "3.1.6" + "@antora/asciidoc-loader": "3.1.7" } }, "@antora/page-composer": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.6.tgz", - "integrity": "sha512-zl2UXVmHEy23zWsGzz3ZpnqtzTVfYvAVS7osPZpSyto3unwtQUI0dR+JpWndElsEDt71JJedbsXa/y/tNC2ZOQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/page-composer/-/page-composer-3.1.7.tgz", + "integrity": "sha512-zJMzYznPT6Vd59nEXio/2rolkX070Nup6g4a8d4RCz0WoE8dmMidw6XFgjAHr0Lyh14/FHgBPlYXfhkDFR16Mw==", "requires": { - "@antora/logger": "3.1.6", + "@antora/logger": "3.1.7", "handlebars": "~4.7", "require-from-string": "~2.0" } }, "@antora/playbook-builder": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.6.tgz", - "integrity": "sha512-bZcDastZViAgPVPNvvbbw7ci63gL5YnyG5X7NuHJoORgzyGQAsMYEjzfa9yfNfXubUmXv/oSteUSxbACjdjzWg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/playbook-builder/-/playbook-builder-3.1.7.tgz", + "integrity": "sha512-lU80S1BqUy9DvqziEzRwpYTaWhOshxgrGAjf/F5VjAIaHCGVx0rZgfoI2rgFFkbVaH94kauOngdtTXDPXC1fPQ==", "requires": { "@iarna/toml": "~2.2", "convict": "~6.2", @@ -3181,63 +3181,63 @@ } }, "@antora/redirect-producer": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.6.tgz", - "integrity": "sha512-tzlrJa2vny0HPBtIAgEM/xCMTfOi4z2CYUt4Ctz7rV8PBv6NOjlLkbu7Goc57CpR9wmJ3C4AGJcVHN0tah0FmA==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/redirect-producer/-/redirect-producer-3.1.7.tgz", + "integrity": "sha512-6zAHfcOb0v0829nAbn/3HMilbactjbjU7zBT9Iy6JHQfbqXT/g/mUT9N13Lj/wbq/nm0qKQJweB0Mi6BS2fbMw==", "requires": { "vinyl": "~2.2" } }, "@antora/site-generator": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.6.tgz", - "integrity": "sha512-NKRTVDGB7CuzEBx68iQGweSTDXLvqccExEfBiudPEhyagcn4U+fwef+0LjE4A2imcpD/QQC7l5U8VaNObQYnRQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/site-generator/-/site-generator-3.1.7.tgz", + "integrity": "sha512-39KWip9bLdQ+4ssyqjI+O0COquKHxoeznxY2/3w5pNZEoeTg8cD7kOsnWfbocw0R3Rj+kJV5MnqICFNq0nuPeA==", "requires": { - "@antora/asciidoc-loader": "3.1.6", - "@antora/content-aggregator": "3.1.6", - "@antora/content-classifier": "3.1.6", - "@antora/document-converter": "3.1.6", - "@antora/file-publisher": "3.1.6", - "@antora/logger": "3.1.6", - "@antora/navigation-builder": "3.1.6", - "@antora/page-composer": "3.1.6", - "@antora/playbook-builder": "3.1.6", - "@antora/redirect-producer": "3.1.6", - "@antora/site-mapper": "3.1.6", - "@antora/site-publisher": "3.1.6", - "@antora/ui-loader": "3.1.6", + "@antora/asciidoc-loader": "3.1.7", + "@antora/content-aggregator": "3.1.7", + "@antora/content-classifier": "3.1.7", + "@antora/document-converter": "3.1.7", + "@antora/file-publisher": "3.1.7", + "@antora/logger": "3.1.7", + "@antora/navigation-builder": "3.1.7", + "@antora/page-composer": "3.1.7", + "@antora/playbook-builder": "3.1.7", + "@antora/redirect-producer": "3.1.7", + "@antora/site-mapper": "3.1.7", + "@antora/site-publisher": "3.1.7", + "@antora/ui-loader": "3.1.7", "@antora/user-require-helper": "~2.0" } }, "@antora/site-generator-default": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.6.tgz", - "integrity": "sha512-HXnqYYQdQHwjg6NAvEr3oolt4Kmb3cEVqMsR9BoN8ZPfVpnx+kM9r1/qqpgx9BZxY2oTDYPhFFeygdq2Fu5rIg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/site-generator-default/-/site-generator-default-3.1.7.tgz", + "integrity": "sha512-m9UbejttKzp8MKJTEc+aKXi5SNb864QO7lQiQzSR0fiWnIR8WIM73CPPwkVeOXdKqaJvQp5IF9rlXXTkkC19fw==", "requires": { - "@antora/site-generator": "3.1.6" + "@antora/site-generator": "3.1.7" } }, "@antora/site-mapper": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.6.tgz", - "integrity": "sha512-Jc5AqY2uS3wO21iwEFyJuXOspTLN6UdNnZP/Os2oguR+cSsjwUx+l6+X7lquIndq+dXUQS3tMQkwNkhLgfcsrw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/site-mapper/-/site-mapper-3.1.7.tgz", + "integrity": "sha512-x++89btbwk8FxyU2+H/RHQMnsC9sdvQvXcwXwNt11eXN1qj7t8TPiQZTalg7gkf0/osY4l7JRpGBY5JJfOJVig==", "requires": { - "@antora/content-classifier": "3.1.6", + "@antora/content-classifier": "3.1.7", "vinyl": "~2.2" } }, "@antora/site-publisher": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.6.tgz", - "integrity": "sha512-AOpM12gmMJeucebEGneHvOJAXQgco0lAg7Vx9CH7slHVeJy6mM74Mcut7KkKlv3AOJJKgYfdYkJndvq9dqbWmQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/site-publisher/-/site-publisher-3.1.7.tgz", + "integrity": "sha512-zHaJc7UeBfFSIhBbQ5U5Ud4u671M84oqSJb3pPxlUiJDP7iVJlSl+0GNm0NAIoDizjPtVksUI88fzqCy5rfSUQ==", "requires": { - "@antora/file-publisher": "3.1.6" + "@antora/file-publisher": "3.1.7" } }, "@antora/ui-loader": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.6.tgz", - "integrity": "sha512-IivfKW7fCaV7GpXIOxyk8X2mJiYoM6U0CDaFzWiKerJeDikW1Oi9KGxCMe2+onYBJrgzQxAZsIzjr9fXUcDZWw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@antora/ui-loader/-/ui-loader-3.1.7.tgz", + "integrity": "sha512-79QuZB0c91dveoESa3RcE18ZZFJo0rDZX9aJKHVc20dInQBGCgfURPqB2OytkzaXD3lNtDJ41yjKNYZqsAQy1Q==", "requires": { "@antora/expand-path-helper": "~2.0", "@vscode/gulp-vinyl-zip": "~2.5", @@ -3405,9 +3405,9 @@ } }, "async-lock": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.0.tgz", - "integrity": "sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" }, "atomic-sleep": { "version": "1.0.0", @@ -4246,9 +4246,9 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "isomorphic-git": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.21.0.tgz", - "integrity": "sha512-ZqCAUM63CYepA3fB8H7NVyPSiOkgzIbQ7T+QPrm9xtYgQypN9JUJ5uLMjB5iTfomdJf3mdm6aSxjZwnT6ubvEA==", + "version": "1.25.3", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.25.3.tgz", + "integrity": "sha512-iUaDB5kObupWpwjQ+EGkDQmaYQzbq1XaPqo+xJCCfbPViGZL94rU2RvK4EDJKpYbyiwbq5OSLEjkXHa8r5I1aw==", "requires": { "async-lock": "^1.1.0", "clean-git-ref": "^2.0.1", diff --git a/package.json b/package.json index a25fa946c..e15cf3ae6 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "author": "Neo4j", "license": "ISC", "dependencies": { - "@antora/cli": "^3.1.6", - "@antora/site-generator-default": "^3.1.6", + "@antora/cli": "^3.1.7", + "@antora/site-generator-default": "^3.1.7", "@neo4j-antora/antora-add-notes": "^0.3.1", "@neo4j-antora/antora-modify-sitemaps": "^0.4.4", "@neo4j-antora/antora-page-roles": "^0.3.1", From 1fe575f15ddffd9625cce644bf3100cb7afa31c4 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 11 Jan 2024 14:40:51 +0000 Subject: [PATCH 352/383] Run pr checks on publishable branches (#833) --- .github/workflows/docs-pr-checks.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-pr-checks.yml b/.github/workflows/docs-pr-checks.yml index b4c1ff54a..1072ac3f5 100644 --- a/.github/workflows/docs-pr-checks.yml +++ b/.github/workflows/docs-pr-checks.yml @@ -4,7 +4,10 @@ name: "Verify Docs PR" on: pull_request: branches: - - dev + - "dev" + - "5.x" + - "4.[0-9]" + - "3.5" jobs: From 781a64ac28510f539925188f86054189598d039d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:40:48 +0100 Subject: [PATCH 353/383] Add note to labels() function (#835) --- modules/ROOT/pages/functions/list.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ROOT/pages/functions/list.adoc b/modules/ROOT/pages/functions/list.adoc index d5587169b..a5df28286 100644 --- a/modules/ROOT/pages/functions/list.adoc +++ b/modules/ROOT/pages/functions/list.adoc @@ -100,6 +100,9 @@ A `LIST` containing the names of all the properties on the node bound to `labels` returns a `LIST` containing the string representations for all the labels of a `NODE`. +[NOTE] +The order of the returned labels is not guanteed when using the `labels()` function. + *Syntax:* [source, syntax, role="noheader"] From f48031cc73802f96b1e09fbc7dea317cf7964940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:09:26 +0100 Subject: [PATCH 354/383] Convert Constraint Types to headers (#837) (#838) This would allow us to link users to a specific type of constraint. --------- Co-authored-by: Adam Cowley --- modules/ROOT/pages/constraints/index.adoc | 87 +++++++++++++++-------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/modules/ROOT/pages/constraints/index.adoc b/modules/ROOT/pages/constraints/index.adoc index e79ed3d5d..8b3841160 100644 --- a/modules/ROOT/pages/constraints/index.adoc +++ b/modules/ROOT/pages/constraints/index.adoc @@ -3,47 +3,76 @@ [[constraints]] = Constraints -This page contains an overview of the available constraints in Cypher. +This page contains an overview of the available constraints in Cypher, and information about how constraints can impact indexes. -[[types-of-constraint]] -== Types of constraint +Adding constraints is an atomic operation that can take a while -- all existing data has to be scanned before a Neo4j DBMS can use a constraint. -The following constraint types are available: - -*Unique node property constraints*:: +[[unique-node-property]] +== Unique node property constraints Unique node property constraints, or node property uniqueness constraints, ensure that property values are unique for all nodes with a specific label. For property uniqueness constraints on multiple properties, the combination of the property values is unique. Node property uniqueness constraints do not require all nodes to have a unique value for the properties listed (nodes without all properties on which the constraint exists are not subject to this rule). -*Unique relationship property constraints* label:new[Introduced in 5.7]:: +For more information, see xref:constraints/examples.adoc#constraints-examples-node-uniqueness[examples of node property uniqueness constraints]. + +[[unique-relationship-property]] +== Unique relationship property constraints +label:new[Introduced in 5.7] + Unique relationship property constraints, or relationship property uniqueness constraints, ensure that property values are unique for all relationships with a specific type. For property uniqueness constraints on multiple properties, the combination of the property values is unique. Relationship property uniqueness constraints do not require all relationships to have a unique value for the properties listed (relationships without all properties on which the constraint exists are not subject to this rule). -*Node property existence constraints* label:enterprise-edition[]:: +For more information, see xref:constraints/examples.adoc#constraints-examples-relationship-uniqueness[examples of relationship property uniqueness constraints]. + +[[node-property-existence]] +[role=enterprise-edition] +== Node property existence constraints + Node property existence constraints ensure that a property exists for all nodes with a specific label. Queries that try to create new nodes of the specified label, but without this property, will fail. The same is true for queries that try to remove the mandatory property. -*Relationship property existence constraints* label:enterprise-edition[]:: +For more information, see xref:constraints/examples.adoc#constraints-examples-node-property-existence[examples of node property existence constraints]. + +[[relationship-property-existence]] +[role=enterprise-edition] +== Relationship property existence constraints + Relationship property existence constraints ensure that a property exists for all relationships with a specific type. All queries that try to create relationships of the specified type, but without this property, will fail. The same is true for queries that try to remove the mandatory property. -*Node property type constraints* label:new[Introduced in 5.9] label:enterprise-edition[]:: +For more information, see xref:constraints/examples.adoc#constraints-examples-relationship-property-existence[examples of relationship property existence constraints]. + +[[node-property-type]] +[role=enterprise-edition] +== Node property type constraints +label:new[Introduced in 5.9] + Node property type constraints ensure that a property have the required property type for all nodes with a specific label. Queries that try to add or modify this property to nodes of the specified label, but with a different property type, will fail. Node property type constraints do not require all nodes to have the property (nodes without the property on which the constraint exists are not subject to this rule). -*Relationship property type constraints* label:new[Introduced in 5.9] label:enterprise-edition[]:: +For more information, see xref:constraints/examples.adoc#constraints-examples-node-property-type[examples of node property type constraints]. + +[[relationship-property-type]] +[role=enterprise-edition] +== Relationship property type constraints +label:new[Introduced in 5.9] + Relationship property type constraints ensure that a property have the required property type for all relationships with a specific type. Queries that try to add or modify this property to relationships of the specified type, but with a different property type, will fail. Relationship property type constraints do not require all relationships to have the property (relationships without the property on which the constraint exists are not subject to this rule). -*Node key constraints* label:enterprise-edition[]:: +For more information, see xref:constraints/examples.adoc#constraints-examples-relationship-property-type[examples of relationship property type constraints]. + +[[node-key]] +[role=enterprise-edition] +== Node key constraints + Node key constraints ensure that, for a given label and set of properties: -+ -[lowerroman] + . All the properties exist on all the nodes with that label. . The combination of the property values is unique. @@ -54,9 +83,15 @@ Queries attempting to do any of the following will fail: * Remove one of the mandatory properties. * Update the properties so that the combination of property values is no longer unique. -*Relationship key constraints* label:new[Introduced in 5.7] label:enterprise-edition[]:: +For more information, see xref:constraints/examples.adoc#constraints-examples-node-key[examples of node key constraints]. + +[[relationship-key]] +[role=enterprise-edition] +== Relationship key constraints +label:new[Introduced in 5.7] + Relationship key constraints ensure that, for a given type and set of properties: -+ + [lowerroman] . All the properties exist on all the relationships with that type. . The combination of the property values is unique. @@ -68,17 +103,17 @@ Queries attempting to do any of the following will fail: * Remove one of the mandatory properties. * Update the properties so that the combination of property values is no longer unique. +For more information, see xref:constraints/examples.adoc#constraints-examples-relationship-key[examples of relationship key constraints]. + +[[multiple-constrains]] +== Multiple constraints on the same property combinations -[NOTE] -==== -Node key constraints, relationship key constraints, node property existence constraints, relationship property existence, node property type constraints, and relationship property type constraints are not available in Neo4j Community Edition. -Databases containing one of these constraint types cannot be opened using Neo4j Community Edition. -==== Some constraint types are allowed on the same label/relationship type and property combination. For example, it is possible to have a uniqueness and an existence constraint on the same label/relationship type and property combination, though this would be the equivalent of having a node or relationship key constraint. A more useful example would be to combine a property type and an existence constraint to ensure that the property exists and has the given type. +[[index-implications]] == Implications on indexes Creating a constraint has the following implications on indexes: @@ -89,13 +124,3 @@ Creating a constraint has the following implications on indexes: Refer to xref:indexes/search-performance-indexes/managing-indexes.adoc[] for more details on indexes. * If a node key, relationship key, or property uniqueness constraint is dropped and the backing index is still required, the index need to be created explicitly. -Additionally, the following is true for constraints: - -* A given label or relationship type can have multiple constraints, and uniqueness and property existence constraints can be combined on the same property. -* Adding constraints is an atomic operation that can take a while -- all existing data has to be scanned before Neo4j DBMS can turn the constraint 'on'. -* Best practice is to give the constraint a name when it is created. -If the constraint is not explicitly named, it will get an auto-generated name. -* The constraint name must be unique among both indexes and constraints. -* Constraint creation is by default not idempotent, and an error will be thrown if you attempt to create the same constraint twice. -Using the keyword `IF NOT EXISTS` makes the command idempotent, and no error will be thrown if you attempt to create the same constraint twice. - From 4fcd9efd53ec4fa220ba55c2140409c5b56c63eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:20:51 +0100 Subject: [PATCH 355/383] Index chapter clean up (#840) --- .../managing-indexes.adoc | 2 +- .../using-indexes.adoc | 7 ++++--- .../semantic-indexes/full-text-indexes.adoc | 12 ++++++++---- .../semantic-indexes/vector-indexes.adoc | 19 +++++++------------ modules/ROOT/pages/indexes/syntax.adoc | 5 +++++ 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc index acb4fcb33..e484fcfae 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc @@ -1043,7 +1043,7 @@ If an index with that name exists it is removed, if not the command fails. _This feature was introduced in Neo4j 5.16._ -The following statement will attempt to drop the index named `node_range_index_name` using a parameter for the index name. +The following statement will attempt to drop the index named `range_index_param` using a parameter for the index name. .Parameters [source,javascript, indent=0] diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc index aab706148..21f28a242 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc @@ -346,7 +346,7 @@ This is especially beneficial when dealing with complex, large geo-spatial data, [[composite-indexes]] == Composite indexes -It is possible to create an index on a single property or multiple properties. +It is possible to create a range index on a single property or multiple properties (text and point indexes are single-property only). The latter are called composite indexes and can be useful if queries against a database frequently filter on _all_ the properties indexed by the composite index. The following example first creates a composite index on `PointOfInterest` nodes for the properties `name` and `type`, and then queries the graph using the xref:patterns/concepts.adoc#shortest-path[shortestPath function] to determine both the path length (in terms of traversed relationships in the graph) and geographical distance between the `Zoo School` and its nearest `tennis pitch` (note that there are 32 unique `PointOfInterest` `tennis pitch` nodes in the graph): @@ -360,6 +360,7 @@ CREATE INDEX composite_index FOR (n:PointOfInterest) ON (n.name, n.type) .Query with a filter on both properties indexed by the composite index [source,cypher] ---- +PROFILE MATCH (tennisPitch: PointOfInterest {name: 'tennis', type: 'pitch'}) WITH tennisPitch MATCH path = shortestPath((tennisPitch)-[:ROUTE*]-(:PointOfInterest {name: 'Zoo School'})) @@ -538,7 +539,7 @@ These rules can be important when creating composite indexes, as some checks are For instance, it is generally more efficient for the planner to perform an equality check on a property than an existence check. Depending on the queries and the application, it may, therefore, be cost-effective to consider the order in which properties are defined when creating a composite index. -Additionally, it bears repeating that composite indexes can only be used if a predicate filters on all the properties indexed by the composite index, and that composite indexes can only be created for range indexes (point and text indexes are single-property only). +Additionally, it bears repeating that composite indexes can only be used if a predicate filters on all the properties indexed by the composite index, and that composite indexes can only be created for range indexes. [[range-index-backed-order-by]] == Range index-backed ORDER BY @@ -1005,7 +1006,7 @@ The order in which the properties are defined when creating a composite index im * A Cypher query can use several indexes if the planner deems it beneficial to the performance of a query. -* * Neo4j indexes do not store `null` values, and the planner must be able to rule out any entities with properties containing `null` values in order to use an index. +* Neo4j indexes do not store `null` values, and the planner must be able to rule out any entities with properties containing `null` values in order to use an index. There are several strategies to ensure the use of indexes. * The columns `lastRead`, `readCount`, and `trackedSince` returned by the `SHOW INDEX` command can be used to identify redundant indexes that take up unnecessary space. diff --git a/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc b/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc index 769ed3cf6..cf199c306 100644 --- a/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc +++ b/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc @@ -31,13 +31,19 @@ CREATE (nilsE:Employee {name: "Nils-Erik Karlsson", position: "Engineer", team: == Create full-text indexes Full-text indexes are created with the `CREATE FULLTEXT INDEX` command. -An index can be given a unique name when created, which is used to reference the index when querying or dropping it. +It is recommended to to give the index a name when it is created. If no name is given when created, a random name will be assigned to the full-text index. -When creating a full-text index, you need to specify the labels/relationship types and property names it should apply to. + +The `CREATE FULLTEXT INDEX` command is optionally idempotent. +This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. +If `IF NOT EXISTS` is appended to the command, no error is thrown and nothing happens should an index with the same name or a full-text index on the same schema already exist. +As of Neo4j 5.16, the index name can also be given as a parameter, `CREATE FULLTEXT INDEX $name FOR ...`. [TIP] Creating a full-text index requires the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[`CREATE INDEX` privilege]. +When creating a full-text index, you need to specify the labels/relationship types and property names it should apply to. + This statement creates a full-text index named `namesAndTeams` on each `name` and `team` property for nodes with the label `Employee` or `Manager`: .Create a full-text index on a node label and property combination @@ -62,8 +68,6 @@ This statement creates a full-text index named `communications` on the `message` CREATE FULLTEXT INDEX communications FOR ()-[r:REVIEWED|EMAILED]-() ON EACH [r.message] ---- -Full-text indexes follow the same xref:indexes/index.adoc#naming-rules-and-recommendations[naming rules and best-practices] as search-performance indexes. - [[tokenization-analyzers]] === Tokenization and analyzers diff --git a/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc index a37e482a8..e867a33a1 100644 --- a/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc +++ b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc @@ -11,7 +11,7 @@ :l2-norm: image:l2.svg["l2"]-norm [[indexes-vector]] -= Vector search index += Vector search indexes _Vector search indexes were released as a public beta in Neo4j 5.11 and general availability in Neo4j 5.13._ @@ -93,10 +93,6 @@ A vector index is a single-label, single-property index for nodes. A vector index needs to be configured with both the dimensionality of the vector (`INTEGER` between `1` and `2048` _inclusive_), and the measure of similarity between two vectors (case-insensitive `STRING`). For details, see xref:#indexes-vector-similarity[]. -[NOTE] -==== -More details about the syntax descriptions can be found link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/syntax/#administration-syntax-reading[here]. -==== .Syntax for creating vector indexes [options="header", width="100%", cols="5a, 3"] @@ -296,8 +292,6 @@ A vector index is dropped by using the xref:indexes/search-performance-indexes/m Dropping a vector index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. -Dropping a vector index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. - .+DROP INDEX+ ====== @@ -343,12 +337,12 @@ db.create.setVectorProperty(node :: NODE, key :: STRING, vector :: LIST) The following example shows how to define embeddings as Cypher parameters by matching a node and setting its vector properties using `db.create.setNodeVectorProperty`: -.Set a vector via `db.create.setVectorProperty` +.Set a vector via `db.create.setNodeVectorProperty` [source,cypher] ---- MATCH (n:Node {id: $id}) CALL db.create.setNodeVectorProperty(n, 'propertyKey', $vector) -RETURN node +RETURN n ---- Furthermore, you can also use a list parameter containing several `MATCH` criteria and embeddings to update multiple nodes in an `UNWIND` clause. @@ -437,9 +431,6 @@ The requested _k_ nearest neighbors may not be the exact _k_ nearest, but close * For large requested nearest neighbors, _k_, close to the total number of indexed vectors, the search may retrieve fewer than _k_ results. -* The index must have a unique name. -There is no provided method for an autogenerated name. - * Only one vector index can be over a schema. For example, you cannot have one xref:indexes-vector-similarity-euclidean[Euclidean] and one xref:indexes-vector-similarity-cosine[cosine] vector index on the same label-property key pair. @@ -459,6 +450,10 @@ The following table lists the known issues and the version in which they were fi |=== | Known issues | Fixed in +| Vector indexes cannot be assigned autogenerated names. + +| Neo4j 5.15 + | There is no Cypher syntax for creating a vector index. [TIP] diff --git a/modules/ROOT/pages/indexes/syntax.adoc b/modules/ROOT/pages/indexes/syntax.adoc index 777ef49de..52cf1d6b7 100644 --- a/modules/ROOT/pages/indexes/syntax.adoc +++ b/modules/ROOT/pages/indexes/syntax.adoc @@ -4,6 +4,8 @@ This page contains the syntax for creating, listing, and dropping the indexes available in Neo4j. It also contains the signatures for the procedures necessary to call in order to use full-text and vector indexes. +More details about the syntax can be found in the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/syntax/[Operations Manual -> Cypher syntax for administration commands]. + [[create-index]] == CREATE INDEX @@ -263,6 +265,9 @@ The `query` vector refers to the `LIST` in which to search for the neighb [[drop-index]] == DROP INDEX +The `DROP INDEX` command can drop indexes of all types using their name. +The name of the index can be found using the `SHOW INDEXES` command, given in the output column `name`. + The `DROP INDEX` command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. From ca21aeb6722feac445629933787f61afef6cae65 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Thu, 18 Jan 2024 13:39:54 +0100 Subject: [PATCH 356/383] Skip result test check on vector index result missing embeddings (#843) --- modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc index e867a33a1..d47ed18bc 100644 --- a/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc +++ b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc @@ -225,7 +225,7 @@ First you `MATCH` to find the paper, and then you query the `abstract-embeddings Finally, you `MATCH` for the neighborhood's respective titles. .Query -[source,cypher] +[source,cypher, role=test-result-skip] ---- MATCH (title:Title)<--(:Paper)-->(abstract:Abstract) WHERE toLower(title.text) = 'efficient and robust approximate nearest neighbor search using From c55937de4e84304aa6de4b46c47981f1df5cd4d7 Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Thu, 18 Jan 2024 14:06:45 +0100 Subject: [PATCH 357/383] Add operators that are missing on the detail page (#839) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- .../operators/operators-detail.adoc | 484 +++++++++++++++++- 1 file changed, 464 insertions(+), 20 deletions(-) diff --git a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc index 3ebb58b75..df89894cf 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc @@ -311,11 +311,54 @@ Total database accesses: 2, total allocated memory: 184 ====== +[[query-plan-directed-relationship-by-element-id-seek]] +== Directed Relationship By Element Id Seek +// DirectedRelationshipByElementIdSeek + +The `DirectedRelationshipByElementIdSeek` operator reads one or more relationships by element id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]) and produces the relationship as well as the source and target node of the relationship. + + +.DirectedRelationshipByElementIdSeek +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (n1)-[r]->() +WHERE elementId(r) = 0 +RETURN r, n1 +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r, n1 | 1 | 0 | 0 | 0 | 0/0 | 0.314 | | +| | +----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +DirectedRelationshipByElementIdSeek | 1 | (n1)-[r]->(anon_0) WHERE elementId(r) = $autoint_0 | 1 | 0 | 0 | 248 | 0/0 | 2.337 | In Pipeline 0 | ++--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ + +Total database accesses: 0, total allocated memory: 312 +---- + +====== + [[query-plan-directed-relationship-by-id-seek]] == Directed Relationship By Id Seek // DirectedRelationshipByIdSeek -The `DirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store, and produces both the relationship and the nodes on either side. +The `DirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store, and produces the relationship as well as the source and target node of the relationship. .DirectedRelationshipByIdSeek @@ -326,7 +369,7 @@ The `DirectedRelationshipByIdSeek` operator reads one or more relationships by i ---- PROFILE MATCH (n1)-[r]->() -WHERE elementId(r) = 0 +WHERE id(r) = 0 RETURN r, n1 ---- @@ -341,31 +384,28 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | r, n1 | 1 | 0 | 0 | | | | | -| | +----+---------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 1 | elementId(r) = $autoint_0 | 1 | 0 | 0 | | | | | -| | +----+---------------------------+----------------+------+---------+----------------+ | | | -| +DirectedAllRelationshipsScan | 2 | (n1)-[r]->(anon_0) | 3 | 0 | 0 | 248 | 1/0 | 0.134 | Fused in Pipeline 0 | -+-------------------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | r, n1 | 1 | 1 | 7 | 0 | | | | +| | +----+---------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipByIdSeek | 1 | (n1)-[r]->(anon_0) WHERE id(r) = $autoint_0 | 1 | 1 | 1 | 248 | 3/0 | 0.483 | Fused in Pipeline 0 | ++-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 8, total allocated memory: 312 ---- ====== +[[query-plan-undirected-relationship-by-element-id-seek]] +== Undirected Relationship By Element Id Seek +// UndirectedRelationshipByElementIdSeek -[[query-plan-undirected-relationship-by-id-seek]] -== Undirected Relationship By Id Seek -// UndirectedRelationshipByIdSeek - -The `UndirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store. +The `UndirectedRelationshipByElementIdSeek` operator reads one or more relationships by element id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]). As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. -.UndirectedRelationshipByIdSeek +.UndirectedRelationshipByElementIdSeek ====== .Query @@ -401,6 +441,50 @@ Total database accesses: 1, total allocated memory: 184 ====== +[[query-plan-undirected-relationship-by-id-seek]] +== Undirected Relationship By Id Seek +// UndirectedRelationshipByIdSeek + +The `UndirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]). +As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. + + +.UndirectedRelationshipByIdSeek +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (n1)-[r]-() +WHERE id(r) = 1 +RETURN r, n1 +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | r, n1 | 2 | 2 | 14 | 0 | | | | +| | +----+--------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipByIdSeek | 1 | (n1)-[r]-(anon_0) WHERE id(r) = $autoint_0 | 2 | 2 | 1 | 248 | 3/0 | 1.005 | Fused in Pipeline 0 | ++---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 15, total allocated memory: 312 +---- + +====== + [[query-plan-directed-relationship-index-contains-scan]] == Directed Relationship Index Contains Scan @@ -1064,12 +1148,57 @@ Total database accesses: 1, total allocated memory: 184 == Node By Id Seek // NodeByIdSeek -The `NodeByIdSeek` operator reads one or more nodes by ID from the node store, specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]. +The `NodeByIdSeek` operator reads one or more nodes by id from the node store, specified via the function xref::functions/scalar.adoc#functions-id[id()]. .NodeByIdSeek ====== +.Query +[source, cypher] +---- +PROFILE +MATCH (n) +WHERE id(n) = 0 +RETURN n +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | n | 1 | 1 | 2 | 0 | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +NodeByIdSeek | 1 | n WHERE id(n) = $autoint_0 | 1 | 1 | 1 | 248 | 2/0 | 1.109 | Fused in Pipeline 0 | ++-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 3, total allocated memory: 312 + +1 row +---- + +====== + +[[query-plan-node-by-element-id-seek]] +== Node By Element Id Seek +// NodeByElementIdSeek + +The `NodeByElementIdSeek` operator reads one or more nodes by element id from the node store, specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]. + + +.NodeByElementIdSeek +====== + .Query [source, cypher] ---- @@ -1768,6 +1897,58 @@ Total database accesses: 166, total allocated memory: 976 ====== +[query-plan-transaction-apply]] +== TransactionApply +// TransactionApply + +`TransactionApply` works like the `Apply` operator but will commit the current transaction after a specified number of rows. + +.TransactionApply +====== + +.Query +[source, cypher] +---- +PROFILE +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL { + WITH line + CREATE (a: Artist {name: line[0]}) + RETURN a +} IN TRANSACTIONS OF 100 ROWS +RETURN a; +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | a | 10 | 4 | 8 | 0 | | | | +| | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | +| +TransactionApply | 1 | IN TRANSACTIONS OF $autoint_1 ROWS ON ERROR FAIL | 10 | 4 | 0 | 2152 | 0/0 | 2.036 | Fused in Pipeline 3 | +| |\ +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Create | 2 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 16 | | | | | +| | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 3 | line | 10 | 4 | 0 | 3472 | 0/0 | 32.746 | Fused in Pipeline 2 | +| | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +LoadCSV | 4 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | ++-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 24, total allocated memory: 5472 +---- + +====== + [[query-plan-anti]] == Anti @@ -2873,6 +3054,61 @@ Total database accesses: 318, total allocated memory: 208 ====== +[[query-plan-nullify-metadata]] +== Nullify Metadata +// NullifyMetadata +_This feature was introduced in Neo4j 5.9._ + +`NullifyMetadata` is responsible for cleaning up the state produced by `Repeat(Trail)`. +It is only planned directly after `Repeat(Trail)`. + +.NullifyMetadata +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b) WHERE a.name <> b.name){1,2} (friend:Person) +RETURN me, friend +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++------------------+----+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | me, friend | 15 | 48 | 0 | | | | | +| | +----+-----------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 1 | friend:Person | 15 | 48 | 96 | | | | | +| | +----+-----------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NullifyMetadata | 7 | | 15 | 48 | 0 | | | | | +| | +----+-----------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 15 | 48 | 0 | 16784 | 0/0 | 1.146 | Fused in Pipeline 2 | +| |\ +----+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Filter | 3 | not a.name = b.name AND isRepeatTrailUnique(anon_2) | 5 | 48 | 216 | | | | | +| | | +----+-----------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 4 | (a)-[anon_2:FRIENDS_WITH]-(b) | 10 | 72 | 151 | | | | | +| | | +----+-----------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 5 | a | 14 | 38 | 0 | 3112 | 3/0 | 6.279 | Fused in Pipeline 1 | +| | +----+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | 6 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 3.970 | In Pipeline 0 | ++------------------+----+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 318, total allocated memory: 208 +---- + +====== + [[query-plan-assert-same-node]] == Assert Same Node // AssertSameNode @@ -3555,7 +3791,6 @@ In an analogous manner to the xref::planning-and-tuning/operators/operators-deta [source, cypher] ---- PROFILE -CYPHER runtime=slotted FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) ---- @@ -3583,6 +3818,110 @@ Total database accesses: 9, total allocated memory: 64 ====== +[query-plan-transaction-foreach]] +== TransactionForeach +// TransactionForeach + +`TransactionForeach` works like the `Foreach` operator but will commit the current transaction after a specified number of rows. + +.TransactionForeach +====== + +.Query +[source, cypher] +---- +PROFILE +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL { + WITH line + CREATE (a: Artist {name: line[0]}) +} IN TRANSACTIONS OF 100 ROWS +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + +++---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | + +---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | +ProduceResults | 0 | | 10 | 0 | 0 | 0 | | | | + | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | + | +EmptyResult | 1 | | 10 | 0 | 0 | | 0/0 | 0.000 | Fused in Pipeline 3 | + | | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | +TransactionForeach | 2 | IN TRANSACTIONS OF $autoint_1 ROWS ON ERROR FAIL | 10 | 4 | 0 | 4856 | | | | + | |\ +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | + | | +Create | 3 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 12 | | | | | + | | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | + | | +Argument | 4 | line | 10 | 4 | 0 | 3472 | 0/0 | 0.712 | Fused in Pipeline 2 | + | | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | +LoadCSV | 5 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | + +---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + + Total database accesses: 12, total allocated memory: 5704 +---- + +====== + +[query-plan-subquery-foreach]] +== SubqueryForeach +// SubqueryForeach + +`SubqueryForeach` works like the `Foreach` operator but it is only used for executing subqueries. + +.SubqueryForeach +====== + +.Query +[source, cypher] +---- +PROFILE +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL { + WITH line + CREATE (a: Artist {name: line[0]}) +} +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 10 | 0 | 0 | 0 | | | | +| | +----+-------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 10 | 0 | 0 | | 0/0 | 0.000 | Fused in Pipeline 3 | +| | +----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +SubqueryForeach | 2 | | 10 | 4 | 0 | 4080 | | | | +| |\ +----+-------------------------------------+----------------+------+---------+----------------+ | | | +| | +Create | 3 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 12 | | | | | +| | | +----+-------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 4 | line | 10 | 4 | 0 | 3472 | 0/0 | 0.852 | Fused in Pipeline 2 | +| | +----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +LoadCSV | 5 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | ++------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 12, total allocated memory: 4928 +---- + +====== + [[query-plan-eager]] == Eager @@ -5171,6 +5510,51 @@ Total database accesses: 106, total allocated memory: 184 ====== +[query-plan-set-properties]] +== Set Properties +// SetProperties + +The `SetProperties` operator is used when setting multiple properties on a node or relationship. + + +.SetProperties +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (n) +SET n.weekDay = 'Monday', n.meal = 'Lunch' +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------+----+---------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+---------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 35 | 0 | 0 | 0 | | | | +| | +----+---------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 35 | 0 | 0 | | | | | +| | +----+---------------------------------------------------+----------------+------+---------+----------------+ | | | +| +SetProperties | 2 | n.weekDay = $autostring_0, n.meal = $autostring_1 | 35 | 35 | 105 | | | | | +| | +----+---------------------------------------------------+----------------+------+---------+----------------+ | | | +| +AllNodesScan | 3 | n | 35 | 35 | 36 | 248 | 3/0 | 152.289 | Fused in Pipeline 0 | ++-----------------+----+---------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 141, total allocated memory: 312 +---- + +====== [[query-plan-create-constraint]] == Create Constraint @@ -5713,3 +6097,63 @@ Total database accesses: 0, total allocated memory: 64 ====== +[[query-plan-argument-tracker]] +== Argument Tracker +// ArgumentTracker + +The `ArgumentTracker` operator is used to ensure row-by-row semantics. +This restricts the xref:planning-and-tuning/runtimes/index.adoc[Cypher runtime] to not batch operations in larger chunks. + +.ArgumentTracker +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (s:Person {name: 'me'}) +CALL { + WITH s + SET s.seen = coalesce(s.seen + 1,1) + RETURN s.seen AS result +} +RETURN result; +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + ++--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | result | 0 | 0 | 0 | | | | | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | s.seen AS result | 0 | 0 | 0 | | | | | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 2 | | 0 | 0 | 0 | | | | | +| |\ +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +ArgumentTracker | 7 | | 0 | 0 | 0 | 696 | | | | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Eager | 3 | | 0 | 0 | 0 | 696 | 0/0 | 0.000 | Fused in Pipeline 2 | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +SetProperty | 4 | s.seen = coalesce(s.seen + $autoint_1, $autoint_2) | 0 | 0 | 0 | | | | | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 5 | s | 0 | 0 | 0 | 256 | 0/0 | 0.000 | Fused in Pipeline 1 | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeIndexSeek | 6 | RANGE INDEX s:Person(name) WHERE name = $autostring_0 | 0 | 0 | 1 | 376 | 1/0 | 0.286 | In Pipeline 0 | ++--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + + +Total database accesses: 47, total allocated memory: 4144 +---- + +====== + + + From 848f1ecdd869fe4ef9fa7df16dfcb84617556ff5 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 18 Jan 2024 13:54:35 +0000 Subject: [PATCH 358/383] Pin to SHA for untrusted only (#844) --- .github/workflows/docs-deploy-surge.yml | 6 +++--- .github/workflows/docs-pr-checks.yml | 24 ++++-------------------- .github/workflows/docs-teardown.yml | 8 +++++--- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/.github/workflows/docs-deploy-surge.yml b/.github/workflows/docs-deploy-surge.yml index ec2ad6fb8..7d3248b44 100644 --- a/.github/workflows/docs-deploy-surge.yml +++ b/.github/workflows/docs-deploy-surge.yml @@ -6,7 +6,7 @@ # This workflow expects the triggering workflow to generate an artifact called "docs" # - update the reference to "docs" and "docs.zip" in this workflow if your triggering workflow generates an artifact with a different name -name: "Deploy to surge" +name: "Deploy docs preview" on: workflow_run: @@ -80,7 +80,7 @@ jobs: # The changelog contains links to new and changed files in the deployed docs - name: Comment on PR (changelog) if: ${{ hashFiles('changelog') != '' }} - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0 with: number: ${{ steps.get-deploy-id.outputs.deploy-id }} recreate: true @@ -93,7 +93,7 @@ jobs: if: ${{ hashFiles('changelog') == '' }} env: DEPLOY_URL: ${{ steps.get-deploy-url.outputs.deploy-url }} - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0 with: number: ${{ steps.get-deploy-id.outputs.deploy-id }} header: docs-pr-changes diff --git a/.github/workflows/docs-pr-checks.yml b/.github/workflows/docs-pr-checks.yml index 1072ac3f5..78a847069 100644 --- a/.github/workflows/docs-pr-checks.yml +++ b/.github/workflows/docs-pr-checks.yml @@ -13,18 +13,17 @@ jobs: # Generate HTML docs-build-pr: - uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@dev + uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@v1.0.0 with: deploy-id: ${{ github.event.number }} retain-artifacts: 14 - pageList: true # Parse the json log output from the HTML build, and output warnings and errors as annotations # Optionally, fail the build if there are warnings or errors # By default, the job fails if there are errors, passes if there are warnings only. docs-verify-pr: needs: docs-build-pr - uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@dev + uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@v1.0.0 with: failOnWarnings: true @@ -41,7 +40,7 @@ jobs: steps: - name: Get file changes id: get-file-changes - uses: tj-actions/changed-files@v41 + uses: tj-actions/changed-files@cbda684547adc8c052d50711417fa61b428a9f88 # v41.1.2 with: separator: ',' files_yaml: | @@ -56,22 +55,7 @@ jobs: docs-updates-comment-pr: if: needs.docs-build-pr.outputs.pages-listed == 'success' needs: [docs-build-pr, docs-changes-pr] - uses: neo4j/docs-tools/.github/workflows/reusable-docs-pr-changes.yml@dev + uses: neo4j/docs-tools/.github/workflows/reusable-docs-pr-changes.yml@v1.0.0 with: pages-modified: ${{ needs.docs-changes-pr.outputs.pages-modified }} pages-added: ${{ needs.docs-changes-pr.outputs.pages-added }} - - # Use vale to verify the changes against the style guide - # You can specify your own vale config file if you want to override or replace the default Neo4j rules - # docs-lint-pr: - # needs: docs-changes-pr - # if: needs.docs-changes-pr.outputs.asciidoc-files != '' - # uses: neo4j/docs-tools/.github/workflows/reusable-docs-vale.yml@dev - # with: - # files: ${{ needs.docs-changes-pr.outputs.asciidoc-files }} - # vale-fail-on-error: true - # # use-default-rules: false - # # vale-config-file: .vale.ini - # separator: ',' - - diff --git a/.github/workflows/docs-teardown.yml b/.github/workflows/docs-teardown.yml index 1d93cba74..9ab7f8774 100644 --- a/.github/workflows/docs-teardown.yml +++ b/.github/workflows/docs-teardown.yml @@ -4,7 +4,10 @@ name: "Documentation Teardown" on: pull_request_target: branches: - - dev + - "dev" + - "5.x" + - "4.[0-9]" + - "3.5" types: - closed @@ -36,7 +39,7 @@ jobs: surge teardown $DEPLOY_URL --token "$SURGE_TOKEN" - name: Comment on PR - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd # v2.8.0 with: number: ${{ github.event.pull_request.number }} header: docs-pr-changes @@ -45,4 +48,3 @@ jobs: The preview documentation has now been torn down - reopening this PR will republish it. GITHUB_TOKEN: ${{ secrets.DOCS_PR_COMMENT_TOKEN }} - From c39db2aee426495576659e7c2f388c2ea9de3d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:12:21 +0100 Subject: [PATCH 359/383] More fixes to new index chapter (#842) Co-authored-by: Neil Dewhurst --- .../using-indexes.adoc | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc index 21f28a242..6c048d807 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc @@ -1,5 +1,6 @@ :description: Information about how to use the search-performance indexes in Neo4j. :test-skip: true +:test-setup-dump: https://github.com/neo4j-graph-examples/openstreetmap/raw/main/data/openstreetmap-50.dump = The impact of indexes on query performance Search-performance indexes enable quicker and more efficient pattern matching by solving a particular combination of node label/relationship type and property predicate. @@ -54,6 +55,7 @@ d|Rows:1 |=== .Execution plan +[role="queryplan"] ---- +-------------------+----+------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -111,6 +113,7 @@ RETURN count(n) ---- .Execution plan +[role="queryplan"] ---- +-------------------+----+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -180,6 +183,7 @@ RETURN n.name AS name, n.type AS type |=== .Execution plan +[role="queryplan"] ---- +------------------------+----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -209,6 +213,7 @@ RETURN n.name, n.type ---- .Execution plan +[role="queryplan"] ---- +-----------------------+----+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -286,6 +291,7 @@ RETURN p2.name AS name, p2.type AS type |=== .Execution plan +[role="queryplan"] ---- +-------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -361,7 +367,7 @@ CREATE INDEX composite_index FOR (n:PointOfInterest) ON (n.name, n.type) [source,cypher] ---- PROFILE -MATCH (tennisPitch: PointOfInterest {name: 'tennis', type: 'pitch'}) +MATCH (tennisPitch: PointOfInterest {name: 'pitch', type: 'tennis'}) WITH tennisPitch MATCH path = shortestPath((tennisPitch)-[:ROUTE*]-(:PointOfInterest {name: 'Zoo School'})) WITH path, relationships(path) AS relationships @@ -383,6 +389,7 @@ RETURN length(path) AS pathLength, sum(rel.distance) AS geographicalDistance |=== .Execution plan +[role="queryplan"] ---- +---------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | @@ -440,7 +447,7 @@ Note the order in which the properties are defined when creating the index, with ---- PROFILE MATCH (n:PointOfInterest) -WHERE n.lat = 40.769798 AND n.name STARTS WITH 'William' AND n.type IS NOT NULL +WHERE n.lat = 40.7697989 AND n.name STARTS WITH 'William' AND n.type IS NOT NULL RETURN n.name AS name ---- @@ -453,6 +460,7 @@ RETURN n.name AS name |=== .Execution plan +[role="queryplan"] ---- +-----------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -498,6 +506,7 @@ RETURN n.name AS name ---- .Execution plan +[role="queryplan"] ---- +-----------------+----+-----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -560,6 +569,7 @@ ORDER BY distance ---- .Execution plan +[role="queryplan"] ---- +-----------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | @@ -606,6 +616,7 @@ ORDER BY distance ---- .Execution plan +[role="queryplan"] ---- +-----------------------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+----------------+---------------------+ @@ -656,6 +667,7 @@ RETURN poi.name AS name ---- .Execution plan +[role="queryplan"] ---- +-------------------------+----+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -689,7 +701,7 @@ The following query demonstrates the incompatibility between `null` values and i [source,cypher] ---- PROFILE -ATCH (n:PointOfInterest) +MATCH (n:PointOfInterest) WHERE n.name IS NULL RETURN count(n) AS nodes ---- @@ -703,6 +715,7 @@ RETURN count(n) AS nodes |=== .Execution plan +[role="queryplan"] ---- +-------------------+----+-------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -744,6 +757,7 @@ RETURN count(n) AS nodes The query result now includes both the three nodes with an unset `name` value found in the previous query and the two nodes with a `name` value containing `William` (`William Shakespeare` and `William Tecumseh Sherman`). .Execution plan +[role="queryplan"] ---- +--------------------------+----+----------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -797,6 +811,7 @@ RETURN count(n) AS nodes |=== .Execution plan +[role="queryplan"] ---- +-------------------+----+------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -833,6 +848,7 @@ RETURN count(n) AS nodes ---- .Execution plan +[role="queryplan"] ---- +-------------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -882,6 +898,7 @@ RETURN count(n) AS nodes ---- .Execution plan +[role="queryplan"] ---- +------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | @@ -920,6 +937,7 @@ RETURN count(n) AS nodes ---- .Execution plan +[role="queryplan"] ---- +-----------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | From 6fb77ea33a224bd67df669b9202fc19df0f49d14 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 18 Jan 2024 14:54:36 +0000 Subject: [PATCH 360/383] Use v1.0.2 (#848) --- .github/workflows/docs-branch-checks.yml | 8 ++++---- .github/workflows/docs-pr-checks.yml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docs-branch-checks.yml b/.github/workflows/docs-branch-checks.yml index d30ba342e..34e9d512f 100644 --- a/.github/workflows/docs-branch-checks.yml +++ b/.github/workflows/docs-branch-checks.yml @@ -30,7 +30,7 @@ jobs: docs-build: if: ${{ inputs.html }} name: Generate HTML - uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@dev + uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@v1.0.2 with: retain-artifacts: 14 deploy-id: 0 @@ -38,16 +38,16 @@ jobs: docs-verify: name: Verify HTML needs: docs-build - uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@dev + uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@v1.0.2 docs-links: if: ${{ inputs.links }} name: Check links needs: docs-build - uses: neo4j/docs-tools/.github/workflows/reusable-docs-links.yml@dev + uses: neo4j/docs-tools/.github/workflows/reusable-docs-links.yml@v1.0.2 docs-lint: if: ${{ inputs.lint }} name: Lint docs - uses: neo4j/docs-tools/.github/workflows/reusable-docs-vale.yml@dev + uses: neo4j/docs-tools/.github/workflows/reusable-docs-lint.yml@v1.0.2 diff --git a/.github/workflows/docs-pr-checks.yml b/.github/workflows/docs-pr-checks.yml index 78a847069..20acdd6a9 100644 --- a/.github/workflows/docs-pr-checks.yml +++ b/.github/workflows/docs-pr-checks.yml @@ -13,7 +13,7 @@ jobs: # Generate HTML docs-build-pr: - uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@v1.0.0 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@v1.0.2 with: deploy-id: ${{ github.event.number }} retain-artifacts: 14 @@ -23,7 +23,7 @@ jobs: # By default, the job fails if there are errors, passes if there are warnings only. docs-verify-pr: needs: docs-build-pr - uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@v1.0.0 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@v1.0.2 with: failOnWarnings: true @@ -55,7 +55,7 @@ jobs: docs-updates-comment-pr: if: needs.docs-build-pr.outputs.pages-listed == 'success' needs: [docs-build-pr, docs-changes-pr] - uses: neo4j/docs-tools/.github/workflows/reusable-docs-pr-changes.yml@v1.0.0 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-pr-changes.yml@v1.0.2 with: pages-modified: ${{ needs.docs-changes-pr.outputs.pages-modified }} pages-added: ${{ needs.docs-changes-pr.outputs.pages-added }} From 4ff9167534d2cfb5c5fe29b225927c8e0c288d50 Mon Sep 17 00:00:00 2001 From: Neil Dewhurst Date: Thu, 18 Jan 2024 15:07:15 +0000 Subject: [PATCH 361/383] 1.0.3 (#849) --- .github/workflows/docs-branch-checks.yml | 8 ++++---- .github/workflows/docs-pr-checks.yml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docs-branch-checks.yml b/.github/workflows/docs-branch-checks.yml index 34e9d512f..a85cf73b8 100644 --- a/.github/workflows/docs-branch-checks.yml +++ b/.github/workflows/docs-branch-checks.yml @@ -30,7 +30,7 @@ jobs: docs-build: if: ${{ inputs.html }} name: Generate HTML - uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@v1.0.2 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@v1.0.3 with: retain-artifacts: 14 deploy-id: 0 @@ -38,16 +38,16 @@ jobs: docs-verify: name: Verify HTML needs: docs-build - uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@v1.0.2 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@v1.0.3 docs-links: if: ${{ inputs.links }} name: Check links needs: docs-build - uses: neo4j/docs-tools/.github/workflows/reusable-docs-links.yml@v1.0.2 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-links.yml@v1.0.3 docs-lint: if: ${{ inputs.lint }} name: Lint docs - uses: neo4j/docs-tools/.github/workflows/reusable-docs-lint.yml@v1.0.2 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-lint.yml@v1.0.3 diff --git a/.github/workflows/docs-pr-checks.yml b/.github/workflows/docs-pr-checks.yml index 20acdd6a9..ec4640239 100644 --- a/.github/workflows/docs-pr-checks.yml +++ b/.github/workflows/docs-pr-checks.yml @@ -13,7 +13,7 @@ jobs: # Generate HTML docs-build-pr: - uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@v1.0.2 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-build.yml@v1.0.3 with: deploy-id: ${{ github.event.number }} retain-artifacts: 14 @@ -23,7 +23,7 @@ jobs: # By default, the job fails if there are errors, passes if there are warnings only. docs-verify-pr: needs: docs-build-pr - uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@v1.0.2 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-verify.yml@v1.0.3 with: failOnWarnings: true @@ -55,7 +55,7 @@ jobs: docs-updates-comment-pr: if: needs.docs-build-pr.outputs.pages-listed == 'success' needs: [docs-build-pr, docs-changes-pr] - uses: neo4j/docs-tools/.github/workflows/reusable-docs-pr-changes.yml@v1.0.2 + uses: neo4j/docs-tools/.github/workflows/reusable-docs-pr-changes.yml@v1.0.3 with: pages-modified: ${{ needs.docs-changes-pr.outputs.pages-modified }} pages-added: ${{ needs.docs-changes-pr.outputs.pages-added }} From 2f89def02b6496ac1aadf12c08ba4322e0e170a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:00:16 +0100 Subject: [PATCH 362/383] Update operator pages (#851) --- .../planning-and-tuning/operators/index.adoc | 97 +++++-- .../operators/operators-detail.adoc | 248 +++++++----------- 2 files changed, 174 insertions(+), 171 deletions(-) diff --git a/modules/ROOT/pages/planning-and-tuning/operators/index.adoc b/modules/ROOT/pages/planning-and-tuning/operators/index.adoc index 484ad1c63..cf830c219 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/index.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/index.adoc @@ -18,7 +18,7 @@ This table comprises all the execution plan operators ordered lexicographically. * _Eager_ operators xref::planning-and-tuning/execution-plans.adoc#lazy-eager-evaluation[accumulate all their rows] before piping them to the next operator. -[cols="35a,35a,6,10,14", options="header"] +[cols="35a,35a,8,12,18", options="header"] |=== | Name | Description | Leaf? | Updating? | Considerations @@ -49,7 +49,14 @@ Tests for the absence of a pattern predicate. | | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[Argument] -| Indicates the variable to be used as an argument to the right-hand side of an `Apply` operator. +| Indicates the variable to be used as an argument to the right-hand side of an xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. +| label:yes[] +| +| + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument-tracker[ArgumentTracker] +| Used to ensure row-by-row semantics. +Restricts the xref:planning-and-tuning/runtimes/index.adoc[Cypher runtime] to not batch operations in larger chunks. | label:yes[] | | @@ -64,7 +71,7 @@ Tests for the absence of a pattern predicate. | Ensures that no relationship property uniqueness constraints are violated. | | -| +| label:new[Introduced in 5.8] | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-asserting-multi-node-index-seek[AssertingMultiNodeIndexSeek] | Used to ensure that no property uniqueness constraints are violated. @@ -120,8 +127,14 @@ Tests for the absence of a pattern predicate. | | +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-by-element-id-seek[DirectedRelationshipByElementIdSeek] +| Reads one or more relationships by element id (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]) from the relationship store and produces the relationship as well as the source and target node of the relationship. +| label:yes[] +| +| + | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-by-id-seek[DirectedRelationshipByIdSeek] -| Reads one or more relationships by id from the relationship store. +| Reads one or more relationships by id (specified via the function xref::functions/scalar.adoc#functions-id[Id()]) from the relationship store, and produces the relationship as well as the source and target node of the relationship. | label:yes[] | | @@ -224,7 +237,7 @@ Tests for the absence of a pattern predicate. | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-exhaustive-limit[ExhaustiveLimit] a| -The `ExhaustiveLimit` operator is similar to the `Limit` operator, but always exhausts the input. +The `ExhaustiveLimit` operator is similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-limit[`Limit`]operator, but always exhausts the input. Used when combining `LIMIT` and updates. | | @@ -260,7 +273,7 @@ Yields rows from the left-hand operator and discards rows from the right-hand op | Fetches all nodes that have all of the provided labels from the node label index. | label:yes[] | -| +| label:new[Introduced in 5.5] | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-let-anti-semi-apply[LetAntiSemiApply] a| @@ -307,7 +320,7 @@ Tests for the presence of a pattern predicate in queries containing multiple pat | | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-locking-merge[LockingMerge] -| Similar to the `Merge` operator but will lock the start and end node when creating a relationship if necessary. +| Similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-merge[`Merge`] operator but will lock the start and end node when creating a relationship if necessary. | | | @@ -324,14 +337,14 @@ Tests for the presence of a pattern predicate in queries containing multiple pat | | -| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-by-elementid-seek[NodeByElementIdSeek] -| Reads one or more nodes by ID from the node store, specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]. +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-by-element-id-seek[NodeByElementIdSeek] +| Reads one or more nodes by id from the node store, specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]. | label:yes[] | -| +| label:new[Introduced in 5.3] | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-by-id-seek[NodeByIdSeek] -| Reads one or more nodes by ID from the node store, specified via the function xref::functions/scalar.adoc#functions-id[id()]. +| Reads one or more nodes by id from the node store, specified via the function xref::functions/scalar.adoc#functions-id[id()]. | label:yes[] | | @@ -408,6 +421,13 @@ Tests for the presence of a pattern predicate in queries containing multiple pat | | +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-nullify-metadata[NullifyMetadata] +| responsible for cleaning up the state produced by xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-repeat[`Repeat(Trail)`]. +It is only planned directly after `Repeat(Trail)`. +| +| +| label:new[Introduced in 5.9] + | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-optional[Optional] | Yields a single row with all columns set to `null` if no data is returned by its source. | @@ -427,15 +447,14 @@ Tests for the presence of a pattern predicate in queries containing multiple pat | | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-ordered-aggregation[OrderedAggregation] -a| -Like `EagerAggregation` but relies on the ordering of incoming rows. -Is not eager. +| Similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-eager-aggregation[`EagerAggregation`] operator but relies on the ordering of incoming rows. +It is not eager. | | | | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-ordered-distinct[OrderedDistinct] -| Like `Distinct` but relies on the ordering of incoming rows. +| Similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-distinct[`DISTINCT`] operator but relies on the ordering of incoming rows. | | | @@ -486,7 +505,7 @@ Is not eager. | Solves quantified path patterns. | | -| +| label:new[Introduced in 5.9] | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-remove-labels[RemoveLabels] | Deletes labels from a node. @@ -495,16 +514,14 @@ Is not eager. | | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-roll-up-apply[RollUpApply] -a| -Performs a nested loop. +| Performs a nested loop. Executes a pattern expression or pattern comprehension. | | | | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-select-or-anti-semi-apply[SelectOrAntiSemiApply] -a| -Performs a nested loop. +| Performs a nested loop. Tests for the absence of a pattern predicate if an expression predicate evaluates to `false`. | | @@ -540,6 +557,12 @@ Tests for the absence of a pattern predicate if an expression predicate evaluate | label:yes[] | +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-set-properties[SetProperties] +| Used when setting multiple properties on a node or relationship. +| +| label:yes[] +| + | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-set-relationship-properties-from-map[SetRelationshipPropertiesFromMap] | Sets properties from a map on a relationship. | @@ -600,6 +623,12 @@ Tests for the absence of a pattern predicate if an expression predicate evaluate | | label:eager[] +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-subquery-foreach[SubqueryForeach] +| Works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-foreach[`Foreach`]operator but it is only used for executing subqueries. +| +| +| + | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-terminate-transactions[TerminateTransactions] | Terminate transactions with the given IDs. | label:yes[] @@ -612,20 +641,32 @@ Tests for the absence of a pattern predicate if an expression predicate evaluate | | label:eager[] +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-transaction-apply[TransactionApply] +| Works like the xref:planning-and-tuning/operators/operators-detail#query-plan-apply[`Apply`] operator but will commit the current transaction after a specified number of rows. +| +| +| + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-transaction-foreach[TransactionForeach] +| Works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-foreach[`Foreach`] operator but will commit the current transaction after a specified number of rows. +| +| +| + | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-build[TriadicBuild] -| The `TriadicBuild` operator is used in conjunction with `TriadicFilter` to solve triangular queries. +| Used in conjunction with xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-filter[`TriadicFilter`] to solve triangular queries. | | | | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-filter[TriadicFilter] -| The `TriadicFilter` operator is used in conjunction with `TriadicBuild` to solve triangular queries. +| Used in conjunction with xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-build[`TriadicBuild`] to solve triangular queries. | | | | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-selection[TriadicSelection] -| Solves triangular queries, such as the very common 'find my friend-of-friends that are not already my friend'. +| Solves triangular queries, such as the very common 'find my friends-of-friends that are not already my friends'. | | | @@ -636,8 +677,16 @@ Tests for the absence of a pattern predicate if an expression predicate evaluate | | +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-by-element-id-seek[UndirectedRelationshipByElementIdSeek] +| Reads one or more relationships by element id (specified via the function xref::functions/scalar.adoc#functions-elementid[ElementId()]) from the relationship store. +As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. +| label:yes[] +| +| + | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-by-id-seek[UndirectedRelationshipByIdSeek] -| Reads one or more relationships by ID from the relationship store. +| Reads one or more relationships by id (specified via the function xref::functions/scalar.adoc#functions-id[Id()]) from the relationship store. +As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. | label:yes[] | | diff --git a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc index df89894cf..92ed087e3 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc @@ -445,7 +445,7 @@ Total database accesses: 1, total allocated memory: 184 == Undirected Relationship By Id Seek // UndirectedRelationshipByIdSeek -The `UndirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]). +The `UndirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-id[Id()]). As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. @@ -818,7 +818,6 @@ Total database accesses: 13, total allocated memory: 184 [[query-plan-intersection-node-by-labels-scan]] == Intersection Node By Labels Scan // IntersectionNodeByLabelsScan -// New in 5.4 _This feature was introduced in Neo4j 5.5._ @@ -860,8 +859,11 @@ Total database accesses: 13, total allocated memory: 184 [[query-plan-directed-all-relationships-scan]] -== Directed All Relationships Scan == +== Directed All Relationships Scan +//DirectedAllRelationshipsScan + The `DirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. + ====== .Query @@ -896,7 +898,8 @@ Total database accesses: 28, total allocated memory: 184 ====== [[query-plan-undirected-all-relationships-scan]] -== Undirected All Relationships Scan == +== Undirected All Relationships Scan +//UndirectedAllRelationshipsScan The `UndirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. ====== @@ -1017,7 +1020,7 @@ Total database accesses: 13, total allocated memory: 184 [[query-plan-directed-union-relationship-types-scan]] == Directed Union Relationship Types Scan -// DirectedUnionRelationshipTypeScan +// DirectedUnionRelationshipTypesScan The `DirectedUnionRelationshipTypeScan` operator fetches all relationships and their start and end nodes with at least one of the provided types from the relationship type index. @@ -1060,7 +1063,7 @@ Total database accesses: 14, total allocated memory: 184 [[query-plan-undirected-union-relationship-types-scan]] == Undirected Union Relationship Types Scan -// UndirectedUnionRelationshipTypeScan +// UndirectedUnionRelationshipTypesScan The `UndirectedUnionRelationshipTypeScan` operator fetches all relationships and their start and end nodes with at least one of the provided types from the relationship type index. @@ -1189,51 +1192,6 @@ Total database accesses: 3, total allocated memory: 312 ====== -[[query-plan-node-by-element-id-seek]] -== Node By Element Id Seek -// NodeByElementIdSeek - -The `NodeByElementIdSeek` operator reads one or more nodes by element id from the node store, specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]. - - -.NodeByElementIdSeek -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH (n) -WHERE elementId(n) = 0 -RETURN n ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+----------------------+----+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------+----+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | n | 1 | 0 | 0 | | 0/0 | 0.042 | | -| | +----+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +NodeByElementIdSeek | 1 | n WHERE elementId(n) = $autoint_0 | 1 | 0 | 1 | 248 | 0/0 | 0.107 | In Pipeline 0 | -+----------------------+----+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ - - - -Total database accesses: 1, total allocated memory: 184 ----- - -====== - [[query-plan-node-by-label-scan]] == Node By Label Scan @@ -1739,7 +1697,7 @@ Total database accesses: 11, total allocated memory: 184 == Apply // Apply -All the different `Apply` operators (listed below) share the same basic functionality: they perform a nested loop by taking a single row from the left-hand side, and using the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[Argument] operator on the right-hand side, execute the operator tree on the right-hand side. +All the different `Apply` operators (listed below) share the same basic functionality: they perform a nested loop by taking a single row from the left-hand side, and using the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[`Argument`] operator on the right-hand side, execute the operator tree on the right-hand side. The versions of the `Apply` operators differ in how the results are managed. The `Apply` operator (i.e. the standard version) takes the row produced by the right-hand side -- which at this point contains data from both the left-hand and right-hand sides -- and yields it. @@ -1789,7 +1747,7 @@ Total database accesses: 2, total allocated memory: 2216 == Semi Apply // SemiApply -The `SemiApply` operator tests for the presence of a pattern predicate, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +The `SemiApply` operator tests for the presence of a pattern predicate, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. If the right-hand side operator yields at least one row, the row from the left-hand side operator is yielded by the `SemiApply` operator. This makes `SemiApply` a filtering operator, used mostly for pattern predicates in queries. @@ -1844,7 +1802,7 @@ Total database accesses: 142, total allocated memory: 64 == Anti Semi Apply // AntiSemiApply -The `AntiSemiApply` operator tests for the absence of a pattern, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +The `AntiSemiApply` operator tests for the absence of a pattern, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. If the right-hand side operator yields no rows, the row from the left-hand side operator is yielded by the `AntiSemiApply` operator. This makes `AntiSemiApply` a filtering operator, used for pattern predicates in queries. @@ -1897,11 +1855,11 @@ Total database accesses: 166, total allocated memory: 976 ====== -[query-plan-transaction-apply]] +[[query-plan-transaction-apply]] == TransactionApply // TransactionApply -`TransactionApply` works like the `Apply` operator but will commit the current transaction after a specified number of rows. +`TransactionApply` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator but will commit the current transaction after a specified number of rows. .TransactionApply ====== @@ -2019,7 +1977,7 @@ Total database accesses: 178, total allocated memory: 6744 == Let Semi Apply // LetSemiApply -The `LetSemiApply` operator tests for the presence of a pattern predicate, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +The `LetSemiApply` operator tests for the presence of a pattern predicate, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. When a query contains multiple pattern predicates separated with `OR`, `LetSemiApply` will be used to evaluate the first of these. It will record the result of evaluating the predicate but will leave any filtering to another operator. In the example, `LetSemiApply` will be used to check for the presence of the `FRIENDS_WITH` relationship from each person. @@ -2083,7 +2041,7 @@ Total database accesses: 165, total allocated memory: 64 == Let Anti Semi Apply // LetAntiSemiApply -The `LetAntiSemiApply` operator tests for the absence of a pattern, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +The `LetAntiSemiApply` operator tests for the absence of a pattern, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. When a query contains multiple negated pattern predicates -- i.e. predicates separated with `OR`, where at least one predicate contains `NOT` -- `LetAntiSemiApply` will be used to evaluate the first of these. It will record the result of evaluating the predicate but will leave any filtering to another operator. In the example, `LetAntiSemiApply` will be used to check for the absence of the `FRIENDS_WITH` relationship from each person. @@ -2147,7 +2105,7 @@ Total database accesses: 142, total allocated memory: 64 // SelectOrSemiApply The `SelectOrSemiApply` operator tests for the presence of a pattern predicate and evaluates a predicate, -and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. This operator allows for the mixing of normal predicates and pattern predicates that check for the presence of a pattern. First, the normal expression predicate is evaluated, and, only if it returns `false`, is the costly pattern predicate evaluated. @@ -2205,7 +2163,7 @@ Total database accesses: 148, total allocated memory: 2952 == Select Or Anti Semi Apply // SelectOrAntiSemiApply -The `SelectOrAntiSemiApply` operator is used to evaluate `OR` between a predicate and a negative pattern predicate (i.e. a pattern predicate preceded with `NOT`), and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +The `SelectOrAntiSemiApply` operator is used to evaluate `OR` between a predicate and a negative pattern predicate (i.e. a pattern predicate preceded with `NOT`), and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. If the predicate returns `true`, the pattern predicate is not tested. If the predicate returns `false` or `null`, `SelectOrAntiSemiApply` will instead test the pattern predicate. @@ -2266,7 +2224,7 @@ Total database accesses: 136, total allocated memory: 4208 // LetSelectOrSemiApply The `LetSelectOrSemiApply` operator is planned for pattern predicates that are combined with other predicates using `OR`. -This is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +This is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. .LetSelectOrSemiApply @@ -2328,7 +2286,7 @@ Total database accesses: 179, total allocated memory: 64 // LetSelectOrAntiSemiApply The `LetSelectOrAntiSemiApply` operator is planned for negated pattern predicates -- i.e. pattern predicates preceded with `NOT` -- that are combined with other predicates using `OR`. -This operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +This operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. .LetSelectOrAntiSemiApply @@ -2443,7 +2401,7 @@ Total database accesses: 4, total allocated memory: 184 == Locking Merge // LockingMerge -The `LockingMerge` operator is just like a normal `Merge` but will lock the start and end node when creating a relationship if necessary. +The `LockingMerge` operator is similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-merge[`Merge`] operator but will lock the start and end node when creating a relationship if necessary. .LockingMerge @@ -2497,7 +2455,7 @@ Total database accesses: 15, total allocated memory: 2232 // RollUpApply The `RollUpApply` operator is used to execute an expression which takes as input a pattern, and returns a list with content from the matched pattern; for example, when using a pattern expression or pattern comprehension in a query. -This operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +This operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. .RollUpApply @@ -2549,7 +2507,7 @@ Total database accesses: 153, total allocated memory: 64 == Argument // Argument -The `Argument` operator indicates the variable to be used as an argument to the right-hand side of an xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator. +The `Argument` operator indicates the variable to be used as an argument to the right-hand side of an xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. .Argument @@ -2597,6 +2555,63 @@ Total database accesses: 15, total allocated memory: 2232 ====== +[[query-plan-argument-tracker]] +== Argument Tracker +// ArgumentTracker + +The `ArgumentTracker` operator is used to ensure row-by-row semantics. +This restricts the xref:planning-and-tuning/runtimes/index.adoc[Cypher runtime] to not batch operations in larger chunks. + +.ArgumentTracker +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (s:Person {name: 'me'}) +CALL { + WITH s + SET s.seen = coalesce(s.seen + 1,1) + RETURN s.seen AS result +} +RETURN result; +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + ++--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | result | 0 | 0 | 0 | | | | | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | s.seen AS result | 0 | 0 | 0 | | | | | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 2 | | 0 | 0 | 0 | | | | | +| |\ +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +ArgumentTracker | 7 | | 0 | 0 | 0 | 696 | | | | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Eager | 3 | | 0 | 0 | 0 | 696 | 0/0 | 0.000 | Fused in Pipeline 2 | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +SetProperty | 4 | s.seen = coalesce(s.seen + $autoint_1, $autoint_2) | 0 | 0 | 0 | | | | | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 5 | s | 0 | 0 | 0 | 256 | 0/0 | 0.000 | Fused in Pipeline 1 | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeIndexSeek | 6 | RANGE INDEX s:Person(name) WHERE name = $autostring_0 | 0 | 0 | 1 | 376 | 1/0 | 0.286 | In Pipeline 0 | ++--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + + +Total database accesses: 47, total allocated memory: 4144 +---- + +====== // --- expand operators --- @@ -3059,7 +3074,7 @@ Total database accesses: 318, total allocated memory: 208 // NullifyMetadata _This feature was introduced in Neo4j 5.9._ -`NullifyMetadata` is responsible for cleaning up the state produced by `Repeat(Trail)`. +`NullifyMetadata` is responsible for cleaning up the state produced by xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-repeat[`Repeat(Trail)`]. It is only planned directly after `Repeat(Trail)`. .NullifyMetadata @@ -3169,7 +3184,7 @@ _This feature was introduced in Neo4j 5.8._ The `AssertSameRelationship` operator is used to ensure that no relationship property uniqueness constraints are violated in the slotted and interpreted runtime. The example looks for the presence of a `WORKS_IN` relationship with the supplied `id` and `badgeNumber`. If it can't be found, then it will be created. -Owing to the existence of two property uniqueness constraints on `:WORKS_IN(id)` and `:WORKS_IN(badgeNumber)`, any relationship that would be found by the `DirectedRelationshipUniqueIndexSeek` operator must be the very same relationship or the constraints would be violated. +Owing to the existence of two property uniqueness constraints on `:WORKS_IN(id)` and `:WORKS_IN(badgeNumber)`, any relationship that would be found by the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-unique-index-seek[`DirectedRelationshipUniqueIndexSeek`] operator must be the very same relationship or the constraints would be violated. .AssertSameRelationship @@ -3541,8 +3556,8 @@ Total database accesses: 211, total allocated memory: 4312 == Triadic Selection // TriadicSelection -The `TriadicSelection` operator is used to solve triangular queries, such as the very common 'find my friend-of-friends that are not already my friend'. -It does so by putting all the friends into a set, and uses the set to check if the friend-of-friends are already connected to me. +The `TriadicSelection` operator is used to solve triangular queries, such as the very common 'find my friends-of-friends that are not already my friend'. +It does so by putting all the friends into a set, and uses the set to check if the friends-of-friends are already connected to me. The example finds the names of all friends of my friends that are not already my friends. @@ -3599,8 +3614,8 @@ Total database accesses: 246, total allocated memory: 64 == Triadic Build // TriadicBuild -The `TriadicBuild` operator is used in conjunction with `TriadicFilter` to solve triangular queries, such as the very common 'find my friend-of-friends that are not already my friend'. -These two operators are specific to Pipelined runtime and together perform the same logic as `TriadicSelection` does for other runtimes. +The `TriadicBuild` operator is used in conjunction with xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-filter[`TriadicFilter`] to solve triangular queries, such as the very common 'find my friend-of-friends that are not already my friend'. +These two operators are specific to Pipelined runtime and together perform the same logic as xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-selection[`TriadicSelection`] does for other runtimes. `TriadicBuild` builds a set of all friends, which is later used by `TriadicFilter`. The example finds the names of all friends of my friends that are not already my friends. @@ -3664,8 +3679,8 @@ Total database accesses: 256, total allocated memory: 7376 == Triadic Filter // TriadicFilter -The `TriadicFilter` operator is used in conjunction with `TriadicBuild` to solve triangular queries, such as the very common 'find my friend-of-friends that are not already my friend'. -These two operators are specific to Pipelined runtime and together perform the same logic as `TriadicSelection` does for other runtimes. +The `TriadicFilter` operator is used in conjunction with xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-build[`TriadicBuild`] to solve triangular queries, such as the very common 'find my friend-of-friends that are not already my friend'. +These two operators are specific to Pipelined runtime and together perform the same logic as xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-selection[`TriadicSelection`] does for other runtimes. `TriadicFilter` uses a set of friends previously built by `TriadicBuild` to check if the friend-of-friends are already connected to me. The example finds the names of all friends of my friends that are not already my friends. @@ -3818,11 +3833,11 @@ Total database accesses: 9, total allocated memory: 64 ====== -[query-plan-transaction-foreach]] +[[query-plan-transaction-foreach]] == TransactionForeach // TransactionForeach -`TransactionForeach` works like the `Foreach` operator but will commit the current transaction after a specified number of rows. +`TransactionForeach` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-foreach[`Foreach`] operator but will commit the current transaction after a specified number of rows. .TransactionForeach ====== @@ -3870,11 +3885,11 @@ Batch size 128 ====== -[query-plan-subquery-foreach]] +[[query-plan-subquery-foreach]] == SubqueryForeach // SubqueryForeach -`SubqueryForeach` works like the `Foreach` operator but it is only used for executing subqueries. +`SubqueryForeach` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-foreach[`Foreach`]operator but it is only used for executing subqueries. .SubqueryForeach ====== @@ -4052,7 +4067,7 @@ Total database accesses: 117, total allocated memory: 2664 == Ordered Aggregation // OrderedAggregation -The `OrderedAggregation` operator is an optimization of the `EagerAggregation` operator that takes advantage of the ordering of the incoming rows. +The `OrderedAggregation` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-eager-aggregation[`EagerAggregation`] operator that takes advantage of the ordering of the incoming rows. This operator uses lazy evaluation and has a lower memory pressure in the system than the `EagerAggregation` operator. @@ -4100,7 +4115,7 @@ Total database accesses: 3, total allocated memory: 352 // NodeCountFromCountStore The `NodeCountFromCountStore` operator uses the count store to answer questions about node counts. -This is much faster than the `EagerAggregation` operator which achieves the same result by actually counting. +This is much faster than the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-eager-aggregation[`EagerAggregation`] operator which achieves the same result by actually counting. However, as the count store only stores a limited range of combinations, `EagerAggregation` will still be used for more complex queries. For example, we can get counts for all nodes, and nodes with a label, but not nodes with more than one label. @@ -4142,11 +4157,11 @@ Total database accesses: 1, total allocated memory: 184 [[query-plan-relationship-count-from-count-store]] -== Relationship Count From Count Store == +== Relationship Count From Count Store // RelationshipCountFromCountStore The `RelationshipCountFromCountStore` operator uses the count store to answer questions about relationship counts. -This is much faster than the `EagerAggregation` operator which achieves the same result by actually counting. +This is much faster than the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-eager-aggregation[`EagerAggregation`] operator which achieves the same result by actually counting. However, as the count store only stores a limited range of combinations, `EagerAggregation` will still be used for more complex queries. For example, we can get counts for all relationships, relationships with a type, relationships with a label on one end, but not relationships with labels on both end nodes. @@ -4240,7 +4255,7 @@ Total database accesses: 95, total allocated memory: 432 == Ordered Distinct // OrderedDistinct -The `OrderedDistinct` operator is an optimization of the `Distinct` operator that takes advantage of the ordering of the incoming rows. +The `OrderedDistinct` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-distinct[`Distinct`] operator that takes advantage of the ordering of the incoming rows. This operator has a lower memory pressure in the system than the `Distinct` operator. @@ -4479,7 +4494,7 @@ Total database accesses: 85, total allocated memory: 1272 == Partial Sort // PartialSort -The `PartialSort` operator is an optimization of the `Sort` operator that takes advantage of the ordering of the incoming rows. +The `PartialSort` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-sort[`Sort`] operator that takes advantage of the ordering of the incoming rows. This operator uses lazy evaluation and has a lower memory pressure in the system than the `Sort` operator. Partial sort is only applicable when sorting on multiple columns. @@ -4580,7 +4595,7 @@ Total database accesses: 85, total allocated memory: 1264 == Partial Top // PartialTop -The `PartialTop` operator is an optimization of the `Top` operator that takes advantage of the ordering of the incoming rows. +The `PartialTop` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-top[`Top`] operator that takes advantage of the ordering of the incoming rows. This operator uses lazy evaluation and has a lower memory pressure in the system than the `Top` operator. Partial top is only applicable when sorting on multiple columns. @@ -4733,7 +4748,7 @@ Total database accesses: 0, total allocated memory: 184 // LockNodes - changed in 4.3 // ExhaustiveLimit -The `ExhaustiveLimit` operator is just like a normal `Limit` but will always exhaust the input. +The `ExhaustiveLimit` operator is similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-limit[`Limit`] operator but will always exhaust the input. Used when combining `LIMIT` and updates @@ -5510,7 +5525,7 @@ Total database accesses: 106, total allocated memory: 184 ====== -[query-plan-set-properties]] +[[query-plan-set-properties]] == Set Properties // SetProperties @@ -6096,64 +6111,3 @@ Total database accesses: 0, total allocated memory: 64 ---- ====== - -[[query-plan-argument-tracker]] -== Argument Tracker -// ArgumentTracker - -The `ArgumentTracker` operator is used to ensure row-by-row semantics. -This restricts the xref:planning-and-tuning/runtimes/index.adoc[Cypher runtime] to not batch operations in larger chunks. - -.ArgumentTracker -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH (s:Person {name: 'me'}) -CALL { - WITH s - SET s.seen = coalesce(s.seen + 1,1) - RETURN s.seen AS result -} -RETURN result; ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -+--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | result | 0 | 0 | 0 | | | | | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | 1 | s.seen AS result | 0 | 0 | 0 | | | | | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | 2 | | 0 | 0 | 0 | | | | | -| |\ +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +ArgumentTracker | 7 | | 0 | 0 | 0 | 696 | | | | -| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Eager | 3 | | 0 | 0 | 0 | 696 | 0/0 | 0.000 | Fused in Pipeline 2 | -| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +SetProperty | 4 | s.seen = coalesce(s.seen + $autoint_1, $autoint_2) | 0 | 0 | 0 | | | | | -| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 5 | s | 0 | 0 | 0 | 256 | 0/0 | 0.000 | Fused in Pipeline 1 | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeIndexSeek | 6 | RANGE INDEX s:Person(name) WHERE name = $autostring_0 | 0 | 0 | 1 | 376 | 1/0 | 0.286 | In Pipeline 0 | -+--------------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - - -Total database accesses: 47, total allocated memory: 4144 ----- - -====== - - - From c2aceb0fb0b17ddeac7cf99a7d7640c5310abbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:55:08 +0100 Subject: [PATCH 363/383] Update antora for 5.17 (#856) --- antora.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/antora.yml b/antora.yml index 29bdd8f77..a4efd5ed1 100644 --- a/antora.yml +++ b/antora.yml @@ -7,5 +7,5 @@ nav: asciidoc: attributes: neo4j-version: '5' - neo4j-version-minor: '5.16' - neo4j-version-exact: '5.16.0' + neo4j-version-minor: '5.17' + neo4j-version-exact: '5.17.0' From b9bbe8215f9ee0ab653fcfb4d3f68f904de7315d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:59:31 +0100 Subject: [PATCH 364/383] Update year in yml files to 2024 (#857) --- preview.yml | 2 +- publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/preview.yml b/preview.yml index d15063a3d..fa119684c 100644 --- a/preview.yml +++ b/preview.yml @@ -53,7 +53,7 @@ asciidoc: includePDF: false nonhtmloutput: "" experimental: '' - copyright: "2023 Neo4j, Inc." + copyright: "2024 Neo4j, Inc." common-license-page-uri: https://neo4j.com/docs/license/ docs-base-uri: https://neo4j.com/docs check-mark: icon:check[] diff --git a/publish.yml b/publish.yml index 7b7ac6141..ed1922c10 100644 --- a/publish.yml +++ b/publish.yml @@ -53,7 +53,7 @@ asciidoc: includePDF: false nonhtmloutput: "" experimental: '' - copyright: "2023 Neo4j, Inc." + copyright: "2024 Neo4j, Inc." common-license-page-uri: https://neo4j.com/docs/license/ docs-base-uri: https://neo4j.com/docs check-mark: icon:check[] From aadc7338ecc0f1b323792a1efd7cd68a2766ac5d Mon Sep 17 00:00:00 2001 From: Gem Lamont <106068376+gem-neo4j@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:57:20 +0100 Subject: [PATCH 365/383] Add docs for new normalize function. (#784) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The normalize function normalizes a string based on the normalization form. It is useful for converting unicode representations of characters that look the same to be the same unicode sequence, allowing for easier comparisons. --------- Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- ...ions-additions-removals-compatibility.adoc | 26 ++- modules/ROOT/pages/functions/index.adoc | 8 +- modules/ROOT/pages/functions/string.adoc | 171 ++++++++++++++++++ 3 files changed, 203 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 47807d95f..c81319a93 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -16,6 +16,30 @@ New features are added to the language continuously, and occasionally, some feat This section lists all of the features that have been removed, deprecated, added, or extended in different Cypher versions. Replacement syntax for deprecated and removed features are also indicated. +[[cypher-deprecations-additions-removals-5.17]] +== Neo4j 5.17 + +=== New features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:new[] + +[source, cypher, role=noheader] +---- +RETURN normalize("string", NFC) +---- + +| Introduction of a xref::functions/string.adoc#functions-normalize[normalize()] function. +This function normalizes a `STRING` according to the specified normalization form, which can be of type `NFC`, `NFD`, `NFKC`, or `NFKD`. + +|=== + [[cypher-deprecations-additions-removals-5.16]] == Neo4j 5.16 @@ -146,7 +170,7 @@ label:updated[] MATCH (n:Label) WHERE $param IS :: STRING NOT NULL AND n.prop = $param ---- -| `IS :: STRING NOT NULL` is now an xref:indexes/search-performance-indexes/using-indexes.adoc#text-indexes-type-predicate-expressions[index-compatible predicate]. +| `IS :: STRING NOT NULL` is now an xref:indexes/search-performance-indexes/using-indexes.adoc#text-indexes-type-predicate-expressions[index-compatible predicate]. |=== diff --git a/modules/ROOT/pages/functions/index.adoc b/modules/ROOT/pages/functions/index.adoc index 14bcb7c5c..97ec28758 100644 --- a/modules/ROOT/pages/functions/index.adoc +++ b/modules/ROOT/pages/functions/index.adoc @@ -505,6 +505,12 @@ These functions are used to manipulate strings or to create a string representat | `ltrim(input :: STRING) :: STRING` | Returns the given `STRING` with leading whitespace removed. +1.2+| xref::functions/string.adoc#functions-normalize[`normalize()`] +| `normalize(input :: STRING) :: STRING` +| Returns the given `STRING` normalized according to the normalization form `NFC`. label:new[Introduced in 5.17] +| `normalize(input :: STRING, normalForm = NFC :: [NFC, NFD, NFKC, NFKD]) :: STRING` +| Returns the given `STRING` normalized according to the specified normalization form. label:new[Introduced in 5.17] + 1.1+| xref::functions/string.adoc#functions-replace[`replace()`] | `replace(original :: STRING, search :: STRING, replace :: STRING) :: STRING` | Returns a `STRING` in which all occurrences of a specified search `STRING` in the given `STRING` have been replaced by another (specified) replacement `STRING`. @@ -773,7 +779,7 @@ Graph functions provide information about the constituent graphs in composite da |=== | Function | Signature | Description 1.1+| xref:functions/graph.adoc#functions-graph-by-elementid[`graph.byElementId()`] | `USE graph.byElementId(elementId :: STRING)` | Resolves the constituent graph to which a given element id belongs. -label:new[Introduced in Neo4j 5.13] +label:new[Introduced in 5.13] 1.1+| xref:functions/graph.adoc#functions-graph-byname[`graph.byName()`] | `USE graph.byName(name :: STRING)` | Resolves a constituent graph by name. 1.1+| xref:functions/graph.adoc#functions-graph-names[`graph.names()`] | `graph.names() :: LIST` | Returns a list containing the names of all graphs in the current composite database. 1.1+| xref:functions/graph.adoc#functions-graph-names[`graph.propertiesByName()`] | `graph.propertiesByName(name :: STRING) :: MAP` | Returns a map containing the properties associated with the given graph. diff --git a/modules/ROOT/pages/functions/string.adoc b/modules/ROOT/pages/functions/string.adoc index ee3009dbf..94fedcc91 100644 --- a/modules/ROOT/pages/functions/string.adoc +++ b/modules/ROOT/pages/functions/string.adoc @@ -148,6 +148,177 @@ RETURN ltrim(' hello') ====== + + +[[functions-normalize]] +== normalize() + +_This feature was introduced in Neo4j 5.17._ + +`normalize()` returns the given `STRING` normalized using the `NFC` Unicode normalization form. + +[NOTE] +==== +Unicode normalization is a process that transforms different representations of the same string into a standardized form. +For more information, see the documentation for link:https://unicode.org/reports/tr15/#Norm_Forms[Unicode normalization forms]. +==== + +The `normalize()` function is useful for converting `STRING` values into comparable forms. +When comparing two `STRING` values, it is their Unicode codepoints that are compared. +In Unicode, a codepoint for a character that looks the same may be represented by two, or more, different codepoints. +For example, the character `<` can be represented as `\uFE64` (﹤) or `\u003C` (<). +To the human eye, the characters may appear identical. +However, if compared, Cypher will return false as `\uFE64` does not equal `\u003C`. +Using the `normalize()` function, it is possible to +normalize the codepoint `\uFE64` to `\u003C`, creating a single codepoint representation, allowing them to be successfully compared. + +*Syntax:* + +[source, syntax, role="noheader"] +---- +normalize(input) +---- + +*Returns:* + +|=== + +| `STRING` + +|=== + +*Arguments:* + +[options="header"] +|=== +| Name | Description + +| `input` +| An expression that returns a `STRING`. + +|=== + +*Considerations:* + +|=== + +| `normalize(null)` returns `null`. + +|=== + + +.+normalize()+ +====== + +.Query +[source, cypher, indent=0] +---- +RETURN normalize('\u212B') = '\u00C5' AS result +---- + +.Result +[role="queryresult",options="header,footer",cols="1* Date: Mon, 22 Jan 2024 14:28:17 +0100 Subject: [PATCH 366/383] Dev normalize predicate (#804) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new Normalization Predicate Expression. RETURN "string" IS NORMALIZED; --------- Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- modules/ROOT/pages/clauses/where.adoc | 29 ++++++ ...ions-additions-removals-compatibility.adoc | 17 ++++ modules/ROOT/pages/functions/string.adoc | 3 + modules/ROOT/pages/syntax/operators.adoc | 99 ++++++++++++++++++- 4 files changed, 147 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index b0505906d..088b66bc3 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -338,6 +338,35 @@ The `name` and `age` for `Peter` are are returned because his name contains "ete |=== +[[match-string-is-normalized]] +=== Checking if a `STRING` `IS NORMALIZED` + +The `IS NORMALIZED` operator (introduced in Neo4j 5.17) is used to check whether the given `STRING` is in the `NFC` Unicode normalization form: + +.Query +[source, cypher] +---- +MATCH (n:Person) +WHERE n.name IS NORMALIZED +RETURN n.name AS normalizedNames +---- + +The given `STRING` values contain only normalized Unicode characters, therefore all the matched `name` properties are returned. +For more information, see the section about the xref:syntax/operators.adoc#match-string-is-normalized[normalization operator]. + +.Result +[role="queryresult",options="header,footer",cols="1*+`, `+<+`, `+>+`, `+<=+`, `+>=+`, `IS NULL`, `IS NOT NULL` | xref::syntax/operators.adoc#query-operators-comparison[String-specific comparison operators] | `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `=~` (regex matching) | xref::syntax/operators.adoc#query-operators-boolean[Boolean operators] | `AND`, `OR`, `XOR`, `NOT` -| xref::syntax/operators.adoc#query-operators-string[String operators] | `+` (string concatenation) +| xref::syntax/operators.adoc#query-operators-string[String operators] | `+` (string concatenation), `IS NORMALIZED` | xref::syntax/operators.adoc#query-operators-temporal[Temporal operators] | `+` and `-` for operations between durations and temporal instants/durations, `*` and `/` for operations between durations and numbers | xref::syntax/operators.adoc#query-operators-map[Map operators] | `.` for static value access by key, `[]` for dynamic value access by key | xref::syntax/operators.adoc#query-operators-list[List operators] | `+` (list concatenation), `IN` to check existence of an element in a list, `[]` for accessing element(s) dynamically @@ -543,6 +543,7 @@ RETURN number The string operators comprise: * concatenating strings: `+` +* checking if a string is normalized: `IS NORMALIZED` [[syntax-concatenating-two-strings]] @@ -563,6 +564,102 @@ RETURN 'neo' + '4j' AS result |=== +[[match-string-is-normalized]] +=== Checking if a `STRING` `IS NORMALIZED` + +_This feature was introduced in Neo4j 5.17._ + +The `IS NORMALIZED` operator is used to check whether the given `STRING` is in the `NFC` Unicode normalization form: + +[NOTE] +==== +Unicode normalization is a process that transforms different representations of the same string into a standardized form. +For more information, see the documentation for link:https://unicode.org/reports/tr15/#Norm_Forms[Unicode normalization forms]. +==== + +.Query +[source, cypher] +---- +RETURN "the \u212B char" IS NORMALIZED AS normalized +---- + +.Result +[role="queryresult",options="header,footer",cols="1* Date: Tue, 23 Jan 2024 12:54:08 +0100 Subject: [PATCH 367/383] Add descriptions of partitioned operators. (#847) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- ...ions-additions-removals-compatibility.adoc | 28 + .../planning-and-tuning/operators/index.adoc | 124 ++ .../operators/operators-detail.adoc | 1009 ++++++++++++++++- 3 files changed, 1120 insertions(+), 41 deletions(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index a7c83e70e..bb9478e72 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -55,6 +55,34 @@ RETURN "string" IS NORMALIZED | Introduction of an xref::syntax/operators.adoc#match-string-is-normalized[IS NORMALIZED] operator. The operator can be used to check if a `STRING` is normalized according to the specified normalization form, which can be of type `NFC`, `NFD`, `NFKC`, or `NFKD`. +a| +label:functionality[] +label:new[] + +New operators: + +* xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-all-nodes-scan[`PartitionedAllNodesScan`] +* xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-all-relationships-scan[`PartitionedDirectedAllRelationshipsScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-relationship-index-scan[`PartitionedDirectedRelationshipIndexScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-relationship-index-seek[`PartitionedDirectedRelationshipIndexSeek`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-relationship-index-seek-by-range[`PartitionedDirectedRelationshipIndexSeekByRange`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-union-relationship-types-scan[`PartitionedDirectedUnionRelationshipTypesScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-by-label-scan[`PartitionedNodeByLabelScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-index-scan[`PartitionedNodeIndexScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-index-seek[`PartitionedNodeIndexSeek`] +* xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-index-seek-by-range[`PartitionedNodeIndexSeekByRange`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-all-relationships-scan[`PartitionedUndirectedAllRelationshipsScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-relationship-index-scan[`PartitionedUndirectedRelationshipIndexScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-relationship-index-seek[`PartitionedUndirectedRelationshipIndexSeek`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-relationship-index-seek-by-range[`PartitionedUndirectedRelationshipIndexSeekByRange`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-relationship-type-scan[`PartitionedUndirectedRelationshipTypeScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-union-relationship-types-scan[`PartitionedUndirectedUnionRelationshipTypesScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-union-node-by-labels-scan[`PartitionedUnionNodeByLabelsScan`] +* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-unwind[`PartitionedUnwind`] + +| Introduction of partitioned operators used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +These operators segment the data and operate on each segment in parallel + |=== [[cypher-deprecations-additions-removals-5.16]] diff --git a/modules/ROOT/pages/planning-and-tuning/operators/index.adoc b/modules/ROOT/pages/planning-and-tuning/operators/index.adoc index cf830c219..cb8a1dc50 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/index.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/index.adoc @@ -471,6 +471,130 @@ It is not eager. | | +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-all-nodes-scan[PartitionedAllNodesScan] +| Used by the parallel runtime to read all nodes from the node store. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-all-relationships-scan[PartitionedDirectedAllRelationshipsScan] +| Used by the parallel runtime to fetch all relationships and their start and end nodes from the database. +| label:yes[] +| +| label:new[Introduced in 5.17] + + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-relationship-index-scan[PartitionedDirectedRelationshipIndexScan] +| Used by the parallel runtime to examine all values stored in an index. +It returns all relationships with a particular type and a specified property, along with their start and end nodes. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-relationship-index-seek[PartitionedDirectedRelationshipIndexSeek] +| Finds relationships and their start and end nodes using a parallel index seek. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-relationship-index-seek-by-range[PartitionedDirectedRelationshipIndexSeekByRange] +| Finds relationships using a parallel index seek where the value of the of the specified relationship type property is within a given range. +It also finds the start and end nodes of those relationships. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-relationship-types-scan[PartitionedDirectedRelationshipTypesScan] +| Fetches all relationships with a specific type from the relationship type index using a parallel scan. +It also fetches the start and end nodes of those relationships. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-directed-union-relationship-types-scan[PartitionedDirectedUnionRelationshipTypesScan] +| Fetches all relationships with at least one of the provided types from the relationship type index using a parallel scan. +It also fetches the start and end nodes of those relationships. +| +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-by-label-scan[PartitionedNodeByLabelScan] +| Used by the parallel runtime to fetch all nodes with a specific label from the node label index. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-index-scan[PartitionedNodeIndexScan] +| Used by the parallel runtime to examine all values stored in an index, returning all nodes with a particular label and a specified property. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-index-seek[PartitionedNodeIndexSeek] +| Used by the parallel runtime to find nodes using an index seek. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-index-seek-by-range[PartitionedNodeIndexSeekByRange] +| Finds nodes using a parallel index seek where the value of the specified property is within a given range. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-all-relationships-scan[PartitionedUndirectedAllRelationshipsScan] +| Used by the parallel runtime to fetch all relationships and their start and end nodes from the database. +| label:yes[] +| +| label:new[Introduced in 5.17] + + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-relationship-index-scan[PartitionedUndirectedRelationshipIndexScan] +| Used by the parallel runtime to examine all values stored in an index, returning all relationships with a particular relationship type and a specified property. +It also returns the start and end nodes of those relationships. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-relationship-index-seek[PartitionedUndirectedRelationshipIndexSeek] +| Finds relationships and their start and end nodes using a parallel index seek. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-relationship-index-seek-by-range[PartitionedUndirectedRelationshipIndexSeekByRange] +| Finds relationships using a parallel index seek where the value of the specified relationship property type is within a given range. +It also finds the start and end nodes of those relationships. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-relationship-type-scan[PartitionedUndirectedRelationshipTypeScan] +| Used by the parallel runtime to fetch all relationships with a specific type from the relationship type index. +It also fetches the start and end nodes of those relationships. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-undirected-union-relationship-types-scan[PartitionedUndirectedUnionRelationshipTypesScan] +| Used by the parallel runtime to fetch all relationships with at least one of the provided types from the relationship type index. +It also fetches the start and end nodes of those relationships. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-union-node-by-labels-scan[PartitionedUnionNodeByLabelsScan] +| Used by the parallel runtime to fetch all nodes that have at least one of the provided labels from the node label index. +| label:yes[] +| +| label:new[Introduced in 5.17] + +| xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-unwind[PartitionedUnwind] +| Used by the parallel runtime to return one row per item in a list. +| +| +| label:new[Introduced in 5.17] + | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-procedure-call[ProcedureCall] | Calls a procedure. | diff --git a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc index 92ed087e3..59fb93429 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc @@ -125,6 +125,53 @@ Total database accesses: 36, total allocated memory: 184 ====== +[[query-plan-partitioned-all-nodes-scan]] +== Partitioned All Nodes Scan +// PartitionedAllNodesScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + + +The `PartitionedAllNodesScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-all-nodes-scan[`AllNodesScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the store to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedAllNodesScan +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (n) +RETURN n +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++--------------------------+----+---------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------+----+---------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | n | 35 | 35 | 71 | 2/0 | 6.470 | In Pipeline 1 | +| | +----+---------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedAllNodesScan | 1 | n | 35 | 35 | 35 | 3/0 | 1.491 | In Pipeline 0 | ++--------------------------+----+---------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 106 +---- + +====== + [[query-plan-directed-relationship-index-scan]] == Directed Relationship Index Scan @@ -176,6 +223,53 @@ Total database accesses: 16, total allocated memory: 184 ====== +[[query-plan-partitioned-directed-relationship-index-scan]] +== Partitioned Directed Relationship Index Scan +// PartitionedDirectedRelationshipIndexScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + + +The `PartitionedDirectedRelationshipIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-scan[`DirectedRelationshipIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedDirectedRelationshipIndexScan +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH ()-[r: WORKS_IN]->() +WHERE r.title IS NOT NULL +RETURN r +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r | 15 | 15 | 70 | 1/0 | 2.865 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedDirectedRelationshipIndexScan | 1 | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title IS NOT NULL | 15 | 15 | 16 | 2/0 | 0.527 | In Pipeline 0 | ++-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 86 +---- + +====== [[query-plan-undirected-relationship-index-scan]] == Undirected Relationship Index Scan @@ -220,6 +314,54 @@ Total database accesses: 16, total allocated memory: 184 ====== +[[query-plan-partitioned-undirected-relationship-index-scan]] +== Partitioned Undirected Relationship Index Scan +// PartitionedUndirectedRelationshipIndexScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + + +The `PartitionedUndirectedRelationshipIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-index-scan[`UndirectedRelationshipIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedUndirectedRelationshipIndexScan +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH ()-[r: WORKS_IN]-() +WHERE r.title IS NOT NULL +RETURN r +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r | 30 | 30 | 140 | 1/0 | 3.088 | In Pipeline 1 | +| | +----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUndirectedRelationshipIndexScan | 1 | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title IS NOT NULL | 30 | 30 | 16 | 2/0 | 0.572 | In Pipeline 0 | ++---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 156 +---- + +====== + [[query-plan-directed-relationship-index-seek]] == Directed Relationship Index Seek @@ -265,6 +407,54 @@ Total database accesses: 2, total allocated memory: 184 ====== +[[query-plan-partitioned-directed-relationship-index-seek]] +== Partitioned Directed Relationship Index Seek +// PartitionedDirectedRelationshipIndexSeek +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + + +The `PartitionedDirectedRelationshipIndexSeek` is a variant of the the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-seek[`DirectedRelationshipIndexSeek`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedDirectedRelationshipIndexSeek +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (candidate)-[r:WORKS_IN]->() +WHERE r.title = 'chief architect' +RETURN candidate +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | candidate | 2 | 1 | 2 | 2/0 | 0.284 | In Pipeline 1 | +| | +----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedDirectedRelationshipIndexSeek | 1 | RANGE INDEX (candidate)-[r:WORKS_IN(title)]->(anon_0) WHERE title = $autostring_0 | 2 | 1 | 2 | 2/0 | 0.148 | In Pipeline 0 | ++-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 4 +---- + +====== + [[query-plan-undirected-relationship-index-seek]] == Undirected Relationship Index Seek @@ -310,6 +500,51 @@ Total database accesses: 2, total allocated memory: 184 ====== +[[query-plan-partitioned-undirected-relationship-index-seek]] +== Partitioned Undirected Relationship Index Seek +// PartitionedUndirectedRelationshipIndexSeek + +The `PartitionedUndirectedRelationshipIndexSeek` operator finds relationships and their start and end nodes using an index seek. +The relationship variable and the index used are shown in the arguments of the operator. + + +.PartitionedUndirectedRelationshipIndexSeek +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (candidate)-[r:WORKS_IN]-() +WHERE r.title = 'chief architect' +RETURN candidate +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | candidate | 4 | 2 | 4 | 2/0 | 0.333 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUndirectedRelationshipIndexSeek | 1 | RANGE INDEX (candidate)-[r:WORKS_IN(title)]-(anon_0) WHERE title = $autostring_0 | 4 | 2 | 2 | 2/0 | 0.151 | In Pipeline 0 | ++---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 6 +---- + +====== + [[query-plan-directed-relationship-by-element-id-seek]] == Directed Relationship By Element Id Seek @@ -726,6 +961,56 @@ Total database accesses: 46, total allocated memory: 184 ====== +[[query-plan-partitioned-directed-relationship-index-seek-by-range]] +== Partitioned Directed Relationship Index Seek By Range +// PartitionedDirectedRelationshipIndexSeekByRange +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + + +The `PartitionedDirectedRelationshipIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-seek-by-range[`DirectedRelationshipIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedDirectedRelationshipIndexSeekByRange +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (candidate: Person)-[r:WORKS_IN]->(location) +WHERE r.duration > 100 +RETURN candidate +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | candidate | 4 | 15 | 30 | 1/0 | 1.031 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +Filter | 1 | candidate:Person | 4 | 15 | 30 | | | | +| | +----+----------------------------------------------------------------------------------------+----------------+------+---------+ | | | +| +PartitionedDirectedRelationshipIndexSeekByRange | 2 | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]->(location) WHERE duration > $autoint_0 | 4 | 15 | 16 | 3/0 | 0.203 | Fused in Pipeline 0 | ++--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ + +Total database accesses: 76 +---- + +====== + [[query-plan-undirected-relationship-index-seek-by-range]] == Undirected Relationship Index Seek By Range @@ -773,6 +1058,56 @@ Total database accesses: 76, total allocated memory: 184 ====== +[[query-plan-partitioned-undirected-relationship-index-seek-by-range]] +== Partitioned Undirected Relationship Index Seek By Range +// PartitionedUndirectedRelationshipIndexSeekByRange +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + + +The `PartitionedUndirectedRelationshipIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-index-seek-by-range[`UndirectedRelationshipIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the store to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedUndirectedRelationshipIndexSeekByRange +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (candidate: Person)-[r:WORKS_IN]-(location) +WHERE r.duration > 100 +RETURN candidate +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | candidate | 5 | 15 | 30 | 1/0 | 0.918 | In Pipeline 1 | +| | +----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +Filter | 1 | candidate:Person | 5 | 15 | 60 | | | | +| | +----+---------------------------------------------------------------------------------------+----------------+------+---------+ | | | +| +PartitionedUndirectedRelationshipIndexSeekByRange | 2 | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]-(location) WHERE duration > $autoint_0 | 8 | 30 | 16 | 3/0 | 0.413 | Fused in Pipeline 0 | ++----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ + +Total database accesses: 106 +---- + +====== + [[query-plan-union-node-by-labels-scan]] == Union Node By Labels Scan @@ -814,6 +1149,50 @@ Total database accesses: 13, total allocated memory: 184 ====== +[[query-plan-partitioned-union-node-by-labels-scan]] +== Partitioned Union Node By Labels Scan +// PartitionedUnionNodeByLabelsScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedUnionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-union-node-by-labels-scan[`UnionNodeByLabelsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. +====== + +.Query +[source,cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (countryOrLocation:Country|Location) +RETURN countryOrLocation +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | countryOrLocation | 17 | 11 | 22 | 2/0 | 1.548 | In Pipeline 1 | +| | +----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUnionNodeByLabelsScan | 1 | countryOrLocation:Country|Location | 17 | 11 | 13 | 2/0 | 1.976 | In Pipeline 0 | ++-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 35 +---- + +====== + [[query-plan-intersection-node-by-labels-scan]] == Intersection Node By Labels Scan @@ -857,6 +1236,51 @@ Total database accesses: 13, total allocated memory: 184 ====== +[[query-plan-partitioned-intersection-node-by-labels-scan]] +== Partitioned Intersection Node By Labels Scan +// PartitionedIntersectionNodeByLabelsScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedIntersectionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-intersection-node-by-labels-scan[`IntersectionNodeByLabelsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. +====== + +.Query +[source,cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (countryAndLocation:Country&Location) +RETURN countryAndLocation +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + + +------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | countryAndLocation | 3 | 0 | 0 | 0/0 | 0.018 | In Pipeline 1 | +| | +----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedIntersectionNodeByLabelsScan | 1 | countryAndLocation:Country&Location | 3 | 0 | 13 | 2/0 | 0.770 | In Pipeline 0 | ++------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 13 +---- + +====== + [[query-plan-directed-all-relationships-scan]] == Directed All Relationships Scan @@ -897,6 +1321,48 @@ Total database accesses: 28, total allocated memory: 184 ---- ====== +[[query-plan-partitioned-directed-all-relationships-scan]] +== Partitioned Directed All Relationships Scan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedDirectedAllRelationshipsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-all-relationships-scan[`DirectedAllRelationshipsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the store to be partitioned into different segments where each segment can be scanned independently in parallel. + +====== + +.Query +[source,cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH ()-[r]->() RETURN r +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------------+----+------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r | 28 | 28 | 83 | 2/0 | 3.872 | In Pipeline 1 | +| | +----+------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedDirectedAllRelationshipsScan | 1 | (anon_0)-[r]->(anon_1) | 28 | 28 | 28 | 3/0 | 1.954 | In Pipeline 0 | ++------------------------------------------+----+------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 111 + +---- +====== + [[query-plan-undirected-all-relationships-scan]] == Undirected All Relationships Scan //UndirectedAllRelationshipsScan @@ -904,10 +1370,140 @@ The `UndirectedAllRelationshipsScan` operator fetches all relationships and thei ====== .Query -[source,cypher] +[source,cypher] +---- +PROFILE +MATCH ()-[r]-() RETURN r +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++---------------------------------+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 56 | 56 | 0 | | | | | +| | +-----------------------+----------------+------+---------+----------------+ | | | +| +UndirectedAllRelationshipsScan | (anon_0)-[r]-(anon_1) | 56 | 56 | 28 | 120 | 3/0 | 1.110 | Fused in Pipeline 0 | ++---------------------------------+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 28, total allocated memory: 184 + +---- +====== + +[[query-plan-partitioned-undirected-all-relationships-scan]] +== Partitioned Undirected All Relationships Scan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedUndirectedAllRelationshipsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-all-relationships-scan[`UndirectedAllRelationshipsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the store to be partitioned into different segments where each segment can be scanned independently in parallel. + +====== +.Query +[source,cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH ()-[r]-() RETURN r +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++--------------------------------------------+----+-----------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------------------+----+-----------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r | 56 | 56 | 166 | 2/0 | 4.905 | In Pipeline 1 | +| | +----+-----------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUndirectedAllRelationshipsScan | 1 | (anon_0)-[r]-(anon_1) | 56 | 56 | 28 | 9/0 | 1.573 | In Pipeline 0 | ++--------------------------------------------+----+-----------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 194 + +---- +====== + +[[query-plan-directed-relationship-type-scan]] +== Directed Relationship Type Scan +// DirectedRelationshipTypeScan + +The `DirectedRelationshipTypeScan` operator fetches all relationships and their start and end nodes with a specific type from the relationship type index. + + +.DirectedRelationshipTypeScan +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH ()-[r: FRIENDS_WITH]->() +RETURN r +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-------------------------------+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 12 | 12 | 0 | | | | | +| | +-------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipTypeScan | (anon_0)-[r:FRIENDS_WITH]->(anon_1) | 12 | 12 | 13 | 120 | 2/1 | 0.557 | Fused in Pipeline 0 | ++-------------------------------+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 13, total allocated memory: 184 +---- + +====== + +[[query-plan-partitioned-directed-relationship-types-scan]] +== Partitioned Directed Relationship Type Scan +// PartitionedDirectedRelationshipTypeScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedDirectedRelationshipTypeScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-type-scan[`DirectedRelationshipTypeScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedDirectedRelationshipTypeScan +====== + +.Query +[source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH ()-[r]-() RETURN r +MATCH ()-[r: FRIENDS_WITH]->() +RETURN r ---- .Query Plan @@ -915,40 +1511,39 @@ MATCH ()-[r]-() RETURN r ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------------+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 56 | 56 | 0 | | | | | -| | +-----------------------+----------------+------+---------+----------------+ | | | -| +UndirectedAllRelationshipsScan | (anon_0)-[r]-(anon_1) | 56 | 56 | 28 | 120 | 3/0 | 1.110 | Fused in Pipeline 0 | -+---------------------------------+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 28, total allocated memory: 184 +++------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | + +------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + | +ProduceResults | 0 | r | 12 | 12 | 12 | 0/0 | 0.560 | In Pipeline 1 | + | | +----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + | +PartitionedDirectedRelationshipTypeScan | 1 | (anon_0)-[r:FRIENDS_WITH]->(anon_1) | 12 | 12 | 13 | 2/0 | 0.167 | In Pipeline 0 | + +------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + Total database accesses: 25 ---- + ====== -[[query-plan-directed-relationship-type-scan]] -== Directed Relationship Type Scan -// DirectedRelationshipTypeScan -The `DirectedRelationshipTypeScan` operator fetches all relationships and their start and end nodes with a specific type from the relationship type index. +[[query-plan-undirected-relationship-type-scan]] +== Undirected Relationship Type Scan +// UndirectedRelationshipTypeScan +The `UndirectedRelationshipTypeScan` operator fetches all relationships and their start and end nodes with a specific type from the relationship type index. -.DirectedRelationshipTypeScan ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: FRIENDS_WITH]->() +MATCH ()-[r: FRIENDS_WITH]-() RETURN r ---- @@ -963,31 +1558,35 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 12 | 12 | 0 | | | | | -| | +-------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipTypeScan | (anon_0)-[r:FRIENDS_WITH]->(anon_1) | 12 | 12 | 13 | 120 | 2/1 | 0.557 | Fused in Pipeline 0 | -+-------------------------------+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 24 | 24 | 0 | | | | | +| | +------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipTypeScan | (anon_0)-[r:FRIENDS_WITH]-(anon_1) | 24 | 24 | 13 | 120 | 2/1 | 0.749 | Fused in Pipeline 0 | ++---------------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 13, total allocated memory: 184 ---- ====== +[[query-plan-partitioned-undirected-relationship-type-scan]] +== Partitioned Undirected Relationship Type Scan +// PartitionedUndirectedRelationshipTypeScan +// New in 5.17 -[[query-plan-undirected-relationship-type-scan]] -== Undirected Relationship Type Scan -// UndirectedRelationshipTypeScan +_This feature was introduced in Neo4j 5.17._ -The `UndirectedRelationshipTypeScan` operator fetches all relationships and their start and end nodes with a specific type from the relationship type index. +The `PartitionedUndirectedRelationshipTypeScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-type-scan[`UndirectedRelationshipTypeScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE MATCH ()-[r: FRIENDS_WITH]-() RETURN r @@ -998,21 +1597,21 @@ RETURN r ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 24 | 24 | 0 | | | | | -| | +------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipTypeScan | (anon_0)-[r:FRIENDS_WITH]-(anon_1) | 24 | 24 | 13 | 120 | 2/1 | 0.749 | Fused in Pipeline 0 | -+---------------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r | 24 | 24 | 24 | 1/0 | 1.466 | In Pipeline 1 | +| | +----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUndirectedRelationshipTypeScan | 1 | (anon_0)-[r:FRIENDS_WITH]-(anon_1) | 24 | 24 | 13 | 2/0 | 0.171 | In Pipeline 0 | ++--------------------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 13, total allocated memory: 184 +Total database accesses: 37 ---- ====== @@ -1022,10 +1621,10 @@ Total database accesses: 13, total allocated memory: 184 == Directed Union Relationship Types Scan // DirectedUnionRelationshipTypesScan -The `DirectedUnionRelationshipTypeScan` operator fetches all relationships and their start and end nodes with at least one of the provided types from the relationship type index. +The `DirectedUnionRelationshipTypesScan` operator fetches all relationships and their start and end nodes with at least one of the provided types from the relationship type index. -.DirectedUnionRelationshipTypeScan +.DirectedUnionRelationshipTypesScan ====== .Query @@ -1060,12 +1659,59 @@ Total database accesses: 14, total allocated memory: 184 ====== +[[query-plan-partitioned-directed-union-relationship-types-scan]] +== Partitioned Directed Union Relationship Types Scan +// PartitionedDirectedUnionRelationshipTypesScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedDirectedUnionRelationshipTypeScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-union-relationship-types-scan[`DirectedUnionRelationshipTypesScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + + +.PartitionedDirectedUnionRelationshipTypesScan +====== + +.Query +[source,cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH ()-[friendOrFoe: FRIENDS_WITH|FOE]->() +RETURN friendOrFoe +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++------------------------------------------------+----+---------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------------------+----+---------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | friendOrFoe | 15 | 12 | 12 | 0/0 | 0.570 | In Pipeline 1 | +| | +----+---------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedDirectedUnionRelationshipTypesScan | 1 | (anon_0)-[friendOrFoe:FRIENDS_WITH|FOE]->(anon_1) | 15 | 12 | 13 | 2/0 | 0.170 | In Pipeline 0 | ++------------------------------------------------+----+---------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 25 +---- + +====== + [[query-plan-undirected-union-relationship-types-scan]] == Undirected Union Relationship Types Scan // UndirectedUnionRelationshipTypesScan -The `UndirectedUnionRelationshipTypeScan` operator fetches all relationships and their start and end nodes with at least one of the provided types from the relationship type index. +The `UndirectedUnionRelationshipTypesScan` operator fetches all relationships and their start and end nodes with at least one of the provided types from the relationship type index. .UndirectedUnionRelationshipTypeScan @@ -1103,6 +1749,52 @@ Total database accesses: 14, total allocated memory: 184 ====== +[[query-plan-partitioned-undirected-union-relationship-types-scan]] +== Partitioned Undirected Union Relationship Types Scan +// PartitionedUndirectedUnionRelationshipTypesScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedUndirectedUnionRelationshipTypeScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-union-relationship-types-scan[`UndirectedUnionRelationshipTypesScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedUndirectedUnionRelationshipTypesScan +====== + +.Query +[source,cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH ()-[friendOrFoe: FRIENDS_WITH|FOE]-() +RETURN friendOrFoe +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++--------------------------------------------------+----+--------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------------------------+----+--------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | friendOrFoe | 30 | 24 | 24 | 0/0 | 0.896 | In Pipeline 1 | +| | +----+--------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUndirectedUnionRelationshipTypesScan | 1 | (anon_0)-[friendOrFoe:FRIENDS_WITH|FOE]-(anon_1) | 30 | 24 | 13 | 2/0 | 0.818 | In Pipeline 0 | ++--------------------------------------------------+----+--------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 37 +---- + +====== + [[query-plan-node-by-elementid-seek]] == Node By ElementId Seek // NodeByElementIdSeek @@ -1235,6 +1927,53 @@ Total database accesses: 15, total allocated memory: 184 ====== +[[query-plan-partitioned-node-by-label-scan]] +== Partitioned Node By Label Scan +// PartitionedNodeByLabelScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedNodeByLabelScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-by-label-scan[`NodeByLabelScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + + +.PartitionedNodeByLabelScan +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (person:Person) +RETURN person +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | person | 14 | 14 | 28 | 2/0 | 0.623 | In Pipeline 1 | +| | +----+---------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedNodeByLabelScan | 1 | person:Person | 14 | 14 | 15 | 1/0 | 0.094 | In Pipeline 0 | ++-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 43 +---- + +====== + [[query-plan-node-index-seek]] == Node Index Seek @@ -1287,6 +2026,53 @@ Total database accesses: 2, total allocated memory: 184 ====== +[[query-plan-partitioned-node-index-seek]] +== Partitioned Node Index Seek +// PartitionedNodeIndexSeek +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedNodeIndexSeek` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[`NodeIndexSeek`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + + +.PartitionedNodeIndexSeek +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (location:Location {name: 'Malmo'}) +RETURN location +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | location | 0 | 1 | 2 | 2/0 | 0.179 | In Pipeline 1 | +| | +----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedNodeIndexSeek | 1 | RANGE INDEX location:Location(name) WHERE name = $autostring_0 | 0 | 1 | 2 | 1/0 | 0.167 | In Pipeline 0 | ++---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 4 +---- + +====== + [[query-plan-node-unique-index-seek]] == Node Unique Index Seek @@ -1502,6 +2288,54 @@ Total database accesses: 2, total allocated memory: 184 ====== +[[query-plan-partitioned-node-index-seek-by-range]] +== Partitioned Node Index Seek By Range +// PartitionedNodeIndexSeekByRange +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedNodeIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek-by-range[`NodeIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + + +.PartitionedNodeIndexSeekByRange +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (l:Location) +WHERE l.name STARTS WITH 'Lon' +RETURN l +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | l | 0 | 1 | 2 | 2/0 | 0.191 | In Pipeline 1 | +| | +----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedNodeIndexSeekByRange | 1 | RANGE INDEX l:Location(name) WHERE name STARTS WITH $autostring_0 | 0 | 1 | 2 | 1/0 | 0.087 | In Pipeline 0 | ++----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 4 +---- + +====== + [[query-plan-node-unique-index-seek-by-range]] == Node Unique Index Seek By Range @@ -1689,6 +2523,53 @@ Total database accesses: 11, total allocated memory: 184 ====== +[[query-plan-partitioned-node-index-scan]] +== Partitioned Node Index Scan +// PartitionedNodeIndexScan +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedNodeIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-scan[`NodeIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + + +.PartitionedNodeIndexScan +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (l:Location) +WHERE l.name IS NOT NULL +RETURN l +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | l | 1 | 10 | 20 | 2/0 | 0.472 | In Pipeline 1 | +| | +----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedNodeIndexScan | 1 | RANGE INDEX l:Location(name) WHERE name IS NOT NULL | 1 | 10 | 11 | 1/0 | 0.187 | In Pipeline 0 | ++---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 31 +---- + +====== // --- apply operators --- @@ -4742,6 +5623,52 @@ Total database accesses: 0, total allocated memory: 184 ====== +[[query-plan-partitioned-unwind]] +== Partitioned Unwind +// PartitionedUnwind +// New in 5.17 + +_This feature was introduced in Neo4j 5.17._ + +The `PartitionedUnwind` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-unwind[`Unwind`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + + +.PartitionedUnwind +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +UNWIND range(1, 5) AS value +RETURN value +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++--------------------+----+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------+----+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | value | 10 | 5 | 0 | 0/0 | 0.119 | In Pipeline 1 | +| | +----+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +PartitionedUnwind | 1 | range($autoint_0, $autoint_1) AS value | 10 | 5 | 0 | | | Fused in Pipeline 0 | ++--------------------+----+----------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ + +Total database accesses: 0 +---- + +====== [[query-plan-exhaustive-limit]] == Exhaustive Limit From bf15641c9714a2cd41a2a1cd43de61829fc9e5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 23 Jan 2024 14:18:14 +0100 Subject: [PATCH 368/383] Update Parallel runtime example query plan (#860) --- .../runtimes/concepts.adoc | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/modules/ROOT/pages/planning-and-tuning/runtimes/concepts.adoc b/modules/ROOT/pages/planning-and-tuning/runtimes/concepts.adoc index 813ecaf95..c40aa1dc5 100644 --- a/modules/ROOT/pages/planning-and-tuning/runtimes/concepts.adoc +++ b/modules/ROOT/pages/planning-and-tuning/runtimes/concepts.adoc @@ -273,44 +273,47 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------+----+------------------------------------------------------------------------+----------------+---------------------+ -| Operator | Id | Details | Estimated Rows | Pipeline | -+-------------------+----+------------------------------------------------------------------------+----------------+---------------------+ -| +ProduceResults | 0 | `count(*)` | 1 | In Pipeline 6 | -| | +----+------------------------------------------------------------------------+----------------+---------------------+ -| +EagerAggregation | 1 | count(*) AS `count(*)` | 1 | | -| | +----+------------------------------------------------------------------------+----------------+ | -| +Filter | 2 | not anon_1 = anon_5 AND anon_0.name = $autostring_0 AND anon_0:Station | 0 | | -| | +----+------------------------------------------------------------------------+----------------+ | -| +Expand(All) | 3 | (d)-[anon_1:CALLS_AT]->(anon_0) | 0 | Fused in Pipeline 5 | -| | +----+------------------------------------------------------------------------+----------------+---------------------+ -| +Filter | 4 | d:Stop | 0 | | -| | +----+------------------------------------------------------------------------+----------------+ | -| +NullifyMetadata | 14 | | 0 | | -| | +----+------------------------------------------------------------------------+----------------+ | -| +Repeat(Trail) | 5 | (a) (...){1, *} (d) | 0 | Fused in Pipeline 4 | -| |\ +----+------------------------------------------------------------------------+----------------+---------------------+ -| | +Filter | 6 | isRepeatTrailUnique(anon_7) AND anon_2:Stop | 6 | | -| | | +----+------------------------------------------------------------------------+----------------+ | -| | +Expand(All) | 7 | (anon_4)<-[anon_7:NEXT]-(anon_2) | 6 | Fused in Pipeline 3 | -| | | +----+------------------------------------------------------------------------+----------------+---------------------+ -| | +Filter | 8 | anon_4:Stop | 11 | | -| | | +----+------------------------------------------------------------------------+----------------+ | -| | +Argument | 9 | anon_4 | 13 | Fused in Pipeline 2 | -| | +----+------------------------------------------------------------------------+----------------+---------------------+ -| +Filter | 10 | a:Stop | 0 | | -| | +----+------------------------------------------------------------------------+----------------+ | -| +Expand(All) | 11 | (anon_6)<-[anon_5:CALLS_AT]-(a) | 0 | Fused in Pipeline 1 | -| | +----+------------------------------------------------------------------------+----------------+---------------------+ -| +Filter | 12 | anon_6.name = $autostring_1 | 1 | | -| | +----+------------------------------------------------------------------------+----------------+ | -| +NodeByLabelScan | 13 | anon_6:Station | 10 | Fused in Pipeline 0 | -+-------------------+----+------------------------------------------------------------------------+----------------+---------------------+ ++-----------------------------+----+------------------------------------------------------------------------+----------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Pipeline | ++-----------------------------+----+------------------------------------------------------------------------+----------------+---------------------+ +| +ProduceResults | 0 | `count(*)` | 1 | In Pipeline 6 | +| | +----+------------------------------------------------------------------------+----------------+---------------------+ +| +EagerAggregation | 1 | count(*) AS `count(*)` | 1 | | +| | +----+------------------------------------------------------------------------+----------------+ | +| +Filter | 2 | NOT anon_1 = anon_5 AND anon_0.name = $autostring_0 AND anon_0:Station | 0 | | +| | +----+------------------------------------------------------------------------+----------------+ | +| +Expand(All) | 3 | (d)-[anon_1:CALLS_AT]->(anon_0) | 0 | Fused in Pipeline 5 | +| | +----+------------------------------------------------------------------------+----------------+---------------------+ +| +Filter | 4 | d:Stop | 0 | | +| | +----+------------------------------------------------------------------------+----------------+ | +| +NullifyMetadata | 14 | | 0 | | +| | +----+------------------------------------------------------------------------+----------------+ | +| +Repeat(Trail) | 5 | (a) (...){1, *} (d) | 0 | Fused in Pipeline 4 | +| |\ +----+------------------------------------------------------------------------+----------------+---------------------+ +| | +Filter | 6 | isRepeatTrailUnique(anon_8) AND anon_7:Stop | 6 | | +| | | +----+------------------------------------------------------------------------+----------------+ | +| | +Expand(All) | 7 | (anon_9)<-[anon_8:NEXT]-(anon_7) | 6 | Fused in Pipeline 3 | +| | | +----+------------------------------------------------------------------------+----------------+---------------------+ +| | +Filter | 8 | anon_9:Stop | 11 | | +| | | +----+------------------------------------------------------------------------+----------------+ | +| | +Argument | 9 | anon_9 | 13 | Fused in Pipeline 2 | +| | +----+------------------------------------------------------------------------+----------------+---------------------+ +| +Filter | 10 | a:Stop | 0 | | +| | +----+------------------------------------------------------------------------+----------------+ | +| +Expand(All) | 11 | (anon_6)<-[anon_5:CALLS_AT]-(a) | 0 | Fused in Pipeline 1 | +| | +----+------------------------------------------------------------------------+----------------+---------------------+ +| +Filter | 12 | anon_6.name = $autostring_1 | 1 | | +| | +----+------------------------------------------------------------------------+----------------+ | +| +PartitionedNodeByLabelScan | 13 | anon_6:Station | 10 | Fused in Pipeline 0 | ++-----------------------------+----+------------------------------------------------------------------------+----------------+---------------------+ ---- A key difference between the physical plans produced by the parallel runtime compared to those generated by pipelined runtime is that, in general, more pipelines are produced when using the parallel runtime (in this case, seven instead of the four produced by the same query being run on pipelined runtime). This is because, when executing a query in the parallel runtime, it is more efficient to have more tasks that can be run in parallel, whereas when running a single-threaded execution in the pipelined runtime it is more efficient to fuse several pipelines together. +Another important difference is that the parallel runtime uses partitioned operators (xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-partitioned-node-by-label-scan[`PartitionedNodeByLabelScan`] in this case). +These operators first segment the retrieved data and then operate on each segment in parallel. + The parallel runtime shares the same architecture as the pipelined runtime, meaning that it will transform the logical plan into the same type of execution graph as described above. However, when using parallel runtime, each pipeline task can be executed in a separate thread. Another similarity with pipelined runtime is that queries run on the parallel runtime will begin by generating the first pipeline which eventually will produce a morsel in the input buffer of the subsequent pipeline. @@ -331,10 +334,10 @@ Consider the execution graph below, based on the same example query: image::runtimes_execution_graph2.svg[width="900",role="middle"] -The execution graph shows that execution starts at `pipeline 0`, which consists of the operator `NodeByLabelScan` and can be executed simultaneously on all available threads working on different morsels of data. +The execution graph shows that execution starts at `pipeline 0`, which consists of the operator `PartitionedNodeByLabelScan` and can be executed simultaneously on all available threads working on different morsels of data. Once pipeline `0` has produced at least one full morsel of data, any thread can then start executing `pipeline 1`, while other threads may continue to execute `pipeline 0`. More specifically, once there is data from a pipeline, the scheduler can proceed to the next pipeline while concurrently executing earlier pipelines. -In this case, `pipeline 5` ends with an aggregation (performed by the EagerAggregation operator), which means that the last pipeline (6) cannot start until all preceding pipelines are completely finished for all the preceding morsels of data. +In this case, `pipeline 5` ends with an aggregation (performed by the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-eager-aggregation[`EagerAggregation`] operator), which means that the last pipeline (`6`) cannot start until all preceding pipelines are completely finished for all the preceding morsels of data. [[runtimes-parallel-runtime-considerations]] === Considerations From 4c06d09074ef3924962023f324560a6d3be377c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louise=20S=C3=B6derstr=C3=B6m?= Date: Wed, 24 Jan 2024 11:07:36 +0100 Subject: [PATCH 369/383] Mention explicitly that unsignedInteger must not be larger than Long.MAX_VALUE. (#841) --- modules/ROOT/pages/patterns/reference.adoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/ROOT/pages/patterns/reference.adoc b/modules/ROOT/pages/patterns/reference.adoc index e3d81a82b..e7688332c 100644 --- a/modules/ROOT/pages/patterns/reference.adoc +++ b/modules/ROOT/pages/patterns/reference.adoc @@ -760,6 +760,11 @@ upperBound ::= unsignedInteger unsignedInteger ::= [0-9]+ ---- +[NOTE] +==== +The `unsignedInteger` must not be larger than the Java constant `Long.MAX_VALUE` (2^63-1). +==== + [[quantifiers-rules]] === Rules @@ -1104,6 +1109,11 @@ upperBound ::= unsignedInteger unsignedInteger ::= [0-9]+ ---- +[NOTE] +==== +The `unsignedInteger` must not be larger than the Java constant `Long.MAX_VALUE` (2^63-1). +==== + For rules on valid relationship variable names, see xref:syntax/naming.adoc[Cypher naming rules]. [[variable-length-relationships-rules]] From e448ad7f968ea68994743dfa840549579150ba26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:40:44 +0100 Subject: [PATCH 370/383] Fix broken links (#865) --- modules/ROOT/content-nav.adoc | 3 +++ modules/ROOT/pages/clauses/order-by.adoc | 2 +- modules/ROOT/pages/clauses/use.adoc | 2 +- modules/ROOT/pages/patterns/concepts.adoc | 1 - 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 6b1add734..b82e15537 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -119,3 +119,6 @@ * Appendix ** xref:styleguide.adoc[] ** xref:appendix/tutorials/index.adoc[] +*** xref:appendix/tutorials/basic-query-tuning.adoc[] +*** xref:appendix/tutorials/advanced-query-tuning.adoc[] +*** xref:appendix/tutorials/shortestpath-planning.adoc[] diff --git a/modules/ROOT/pages/clauses/order-by.adoc b/modules/ROOT/pages/clauses/order-by.adoc index af3afcc60..d71bd51e8 100644 --- a/modules/ROOT/pages/clauses/order-by.adoc +++ b/modules/ROOT/pages/clauses/order-by.adoc @@ -240,4 +240,4 @@ This rule is to make sure that `ORDER BY` does not change the results, only the The performance of Cypher queries using `ORDER BY` on node properties can be influenced by the existence and use of an index for finding the nodes. If the index can provide the nodes in the order requested in the query, Cypher can avoid the use of an expensive `Sort` operation. -Read more about this capability in xref::appendix/tutorials/advanced-query-tuning.adoc#advanced-query-tuning-example-index-backed-order-by[Index-backed ORDER BY]. +Read more about this capability in xref::indexes/search-performance-indexes/using-indexes.adoc#range-index-backed-order-by[Range index-backed ORDER BY]. diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index 9cff87f1e..54dacfba5 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -27,7 +27,7 @@ Where `` refers to the name or alias of a database in the DBMS. [[query-use-syntax-composite]] === Composite database syntax -When running queries against a link:{neo4j-docs-base-uri}/operations-manual/{page-version}/composite-databases[composite database], the `USE` clause can also appear as the first clause of: +When running queries against a link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/composite-databases/concepts/[composite database], the `USE` clause can also appear as the first clause of: * Union parts: + diff --git a/modules/ROOT/pages/patterns/concepts.adoc b/modules/ROOT/pages/patterns/concepts.adoc index 24ea0a0e2..3715e8894 100644 --- a/modules/ROOT/pages/patterns/concepts.adoc +++ b/modules/ROOT/pages/patterns/concepts.adoc @@ -836,7 +836,6 @@ RETURN [n in nodes(p) | n.name] AS stops |=== On a large, highly connected graph, this can be very time consuming. -See the section on xref::appendix/tutorials/shortestpath-planning.adoc[shortest path planning] for more information. [[shortest-path-all-shortest-paths]] === All shortest paths From d743e8d77599a1a6d452cc30cb5ab36c00cee6c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:02:37 +0100 Subject: [PATCH 371/383] Bump the dev-dependencies group with 1 update (#861) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the dev-dependencies group with 1 update: [nodemon](https://github.com/remy/nodemon). Updates `nodemon` from 3.0.2 to 3.0.3
Release notes

Sourced from nodemon's releases.

v3.0.3

3.0.3 (2024-01-16)

Bug Fixes

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nodemon&package-manager=npm_and_yarn&previous-version=3.0.2&new-version=3.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88f1c92b8..7f25845cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ }, "devDependencies": { "express": "^4.17.1", - "nodemon": "^3.0.2" + "nodemon": "^3.0.3" } }, "node_modules/@antora/asciidoc-loader": { @@ -1838,9 +1838,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/nodemon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz", - "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", + "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", "dev": true, "dependencies": { "chokidar": "^3.5.2", @@ -4444,9 +4444,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "nodemon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz", - "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", + "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", "dev": true, "requires": { "chokidar": "^3.5.2", diff --git a/package.json b/package.json index e15cf3ae6..0633e84c2 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "express": "^4.17.1", - "nodemon": "^3.0.2" + "nodemon": "^3.0.3" }, "overrides": { "@antora/site-generator-default": { From 3fcf9f5be8bac1916cfd0c5ba81d34acddb8f2f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:34:49 +0100 Subject: [PATCH 372/383] Update SHOW SETTINGS table to include `dbms.cluster.network.connect_timeout` (#862) Failure resolved when https://github.com/neo-technology/neo4j/pull/23939 is merged --- modules/ROOT/pages/clauses/listing-settings.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/clauses/listing-settings.adoc b/modules/ROOT/pages/clauses/listing-settings.adoc index cee906299..40b1ea5d0 100644 --- a/modules/ROOT/pages/clauses/listing-settings.adoc +++ b/modules/ROOT/pages/clauses/listing-settings.adoc @@ -227,6 +227,10 @@ LIMIT 10 | "20s" | "Time out for protocol negotiation handshake." +| "dbms.cluster.network.connect_timeout" +| "30s" +| "The maximum amount of time to wait for a network connection to be established." + | "dbms.cluster.network.max_chunk_size" | "32768" | "Maximum chunk size allowable across network by clustering machinery." @@ -235,10 +239,6 @@ LIMIT 10 | "" | "Network compression algorithms that this instance will allow in negotiation as a comma-separated list. Listed in descending order of preference for incoming connections. An empty list implies no compression. For outgoing connections this merely specifies the allowed set of algorithms and the preference of the remote peer will be used for making the decision. Allowable values: [Gzip, Snappy, Snappy_validating, LZ4, LZ4_high_compression, LZ_validating, LZ4_high_compression_validating]" -| "dbms.cluster.raft.binding_timeout" -| "1d" -| "The time allowed for a database on a Neo4j server to either join a cluster or form a new cluster with at least the quorum of the members available. The members are provided by `dbms.cluster.discovery.endpoints` for the system database and by the topology graph for user databases." - 3+d|Rows: 10 |=== From fab239b52cf256c461aea2c50ef26f245c892e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 26 Jan 2024 13:00:02 +0100 Subject: [PATCH 373/383] Update value types: `STRING` (#853) --- modules/ROOT/pages/clauses/call.adoc | 2 +- .../ROOT/pages/clauses/listing-settings.adoc | 2 +- modules/ROOT/pages/clauses/load-csv.adoc | 11 +++-- .../pages/clauses/transaction-clauses.adoc | 26 ++++++------ modules/ROOT/pages/clauses/use.adoc | 2 +- modules/ROOT/pages/clauses/where.adoc | 22 +++++----- modules/ROOT/pages/constraints/examples.adoc | 14 +++---- ...ions-additions-removals-compatibility.adoc | 18 ++++---- modules/ROOT/pages/functions/aggregating.adoc | 14 +++---- modules/ROOT/pages/functions/index.adoc | 4 +- modules/ROOT/pages/functions/list.adoc | 2 +- modules/ROOT/pages/functions/predicate.adoc | 6 +-- modules/ROOT/pages/functions/scalar.adoc | 12 +++--- modules/ROOT/pages/functions/string.adoc | 6 +-- .../pages/functions/temporal/duration.adoc | 2 +- .../ROOT/pages/functions/temporal/index.adoc | 42 +++++++++---------- .../planning-and-tuning/operators/index.adoc | 20 ++++----- .../operators/operators-detail.adoc | 20 ++++----- modules/ROOT/pages/styleguide.adoc | 8 ++-- modules/ROOT/pages/syntax/operators.adoc | 21 +++++----- modules/ROOT/pages/syntax/parameters.adoc | 2 +- modules/ROOT/pages/syntax/parsing.adoc | 6 +-- .../ROOT/pages/values-and-types/lists.adoc | 2 +- .../ROOT/pages/values-and-types/spatial.adoc | 2 +- .../ROOT/pages/values-and-types/temporal.adoc | 2 +- 25 files changed, 133 insertions(+), 135 deletions(-) diff --git a/modules/ROOT/pages/clauses/call.adoc b/modules/ROOT/pages/clauses/call.adoc index f2cf65652..326116a59 100644 --- a/modules/ROOT/pages/clauses/call.adoc +++ b/modules/ROOT/pages/clauses/call.adoc @@ -328,7 +328,7 @@ Since the procedure call is part of a larger query, all outputs must be named ex [[call-call-a-procedure-and-filter-its-results]] == Call a procedure and filter its results -This calls the built-in procedure `db.labels` to count all in-use labels in the database that contain the string `'User'`. +This calls the built-in procedure `db.labels` to count all in-use labels in the database that contain the string 'User'`. .Query [source, cypher] diff --git a/modules/ROOT/pages/clauses/listing-settings.adoc b/modules/ROOT/pages/clauses/listing-settings.adoc index 40b1ea5d0..6dcf48a42 100644 --- a/modules/ROOT/pages/clauses/listing-settings.adoc +++ b/modules/ROOT/pages/clauses/listing-settings.adoc @@ -89,7 +89,7 @@ SHOW SETTING[S] [setting-name[,...]] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] ---- -Setting names must be supplied as one or more comma-separated quoted strings or as an expression resolving to a string or a list of strings. +Setting names must be supplied as one or more comma-separated quoted `STRING` values or as an expression resolving to a `STRING` or a `LIST`. [NOTE] ==== diff --git a/modules/ROOT/pages/clauses/load-csv.adoc b/modules/ROOT/pages/clauses/load-csv.adoc index 526a9ca73..c720dde8e 100644 --- a/modules/ROOT/pages/clauses/load-csv.adoc +++ b/modules/ROOT/pages/clauses/load-csv.adoc @@ -62,10 +62,10 @@ The CSV file to use with `LOAD CSV` must have the following characteristics: * the end line termination is system dependent, e.g., it is `\n` on unix or `\r\n` on windows; * the default field terminator is `,`; * the field terminator character can be change by using the option `FIELDTERMINATOR` available in the `LOAD CSV` command; -* quoted strings are allowed in the CSV file and the quotes are dropped when reading the data; -* the character for string quotation is double quote `"`; +* quoted `STRING` values are allowed in the CSV file and the quotes are dropped when reading the data; +* the character for `STRING` quotation is double quote `"`; * if `dbms.import.csv.legacy_quote_escaping` is set to the default value of `true`, `\` is used as an escape character; -* a double quote must be in a quoted string and escaped, either with the escape character or a second double quote. +* a double quote must be in a quoted `STRING` and escaped, either with the escape character or a second double quote. [[load-csv-import-data-from-a-csv-file]] == Import data from a CSV file @@ -139,7 +139,7 @@ Labels added: 4 [[load-csv-import-data-from-a-csv-file-containing-headers]] == Import data from a CSV file containing headers -When your CSV file has headers, you can view each row in the file as a map instead of as an array of strings. +When your CSV file has headers, you can view each row in the file as a map instead of as a `LIST`. .artists-with-headers.csv [source, csv, role="noheaders", filename="artists-with-headers.csv"] @@ -314,8 +314,7 @@ RETURN size(a.name) AS size ---- -Note that strings are wrapped in quotes in the output here. -You can see that when comparing to the length of the string in this case! +Note that the `STRING` values are wrapped in quotes in the output. .Result [role="queryresult",options="header,footer",cols="3* // New in 5.0 m| currentQueryStartTime -a| The time at which the query currently executing in this transaction was started, or an empty string if no query is currently executing. +a| The time at which the query currently executing in this transaction was started, or an empty `STRING` if no query is currently executing. m| STRING m| protocol @@ -111,11 +111,11 @@ m| STRING // New in 5.0 m| currentQueryStatus -a| The current status of the query currently executing in this transaction (`parsing`, `planning`, `planned`, `running`, or `waiting`), or an empty string if no query is currently executing. +a| The current status of the query currently executing in this transaction (`parsing`, `planning`, `planned`, `running`, or `waiting`), or an empty `STRING` if no query is currently executing. m| STRING m| statusDetails -a| Provide additional status details from the underlying transaction or an empty string if none is available. +a| Provide additional status details from the underlying transaction or an empty `STRING` if none is available. m| STRING m| resourceInformation @@ -195,7 +195,7 @@ a| The total number of page cache faults that the query currently executing in t m| INTEGER m| initializationStackTrace -a| The initialization stacktrace for this transaction, or an empty string if unavailable. +a| The initialization stacktrace for this transaction, or an empty `STRING` if unavailable. m| STRING |=== @@ -222,7 +222,7 @@ SHOW TRANSACTION[S] [transaction-id[,...]] ---- The format of `transaction-id` is `-transaction-`. -Transaction IDs must be supplied as one or more comma-separated quoted strings or as an expression resolving to a string or a list of strings. +Transaction IDs must be supplied as one or more comma-separated quoted `STRING` values, or as an expression resolving to a `STRING` or a `LIST`. [NOTE] ==== @@ -388,7 +388,7 @@ TERMINATE TRANSACTION[S] transaction_id[, ...] ---- The format of `transaction-id` is `-transaction-`. -Transaction IDs must be supplied as one or more comma-separated quoted strings or as an expression resolving to a string or a list of strings. +Transaction IDs must be supplied as one or more comma-separated quoted `STRING` values, or as an expression resolving to a `STRING` or a `LIST`. [NOTE] ==== diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index 54dacfba5..e76a65e97 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -95,7 +95,7 @@ MATCH (n) RETURN n [[query-use-examples-query-composite-database-constituent-graph-dynamically]] === Query a composite database constituent graph dynamically -The built-in function `graph.byName()` can be used in the `USE` clause to resolve a constituent graph from a string value containing the qualified name of a constituent. +The built-in function `graph.byName()` can be used in the `USE` clause to resolve a constituent graph from a `STRING` value containing the qualified name of a constituent. This example uses a composite database named `myComposite` that includes an alias named `myConstituent`: diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index 088b66bc3..90334ffab 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -258,18 +258,18 @@ In this case, `name` is now the only variable in scope for the `RETURN` clause. [[query-where-string]] -== String matching +== `STRING` matching -The prefix and suffix of a string can be matched using `STARTS WITH` and `ENDS WITH`. -To undertake a substring search (that is, match regardless of the location within a string), use `CONTAINS`. +The prefix and suffix of a `STRING` can be matched using `STARTS WITH` and `ENDS WITH`. +To undertake a substring search (that is, match regardless of the location within a `STRING`), use `CONTAINS`. The matching is _case-sensitive_. -Attempting to use these operators on values which are not strings will return `null`. +Attempting to use these operators on values which are not `STRING` values will return `null`. [[match-string-start]] -=== Prefix string search using `STARTS WITH` +=== Prefix `STRING` search using `STARTS WITH` -The `STARTS WITH` operator is used to perform case-sensitive matching on the beginning of a string: +The `STARTS WITH` operator is used to perform case-sensitive matching on the beginning of a `STRING`: .Query [source, cypher] @@ -291,9 +291,9 @@ The `name` and `age` values for `Peter` are returned because his name starts wit [[match-string-end]] -=== Suffix string search using `ENDS WITH` +=== Suffix `STRING` search using `ENDS WITH` -The `ENDS WITH` operator is used to perform case-sensitive matching on the ending of a string: +The `ENDS WITH` operator is used to perform case-sensitive matching on the ending of a `STRING`: .Query [source, cypher] @@ -317,7 +317,7 @@ The `name` and `age` values for `Peter` are returned because his name ends with [[match-string-contains]] === Substring search using `CONTAINS` -The `CONTAINS` operator is used to perform case-sensitive matching regardless of location within a string: +The `CONTAINS` operator is used to perform case-sensitive matching regardless of location within a `STRING`: .Query [source, cypher] @@ -370,7 +370,7 @@ For example, `RETURN 1 IS NORMALIZED` returns `null`. [[match-string-negation]] === String matching negation -Use the `NOT` keyword to exclude all matches on given string from your result: +Use the `NOT` keyword to exclude all matches on given `STRING` from your result: .Query [source, cypher] @@ -396,7 +396,7 @@ The `name` and `age` values `Peter` are returned because his name does not end w Cypher supports filtering using regular expressions. The regular expression syntax is inherited from the link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html[Java regular expressions]. -This includes support for flags that change how strings are matched, including case-insensitive `(?i)`, multiline `(?m)`, and dotall `(?s)`. +This includes support for flags that change how `STRING` values are matched, including case-insensitive `(?i)`, multiline `(?m)`, and dotall `(?s)`. Flags are given at the beginning of the regular expression. For an example of a regular expression flag given at the beginning of a pattern, see the xref::clauses/where.adoc#case-insensitive-regular-expressions[case-insensitive regular expression] section. diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc index 76678f29f..cc37faf62 100644 --- a/modules/ROOT/pages/constraints/examples.adoc +++ b/modules/ROOT/pages/constraints/examples.adoc @@ -1401,7 +1401,7 @@ CREATE CONSTRAINT movie_titles IF NOT EXISTS FOR (movie:Movie) REQUIRE movie.title :: STRING ---- -Assuming a node property type constraint on the label `Movie` which restricts the property `title` to string values already exists: +Assuming a node property type constraint on the label `Movie` which restricts the property `title` to `STRING` values already exists: .Result [queryresult] @@ -1420,7 +1420,7 @@ Assuming a node property type constraint on the label `Movie` which restricts th .+CREATE CONSTRAINT+ ====== -Create a node property type constraint restricting the property `title` to string values on nodes with the `Movie` label, when that constraint already exists: +Create a node property type constraint restricting the property `title` to `STRING` values on nodes with the `Movie` label, when that constraint already exists: .Query [source, cypher, role=test-fail] @@ -1557,7 +1557,7 @@ Failed to create node property type constraint: Invalid property type `LIST``. a| label:functionality[] @@ -1432,7 +1432,7 @@ OPTIONS "{" btree-option: btree-value[, ...] "}" a| B-tree indexes are removed. -B-tree indexes used for string queries are replaced by: +B-tree indexes used for `STRING` predicates are replaced by: [source, cypher, role="noheader"] ---- CREATE TEXT INDEX ... @@ -1502,7 +1502,7 @@ Replaced by: ---- CREATE CONSTRAINT ... ---- -Constraints used for string properties require an additional text index to cover the string queries properly. +Constraints used for `STRING` properties require an additional text index to cover the `STRING` predicates properly. Constraints used for point properties require an additional point index to cover the spatial queries properly. a| @@ -1913,7 +1913,7 @@ SHOW TRANSACTIONS [transaction-id[,...]] TERMINATE TRANSACTIONS transaction-id[,...] ---- a| -`transaction-id` now allows general expressions resolving to a string or a list of strings instead of just parameters. +`transaction-id` now allows general expressions resolving to a `STRING` or `LIST` instead of just parameters. a| label:functionality[] @@ -1944,8 +1944,8 @@ The query does not require a specific order and there can be zero or more of eac When the command is not in standalone mode, the `YIELD` and `RETURN` clauses are mandatory. `YIELD *` is not allowed. -`transaction-id` is a comma-separated list of one or more quoted strings. -It could also be an expression resolving to a string or a list of strings (for example the output column from `SHOW`). +`transaction-id` is a comma-separated list of one or more quoted `STRING` values. +It could also be an expression resolving to a `STRING` or a `LIST` (for example the output column from `SHOW`). a| label:functionality[] @@ -2711,7 +2711,7 @@ SHOW TRANSACTION[S] [transaction-id[,...]] a| List transactions on the current server. -The `transaction-id` is a comma-separated list of one or more quoted strings, a string parameter, or a list parameter. +The `transaction-id` is a comma-separated list of one or more quoted `STRING` values, a `STRING` parameter, or a list parameter. This replaces the procedures `dbms.listTransactions` and `dbms.listQueries`. @@ -2725,7 +2725,7 @@ TERMINATE TRANSACTION[S] transaction-id[,...] a| Terminate transactions on the current server. -The `transaction-id` is a comma-separated list of one or more quoted strings, a string parameter, or a list parameter. +The `transaction-id` is a comma-separated list of one or more quoted `STRING` values, a `STRING` parameter, or a list parameter. This replaces the procedures `dbms.killTransaction`, `dbms.killTransactions`, `dbms.killQuery`, and `dbms.killQueries`. diff --git a/modules/ROOT/pages/functions/aggregating.adoc b/modules/ROOT/pages/functions/aggregating.adoc index 0c1239e0a..35fbb6707 100644 --- a/modules/ROOT/pages/functions/aggregating.adoc +++ b/modules/ROOT/pages/functions/aggregating.adoc @@ -441,7 +441,7 @@ max(expression) |=== | Any `null` values are excluded from the calculation. -| In a mixed set, any numeric value is always considered to be higher than any string value, and any string value is always considered to be higher than any list. +| In a mixed set, any numeric value is always considered to be higher than any `STRING` value, and any `STRING` value is always considered to be higher than any `LIST``. | Lists are compared in dictionary order, i.e. list elements are compared pairwise in ascending order from the start of the list to the end. | `max(null)` returns `null`. @@ -462,7 +462,7 @@ The highest of all the values in the mixed set -- in this case, the numeric valu [NOTE] ==== -The value `'99'` (a string), is considered to be a lower value than `1` (an integer), because `'99'` is a string. +The value `'99'` (a `STRING`), is considered to be a lower value than `1` (an `INTEGER`), because `'99'` is a `STRING`. ==== .Result @@ -486,7 +486,7 @@ UNWIND [[1, 'a', 89], [1, 2]] AS val RETURN max(val) ---- -The highest of all the lists in the set -- in this case, the list `[1, 2]` -- is returned, as the number `2` is considered to be a higher value than the string `'a'`, even though the list `[1, 'a', 89]` contains more elements. +The highest of all the lists in the set -- in this case, the list `[1, 2]` -- is returned, as the number `2` is considered to be a higher value than the `STRING` `'a'`, even though the list `[1, 'a', 89]` contains more elements. .Result [role="queryresult",options="header,footer",cols="1*` is always considered to be lower than any `STRING`. | Lists are compared in dictionary order, i.e. list elements are compared pairwise in ascending order from the start of the list to the end. | `min(null)` returns `null`. @@ -578,8 +578,8 @@ UNWIND [1, 'a', null, 0.2, 'b', '1', '99'] AS val RETURN min(val) ---- -The lowest of all the values in the mixed set -- in this case, the string value `"1"` -- is returned. -Note that the (numeric) value `0.2`, which may _appear_ at first glance to be the lowest value in the list, is considered to be a higher value than `"1"` as the latter is a string. +The lowest of all the values in the mixed set -- in this case, the `STRING` value `"1"` -- is returned. +Note that the (numeric) value `0.2`, which may _appear_ at first glance to be the lowest value in the list, is considered to be a higher value than `"1"` as the latter is a `STRING`. .Result [role="queryresult",options="header,footer",cols="1*` containing the string representations for all the property names of a `NODE`, `RELATIONSHIP`, or `MAP`. +`keys` returns a `LIST` containing the `STRING` representations for all the property names of a `NODE`, `RELATIONSHIP`, or `MAP`. *Syntax:* diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 4f2f3fba4..62dc78846 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -263,7 +263,7 @@ See xref::subqueries/existential.adoc[Using EXISTS subqueries] for more informat [[functions-isempty]] == isEmpty() -The function `isEmpty()` returns `true` if the given list or map contains no elements, or if the given string contains no characters. +The function `isEmpty()` returns `true` if the given list or map contains no elements, or if the given `STRING` contains no characters. *Syntax:* @@ -389,7 +389,7 @@ isEmpty(string) | Name | Description | `string` -| An expression that returns a string. +| An expression that returns a `STRING`. |=== @@ -405,7 +405,7 @@ WHERE isEmpty(p.address) RETURN p.name AS name ---- -The `name` property of each node that has an empty (empty string) `address` property is returned: +The `name` property of each node that has an empty `STRING` `address` property is returned: .Result [role="queryresult",options="header,footer",cols="1*() ON (r.title) ---- //// -The `DirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific string; for example, in queries including `CONTAINS`. +The `DirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a type scan using `DirectedRelationshipTypeScan`, and a property store filter. @@ -777,7 +777,7 @@ Total database accesses: 5, total allocated memory: 184 == Undirected Relationship Index Contains Scan // UndirectedRelationshipIndexContainsScan -The `UndirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific string; for example, in queries including `CONTAINS`. +The `UndirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a type scan using `DirectedRelationshipTypeScan`, and a property store filter. @@ -822,7 +822,7 @@ Total database accesses: 5, total allocated memory: 184 == Directed Relationship Index Ends With Scan // DirectedRelationshipIndexEndsWithScan -The `DirectedRelationshipIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific string; for example, in queries containing `ENDS WITH`. +The `DirectedRelationshipIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. @@ -867,7 +867,7 @@ Total database accesses: 9, total allocated memory: 184 == Undirected Relationship Index Ends With Scan // UndirectedRelationshipIndexEndsWithScan -The `UndirectedRelationshipIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific string; for example, in queries containing `ENDS WITH`. +The `UndirectedRelationshipIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. @@ -919,7 +919,7 @@ CREATE RANGE INDEX range_worksin_duration FOR ()-[r:WORKS_IN]->() ON (r.duration ---- //// -The `DirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix string. +The `DirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix `STRING`. `DirectedRelationshipIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. @@ -1016,7 +1016,7 @@ Total database accesses: 76 == Undirected Relationship Index Seek By Range // UndirectedRelationshipIndexSeekByRange -The `UndirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix string. +The `UndirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix `STRING`. `UndirectedRelationshipIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. @@ -2247,7 +2247,7 @@ Total database accesses: 4, total allocated memory: 184 // NodeIndexSeekByRange -The `NodeIndexSeekByRange` operator finds nodes using an index seek where the value of the property matches a given prefix string. +The `NodeIndexSeekByRange` operator finds nodes using an index seek where the value of the property matches a given prefix `STRING`. `NodeIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. If the index is a unique index, the operator is instead called `NodeUniqueIndexSeekByRange`. @@ -2341,7 +2341,7 @@ Total database accesses: 4 == Node Unique Index Seek By Range // NodeUniqueIndexSeekByRange -The `NodeUniqueIndexSeekByRange` operator finds nodes using an index seek within a unique index, where the value of the property matches a given prefix string. +The `NodeUniqueIndexSeekByRange` operator finds nodes using an index seek within a unique index, where the value of the property matches a given prefix `STRING`. `NodeUniqueIndexSeekByRange` is used by `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+`, and `+>=+`. If the index is not unique, the operator is instead called `NodeIndexSeekByRange`. @@ -2394,7 +2394,7 @@ CREATE TEXT INDEX text_location_name FOR (l:Location) ON (l.name) ---- //// -The `NodeIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific string; for example, in queries including `CONTAINS`. +The `NodeIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. @@ -2439,7 +2439,7 @@ Total database accesses: 3, total allocated memory: 184 == Node Index Ends With Scan // NodeIndexEndsWithScan -The `NodeIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific string; for example, in queries containing `ENDS WITH`. +The `NodeIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. diff --git a/modules/ROOT/pages/styleguide.adoc b/modules/ROOT/pages/styleguide.adoc index 520297a78..fb7f492cd 100644 --- a/modules/ROOT/pages/styleguide.adoc +++ b/modules/ROOT/pages/styleguide.adoc @@ -153,7 +153,7 @@ WITH null AS n1, null AS n2 RETURN n1 IS NULL AND n2 IS NOT NULL ---- -* Write boolean literals (`true` and `false`) in lower case. +* Write `BOOLEAN` literals (`true` and `false`) in lower case. + .Bad [source, cypher] @@ -443,7 +443,7 @@ RETURN vehicle.mileage [[cypher-styleguide-meta-characters]] == Meta-characters -* Use single quotes, `'`, for literal string values. +* Use single quotes, `'`, for literal `STRING` values. + .Bad [source, cypher] @@ -457,8 +457,8 @@ RETURN "Cypher" RETURN 'Cypher' ---- -** Disregard this rule for literal strings that contain a single quote character. -If the string has both, use the form that creates the fewest escapes. +** Disregard this rule for literal `STRING` values that contain a single quote character. +If the `STRING` has both, use the form that creates the fewest escapes. In the case of a tie, prefer single quotes. + .Bad diff --git a/modules/ROOT/pages/syntax/operators.adoc b/modules/ROOT/pages/syntax/operators.adoc index 1629b7f8d..81f3a8a89 100644 --- a/modules/ROOT/pages/syntax/operators.adoc +++ b/modules/ROOT/pages/syntax/operators.adoc @@ -14,7 +14,7 @@ This page contains an overview of the available Cypher operators. | xref::syntax/operators.adoc#query-operators-property[Property operators] | `.` for static property access, `[]` for dynamic property access, `=` for replacing all properties, `+=` for mutating specific properties | xref::syntax/operators.adoc#query-operators-mathematical[Mathematical operators] | `+`, `-`, `*`, `/`, `%`, `^` | xref::syntax/operators.adoc#query-operators-comparison[Comparison operators] | `+=+`, `+<>+`, `+<+`, `+>+`, `+<=+`, `+>=+`, `IS NULL`, `IS NOT NULL` -| xref::syntax/operators.adoc#query-operators-comparison[String-specific comparison operators] | `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `=~` (regex matching) +| xref::syntax/operators.adoc#query-operators-comparison[`STRING`-specific comparison operators] | `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `=~` (regex matching) | xref::syntax/operators.adoc#query-operators-boolean[Boolean operators] | `AND`, `OR`, `XOR`, `NOT` | xref::syntax/operators.adoc#query-operators-string[String operators] | `+` (string concatenation), `IS NORMALIZED` | xref::syntax/operators.adoc#query-operators-temporal[Temporal operators] | `+` and `-` for operations between durations and temporal instants/durations, `*` and `/` for operations between durations and numbers @@ -270,12 +270,12 @@ The comparison operators comprise: [[query-operator-comparison-string-specific]] -=== String-specific comparison operators comprise: +=== `STRING`-specific comparison operators comprise: -* `STARTS WITH`: perform case-sensitive prefix searching on strings -* `ENDS WITH`: perform case-sensitive suffix searching on strings -* `CONTAINS`: perform case-sensitive inclusion searching in strings -* `=~`: regular expression for matching a pattern +* `STARTS WITH`: perform case-sensitive prefix searching on `STRING` values. +* `ENDS WITH`: perform case-sensitive suffix searching on `STRING` values. +* `CONTAINS`: perform case-sensitive inclusion searching in `STRING` values. +* `=~`: regular expression for matching a pattern. [[syntax-comparing-two-numbers]] === Comparing two numbers @@ -320,7 +320,7 @@ RETURN candidate 1+d|Rows: 2 |=== -xref::clauses/where.adoc#query-where-string[String matching] contains more information regarding the string-specific comparison operators as well as additional examples illustrating the usage thereof. +xref::clauses/where.adoc#query-where-string[`STRING` matching] contains more information regarding the `STRING`-specific comparison operators as well as additional examples illustrating the usage thereof. [[cypher-comparison]] @@ -542,12 +542,11 @@ RETURN number The string operators comprise: -* concatenating strings: `+` -* checking if a string is normalized: `IS NORMALIZED` - +* concatenating `STRING` values: `+` +* checking if a `STRING` is normalized: `IS NORMALIZED` [[syntax-concatenating-two-strings]] -=== Concatenating two strings with `+` +=== Concatenating two `STRING` values with `+` .Query [source, cypher] diff --git a/modules/ROOT/pages/syntax/parameters.adoc b/modules/ROOT/pages/syntax/parameters.adoc index c2afee26d..26fc8e6c7 100644 --- a/modules/ROOT/pages/syntax/parameters.adoc +++ b/modules/ROOT/pages/syntax/parameters.adoc @@ -107,7 +107,7 @@ RETURN n.name [[cypher-parameters-case-sensitive-pattern-matching]] -== Case-sensitive string pattern matching +== Case-sensitive `STRING` pattern matching .Parameters [source,javascript, indent=0] diff --git a/modules/ROOT/pages/syntax/parsing.adoc b/modules/ROOT/pages/syntax/parsing.adoc index 3f67116d1..61fbc94ce 100644 --- a/modules/ROOT/pages/syntax/parsing.adoc +++ b/modules/ROOT/pages/syntax/parsing.adoc @@ -3,15 +3,15 @@ [[parsing]] = Parsing -This page provides a general overview of how Cypher parses an input string. +This page provides a general overview of how Cypher parses an input `STRING`. -The Cypher parser takes an arbitrary input string. +The Cypher parser takes an arbitrary input `STRING`. While the syntax of Cypher is described in subsequent chapters, the following details the general rules on which characters are considered valid input. == Using unicodes in Cypher Unicodes can generally be escaped as `\uxxx`. -Additional documentation on escaping rules for string literals, names and regular expressions can be found here: +Additional documentation on escaping rules for `STRING` literals, names and regular expressions can be found here: * xref::queries/expressions.adoc#expressions-string-literals[String literal escape sequences] * xref::syntax/naming.adoc#symbolic-names-escaping-rules[Using special characters in names] diff --git a/modules/ROOT/pages/values-and-types/lists.adoc b/modules/ROOT/pages/values-and-types/lists.adoc index 2db07d892..2cba68f49 100644 --- a/modules/ROOT/pages/values-and-types/lists.adoc +++ b/modules/ROOT/pages/values-and-types/lists.adoc @@ -292,7 +292,7 @@ RETURN keanu.resume It is not, however, possible to store heterogeneous lists as properties. For example, the following query, which tries to set a list including both the `title` and the `released` properties as the `resume` property of `Keanu Reeves` will fail. -This is because the `title` property values are stored as strings, while the `released` property values are stored as integers. +This is because the `title` property values are stored as `STRING` values, while the `released` property values are stored as `INTEGER` values. .Query [source, cypher, role=test-fail] diff --git a/modules/ROOT/pages/values-and-types/spatial.adoc b/modules/ROOT/pages/values-and-types/spatial.adoc index 0120009f2..a7daf8684 100644 --- a/modules/ROOT/pages/values-and-types/spatial.adoc +++ b/modules/ROOT/pages/values-and-types/spatial.adoc @@ -68,7 +68,7 @@ The units of the `height` field are in meters. When geographic points are passed to the xref::functions/spatial.adoc#functions-distance[distance function], the result will always be in meters. If the coordinates are in any other format or unit than those supported, it is necessary to explicitly convert them. -For example, if the incoming `$height` is a string field in kilometers, it would be necessary to add `height: toFloat($height) * 1000` to the query. +For example, if the incoming `$height` is a `STRING` field in kilometers, it would be necessary to add `height: toFloat($height) * 1000` to the query. Likewise if the results of the `distance` function are expected to be returned in kilometers, an explicit conversion is required. The below query is an example of this conversion: diff --git a/modules/ROOT/pages/values-and-types/temporal.adoc b/modules/ROOT/pages/values-and-types/temporal.adoc index 45f92cfa1..e268aaa4d 100644 --- a/modules/ROOT/pages/values-and-types/temporal.adoc +++ b/modules/ROOT/pages/values-and-types/temporal.adoc @@ -80,7 +80,7 @@ This configuration option influences the creation of temporal types for the foll * Getting the current date and time without specifying a time zone. * Creating a temporal type from its components without specifying a time zone. -* Creating a temporal type by parsing a string without specifying a time zone. +* Creating a temporal type by parsing a `STRING` without specifying a time zone. * Creating a temporal type by combining or selecting values that do not have a time zone component, and without specifying a time zone. * Truncating a temporal value that does not have a time zone component, and without specifying a time zone. From 340d71ec9fdbaa0c51ed8e103a6c8e5a72c0ce3b Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Mon, 29 Jan 2024 11:11:27 +0100 Subject: [PATCH 374/383] Fix some tests (#850) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - A setup block was (silently) failing due to an extra trailing comma. With that fixed, the test for the other example failed because `Charlie` was not reported as a string in the asciidoc. - `test-result-skip` is not needed when a result is missing, only when a result is present but _different_ from what the tester would get. - No need to `test-skip` when query parameters are missing: the tester skips automatically. - After a query block, there may only be _one_ result block, either of type `queryresult` or `queryplan`. If there's both of them, the parser will pick the first one, but its internal state will probably also get messed up. - The last two query plans were surely wrong (they didn't have the right return value), while I'm not sure about the first. It may be something that changed in newer releases or IDK, they look pretty similar. --------- Co-authored-by: Jens Pryce-Åklundh <112686610+JPryce-Aklundh@users.noreply.github.com> --- .../using-indexes.adoc | 172 +++++++++--------- modules/ROOT/pages/patterns/concepts.adoc | 104 +++++------ modules/ROOT/pages/styleguide.adoc | 4 +- .../values-and-types/type-predicate.adoc | 9 +- 4 files changed, 145 insertions(+), 144 deletions(-) diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc index 6c048d807..a45deae3c 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc @@ -1,9 +1,9 @@ -:description: Information about how to use the search-performance indexes in Neo4j. -:test-skip: true +:description: Information about how search-performance indexes impact query performance in Neo4j. :test-setup-dump: https://github.com/neo4j-graph-examples/openstreetmap/raw/main/data/openstreetmap-50.dump + = The impact of indexes on query performance -Search-performance indexes enable quicker and more efficient pattern matching by solving a particular combination of node label/relationship type and property predicate. +Search-performance indexes enable quicker and more efficient pattern matching by solving a particular combination of node label/relationship type and property predicate. They are used automatically by the Cypher planner in `MATCH` clauses, usually at the start of a query, to scan the graph for the most appropriate place to start the pattern-matching process. By examining xref:planning-and-tuning/execution-plans.adoc[query execution plans], this page will explain the scenarios in which the various search-performance indexes are used to improve the performance of Cypher queries. @@ -43,15 +43,15 @@ For more information, see xref:planning-and-tuning/index.adoc#profile-and-explai PROFILE MATCH (n:PointOfInterest) WHERE n.type = 'baseball' -RETURN count(n) +RETURN count(n) ---- .Result -[role="queryresult",options="header,footer",cols="m"] +[options="header,footer",cols="m"] |=== | count(n) | 26 -d|Rows:1 +d|Rows: 1 |=== .Execution plan @@ -83,7 +83,7 @@ In the above example, had a node label lookup index not existed, the `NodeByLabe While useful, token lookup indexes will rarely be sufficient for applications querying databases of a non-trivial size because they cannot solve any property-related predicates. -For more information about the predicates supported by token lookup indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#lookup-index-supported-predicates[Managing search-performance indexes -> Token lookup indexes: supported predicates]. +For more information about the predicates supported by token lookup indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#lookup-index-supported-predicates[Managing search-performance indexes -> Token lookup indexes: supported predicates]. [[range-indexes]] == Range indexes @@ -109,7 +109,7 @@ For more information about creating indexes, see xref:indexes/search-performance PROFILE MATCH (n:PointOfInterest) WHERE n.type = 'baseball' -RETURN count(n) +RETURN count(n) ---- .Execution plan @@ -140,12 +140,12 @@ These points all illustrate the fundamental point that search-performance indexe For more information about the predicates supported by range indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#range-indexes-supported-predicates[Managing search-performance indexes -> Range indexes: supported predicates]. [[text-indexes]] -== Text indexes +== Text indexes Text indexes are used for queries filtering on `STRING` properties. If there exists both a range and a text index on a given `STRING` property, the text index will only be used by the Cypher planner for queries filtering with the `CONTAINS` or `ENDS WITH` operators. -In all other cases, the range index will be used. +In all other cases, the range index will be used. To show this behavior, it is necessary to create a text index and a range index on the same property: @@ -173,13 +173,13 @@ RETURN n.name AS name, n.type AS type ---- .Result -[role="queryresult",options="header,footer",cols="2*m"] +[options="header,footer",cols="2*m"] |=== | name | type | "William Shakespeare" | "statue" | "William Tecumseh Sherman" | "equestrian statue" -2+d|Rows:2 +2+d|Rows: 2 |=== .Execution plan @@ -246,7 +246,7 @@ For more information about the predicates supported by text indexes, see xref:in [[text-index-string-size]] === Text indexes and `STRING` sizes -The size of the indexed `STRING` properties is also relevant to the planner’s selection between range and text indexes. +The size of the indexed `STRING` properties is also relevant to the planner’s selection between range and text indexes. Range indexes have a maximum key size limit of around 8 kb. This means that range indexes cannot be used to index `STRING` values larger than 8 kb. @@ -256,12 +256,12 @@ As a result, they can be used to index `STRING` values up to that size. For information about calculating the size of indexes, see link:https://neo4j.com/developer/kb/a-method-to-calculate-index-size/[Neo4j Knowledge Base -> A method to calculate the size of an index in Neo4j]. [[point-indexes]] -== Point indexes +== Point indexes Point indexes solve predicates operating on spatial xref:values-and-types/spatial.adoc#spatial-values-point-type[`POINT`] values. Point indexes are optimized for queries filtering for the xref:functions/spatial.adoc#functions-distance[distance] between property values, or for property values within a xref:functions/spatial.adoc#functions-withinBBox[bounding box]. -The following example creates a point index which is then accessed through the `point.distance()` function to return the `name` and `type` of all `PointOfInterest` nodes within 100 meters of the `William Shakespeare` statue: +The following example creates a point index which is then used in an query returning the `name` and `type` of all `PointOfInterest` nodes within a set bounding box: .Create a point index [source,cypher] @@ -269,51 +269,51 @@ The following example creates a point index which is then accessed through the ` CREATE POINT INDEX point_index_location FOR (n:PointOfInterest) ON (n.location) ---- -.Query using the `point.distance()` function +.Query using the `point.withinBBox()` function [source,cypher] ---- -PROFILE -MATCH (p1:PointOfInterest {name:'William Shakespeare'}),(p2:PointOfInterest) -WHERE p1<>p2 AND point.distance(p1.location, p2.location) < 100 -RETURN p2.name AS name, p2.type AS type +PROFILE +MATCH (n:PointOfInterest) +WHERE point.withinBBox( + n.location, + point({srid: 4326, x: -73.9723702, y: 40.7697989}), + point({srid: 4326, x: -73.9725659, y: 40.770193})) +RETURN n.name AS name, n.type AS type ---- .Result -[role="queryresult",options="header,footer",cols="2*m"] +[options="header,footer",cols="2*m"] |=== | name | type -| "Walter Scott" | "statue" +| "Heckscher Ballfield 3" | "baseball" +| "Heckscher Ballfield 4" | "baseball" +| "Heckscher Ballfield 1" | "baseball" | "Robert Burns" | "statue" | "Christopher Columbus" | "statue" -| "Fitz-Greene Halleck" | "statue" +| "Walter Scott" | "statue" +| "William Shakespeare" | "statue" +| "Balto" | "statue" -2+d|Rows:4 +2+d|Rows: 8 |=== .Execution plan [role="queryplan"] ---- -+-------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | name, type | 8 | 4 | 0 | 0 | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | 1 | cache[p2.name] AS name, cache[p2.type] AS type | 8 | 4 | 0 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +CacheProperties | 2 | cache[p2.type], cache[p2.name] | 8 | 4 | 12 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 3 | NOT p1 = p2 AND p1.name = $autostring_0 AND point.distance(cache[p1.location], cache[p2.location]) < | 8 | 4 | 2891 | | | | | -| | | | $autoint_1 | | | | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | 4 | | 1060 | 1448 | 0 | | | | | -| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +NodeIndexSeekByRange | 5 | POINT INDEX p1:PointOfInterest(location) WHERE point.distance(location, cache[p2.location]) < $autoi | 1060 | 1448 | 1638 | 16616 | 1529/1 | 125.886 | Fused in Pipeline 1 | -| | | | nt_1, cache[p1.location] | | | | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan | 6 | p2:PointOfInterest | 188 | 188 | 189 | 376 | 2/0 | 0.533 | In Pipeline 0 | -+-------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 4730, total allocated memory: 16952 ++-----------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | `n.name`, `n.type` | 4 | 8 | 0 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[n.name] AS `n.name`, cache[n.type] AS `n.type` | 4 | 8 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +CacheProperties | 2 | cache[n.type], cache[n.name] | 4 | 8 | 24 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeekByRange | 3 | POINT INDEX n:PointOfInterest(location) WHERE point.withinBBox(location, point($autoint_0, $autodoub | 4 | 8 | 10 | 248 | 302/0 | 2.619 | Fused in Pipeline 0 | +| | | le_1, $autodouble_2), point($autoint_3, $autodouble_4, $autodouble_5)) | | | | | | | | ++-----------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 34, total allocated memory: 312 ---- For more information about the predicates supported by text indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#point-indexes-supported-predicates[Managing search-performance indexes -> Point indexes: supported predicates]. @@ -329,7 +329,7 @@ This is done by specifying either of the following settings in the `indexConfig` * `spatial.wgs-84.min` and `spatial.wgs-84.max`: used for xref:values-and-types/spatial.adoc#spatial-values-crs-geographic[WGS-84 2D] coordinate systems. * `spatial.wgs-84-3d.min` and `spatial.wgs-84-3d.max`: used for xref:values-and-types/spatial.adoc#spatial-values-crs-geographic[WGS-84 3D] coordinate systems. -The `min` and `max` of each setting define the minimum and maximum bounds for the spatial data in each coordinate system. +The `min` and `max` of each setting define the minimum and maximum bounds for the spatial data in each coordinate system. For example, the following index would only store `OSMNodes` in the northern half of Central Park: @@ -379,13 +379,13 @@ RETURN length(path) AS pathLength, sum(rel.distance) AS geographicalDistance .Result -[role="queryresult",options="header,footer",cols="2*m"] +[options="header,footer",cols="2*m"] |=== |pathLength | geographicalDistance | 25 | 2410.4495689536334 -2+d|Rows:1 +2+d|Rows: 1 |=== .Execution plan @@ -430,7 +430,7 @@ Like single-property range indexes, composite indexes support all predicates: * Prefix search: `n.prop STARTS WITH value` However, the order in which properties are defined when creating a composite index impacts how the planner will use the index to solve predicates. -For example, a composite index on `(n.prop1, n.prop2, n.prop3)` will generate a different query plan than a composite index created on `(n.prop3, n.prop2, n.prop1)`. +For example, a composite index on `(n.prop1, n.prop2, n.prop3)` will generate a different query plan than a composite index created on `(n.prop3, n.prop2, n.prop1)`. The following example shows how composite indexes on the same properties defined in a different order will generate different execution plans: @@ -452,11 +452,11 @@ RETURN n.name AS name ---- .Result -[role="queryresult",options="header,footer",cols="1*m"] +[options="header,footer",cols="1*m"] |=== | name | "William Shakespeare" -1+d|Rows:1 +1+d|Rows: 1 |=== .Execution plan @@ -477,7 +477,7 @@ Total database accesses: 1, total allocated memory: 312 ---- The plan shows the recently created composite index is used. -It also shows that the predicates are filtered as specified in the query (i.e. an equality check on the `lat` property, a prefix search on the `name` property, and an existence check on the `type` property). +It also shows that the predicates are filtered as specified in the query (i.e. an equality check on the `lat` property, a prefix search on the `name` property, and an existence check on the `type` property). However, if the order of the properties is altered when creating the index, a different query plan will be generated. To demonstrate this behavior, it is first necessary to drop the recently created `composite_2` index and create a new composite index on the same properties defined in a different order: @@ -527,7 +527,7 @@ Total database accesses: 3, total allocated memory: 312 This plan now shows that, while a prefix search has been used to solve the `name` property predicate, the `lat` property predicate is no longer solved with an equality check, but rather with an existence check and an explicit xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-filter[filter] operation afterward. Note that if the `composite_2` index had not been dropped before the query was rerun, the planner would have used it instead of the `composite_3` index. -This is because, when using composite indexes, any predicate after a prefix search will automatically be planned as an existence check predicate. +This is because, when using composite indexes, any predicate after a prefix search will automatically be planned as an existence check predicate. [[composite-index-rules]] === Composite index rules @@ -559,7 +559,7 @@ This can have important implications for query performance, because the planner To demonstrate this behavior, the following query will filter out any `ROUTE` relationships with a `distance` property less than `30`, and return the `distance` property of the matched relationships in ascending numerical order using the xref:clauses/order-by.adoc[ORDER BY] clause. .Query to return order of results without a relevant index -[source,syntax] +[source,cypher] ---- PROFILE MATCH ()-[r:ROUTE]-() @@ -606,7 +606,7 @@ CREATE INDEX range_index_relationships FOR ()-[r:ROUTE]-() ON (r.distance) Re-running the query, it now generates a different plan: .Rerun query after the creation of a relevant index -[source,syntax] +[source,cypher] ---- PROFILE MATCH ()-[r:ROUTE]-() @@ -695,7 +695,7 @@ This plan shows that a separate index is used to improve the performance of each Neo4j indexes do not store xref:values-and-types/working-with-null.adoc[`null`] values. This means that the planner must be able to rule out the possibility of `null` values in order for queries to use an index. -The following query demonstrates the incompatibility between `null` values and indexes by counting all `PointOfInterest` nodes with an unset `name` property: +The following query demonstrates the incompatibility between `null` values and indexes by counting all `PointOfInterest` nodes with an unset `name` property: .Query to count nodes with a `null` `name` value [source,cypher] @@ -707,7 +707,7 @@ RETURN count(n) AS nodes ---- .Result -[role="queryresult",options="header,footer",cols="1*m"] +[options="header,footer",cols="1*m"] |=== | nodes | 3 @@ -732,7 +732,7 @@ RETURN count(n) AS nodes Total database accesses: 562, total allocated memory: 472 ---- -The plan shows that neither of the two available indexes (range and text) on the `name` property is used to solve the predicate. +The plan shows that neither of the two available indexes (range and text) on the `name` property is used to solve the predicate. However, if a query predicate is added which is able to exclude the presence of any `null` values, then an index can be used. The following query shows this by adding a substring predicate to the above query: @@ -747,11 +747,11 @@ RETURN count(n) AS nodes ---- .Result -[role="queryresult",options="header,footer",cols="1*m"] +[options="header,footer",cols="1*m"] |=== | nodes | 5 -1+d|Rows:1 +1+d|Rows: 1 |=== The query result now includes both the three nodes with an unset `name` value found in the previous query and the two nodes with a `name` value containing `William` (`William Shakespeare` and `William Tecumseh Sherman`). @@ -803,11 +803,11 @@ RETURN count(n) AS nodes ---- .Result -[role="queryresult",options="header,footer",cols="1*m"] +[options="header,footer",cols="1*m"] |=== | nodes | 185 -1+d|Rows:1 +1+d|Rows: 1 |=== .Execution plan @@ -900,19 +900,19 @@ RETURN count(n) AS nodes .Execution plan [role="queryplan"] ---- -+------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | name | 187 | 185 | 0 | 0 | | | | -| | +----+---------------------------+----------------+------+---------+----------------+ | | | -| +Projection | 1 | cache[n.name] AS name | 187 | 185 | 0 | | | | | -| | +----+---------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 2 | cache[n.name] IS NOT NULL | 187 | 185 | 373 | | | | | -| | +----+---------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | 3 | n:PointOfInterest | 188 | 188 | 189 | 248 | 119/0 | 1.004 | Fused in Pipeline 0 | -+------------------+----+---------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | nodes | 1 | 1 | 0 | 0 | 0/0 | 0.012 | In Pipeline 1 | +| | +----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(n) AS nodes | 1 | 1 | 0 | 32 | | | | +| | +----+--------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | n.name IS NOT NULL | 187 | 185 | 373 | | | | | +| | +----+--------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 3 | n:PointOfInterest | 188 | 188 | 189 | 376 | 259/0 | 0.363 | Fused in Pipeline 0 | ++-------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 562, total allocated memory: 312 +Total database accesses: 562, total allocated memory: 472 ---- This plan shows that the available text index on the `name` property was not used to solve the predicate. @@ -939,20 +939,20 @@ RETURN count(n) AS nodes .Execution plan [role="queryplan"] ---- -+-----------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | name | 187 | 185 | 0 | 0 | | | | -| | +----+-----------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | 1 | n.name AS name | 187 | 185 | 370 | | | | | -| | +----+-----------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexScan | 2 | TEXT INDEX n:PointOfInterest(name) WHERE name IS NOT NULL | 187 | 185 | 186 | 248 | 115/0 | 1.671 | Fused in Pipeline 0 | -+-----------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | nodes | 1 | 1 | 0 | 0 | 0/0 | 0.013 | In Pipeline 1 | +| | +----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(n) AS nodes | 1 | 1 | 0 | 32 | | | | +| | +----+-----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexScan | 2 | TEXT INDEX n:PointOfInterest(name) WHERE name IS NOT NULL | 187 | 185 | 186 | 376 | 0/0 | 0.328 | Fused in Pipeline 0 | ++-------------------+----+-----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 556, total allocated memory: 312 +Total database accesses: 186, total allocated memory: 472 ---- -Because of the type constraint on the `name` property, the planner is now able to deduce that all `name` properties are of type `STRING`, and therefore use the available text index. +Because of the type constraint on the `name` property, the planner is now able to deduce that all `name` properties are of type `STRING`, and therefore use the available text index. Point indexes can be extended in the same way if a type constraint is created to ensure that all properties are `POINT` values. @@ -964,7 +964,7 @@ Note that xref:constraints/examples.adoc#constraints-examples-node-property-exis While it is impossible to give exact directions on when a search-performance index might be beneficial for a particular use-case, the following points provide some useful heuristics for when creating an index might improve query performance: * *Frequent property-based queries*: if some properties are used frequently for filtering or matching, consider creating an index on them. -* *Performance optimization*: If certain queries are too slow, re-examine the properties that are filtered on, and consider creating indexes for those properties that may cause bottlenecking. +* *Performance optimization*: If certain queries are too slow, re-examine the properties that are filtered on, and consider creating indexes for those properties that may cause bottlenecking. * *High cardinality properties*: high cardinality properties have many distinct values (e.g., unique identifiers, timestamps, or user names). Queries that seek to retrieve such properties will likely benefit from indexing. * *Complex queries*: if queries traverse complex paths in a graph (for example, by involving multiple hops and several layers of filtering), adding indexes to the properties used in those queries can improve query performance. * *Experiment and test*: It is good practice to experiment with different indexes and query patterns, and to measure the performance of critical queries with and without different indexes to evaluate their effectiveness. diff --git a/modules/ROOT/pages/patterns/concepts.adoc b/modules/ROOT/pages/patterns/concepts.adoc index 3715e8894..cc1038b00 100644 --- a/modules/ROOT/pages/patterns/concepts.adoc +++ b/modules/ROOT/pages/patterns/concepts.adoc @@ -1,4 +1,4 @@ -:description: this chapter describes the concepts behind graph pattern matching, including node patterns, relationship patterns, path patterns, path pattern matching, equijoins, shortestPath, quantified path patterns, variable length relationships, and graph patterns. +:description: this chapter describes the concepts behind graph pattern matching, including node patterns, relationship patterns, path patterns, path pattern matching, equijoins, shortestPath, quantified path patterns, variable length relationships, and graph patterns. = Concepts @@ -11,7 +11,7 @@ The model data in the examples below are based on the UK national rail network, [[node-patterns]] == Node patterns -Every graph pattern contains at least one node pattern. +Every graph pattern contains at least one node pattern. The simplest graph pattern is a single, empty node pattern: [source, role=noheader] @@ -55,7 +55,7 @@ The following matches nodes that have their `mode` property equal to `Rail`: [source, role=noheader] ---- -MATCH (n { mode: 'Rail' }) +MATCH (n { mode: 'Rail' }) ---- More general predicates can be expressed with a `WHERE` clause. @@ -66,7 +66,7 @@ The following matches nodes whose name property starts with `Preston`: MATCH (n:Station WHERE n.name STARTS WITH 'Preston') ---- -See the xref:patterns/reference.adoc#node-patterns[node patterns] reference section for more details. +See the xref:patterns/reference.adoc#node-patterns[node patterns] reference section for more details. [[relationship-patterns]] == Relationship patterns @@ -78,8 +78,8 @@ The simplest possible relationship pattern is a pair of dashes: -- ---- -This pattern matches a relationship with any direction and does not filter on any relationship type or property. -Unlike a node pattern, a relationship pattern cannot be used in a `MATCH` clause without node patterns at both ends. +This pattern matches a relationship with any direction and does not filter on any relationship type or property. +Unlike a node pattern, a relationship pattern cannot be used in a `MATCH` clause without node patterns at both ends. See xref:patterns/concepts.adoc#path-patterns[path patterns] for more details. In order to obtain a reference to the relationships matched by the pattern, a relationship variable needs to be declared in the pattern by adding the variable name in square brackets in between the dashes: @@ -117,7 +117,7 @@ A `WHERE` clause can be used for more general predicates: -[r WHERE time() + duration(r.duration) < time('22:00') ]-> ---- -See the xref:patterns/reference.adoc#relationship-patterns[relationship patterns] reference section for more details. +See the xref:patterns/reference.adoc#relationship-patterns[relationship patterns] reference section for more details. [[path-patterns]] == Path patterns @@ -160,11 +160,11 @@ These are invalid path patterns: ---- [[path-pattern-matching]] -== Path pattern matching +== Path pattern matching This section contains an example of matching a path pattern to paths in a property graph. -It uses the following graph: +It uses the following graph: image::path_pattern_example_graph.svg[width="600",role="middle"] @@ -190,10 +190,10 @@ CREATE (pmr:Station {name: 'Peckham Rye'}), The graph contains a number of train `Stations` and `Stops`. -A `Stop` represents the arrival and departure of a train that `CALLS_AT` a `Station`. +A `Stop` represents the arrival and departure of a train that `CALLS_AT` a `Station`. Each `Stop` forms part of a sequence of `Stops` connected by relationships with the type `NEXT`, representing the order of calling points made by a train service. -The graph shows three chains of `Stops` that represent different train services. +The graph shows three chains of `Stops` that represent different train services. Each of these services calls at the `Station` with the name `Denmark Hill`. To return all `Stops` that call at the `Station` `Denmark Hill`, the following _motif_ is used (the term motif is used to describe the pattern looked for in the graph): @@ -227,7 +227,7 @@ RETURN s.departs AS departureTime |=== [[equijoins]] -== Equijoins +== Equijoins An equijoin is an operation on paths that requires more than one of the nodes or relationships of the paths to be the same. The equality between the nodes or relationships is specified by declaring the same variable in multiple node patterns or relationship patterns. @@ -274,7 +274,7 @@ The solution is the following path with a cycle: image::patterns_equijoins_solution2.svg[width="400",role="middle"] -If unique properties exist on the node where the cycle "join" occurs in the path, then it is possible to repeat the node pattern with a predicate matching on the unique property. +If unique properties exist on the node where the cycle "join" occurs in the path, then it is possible to repeat the node pattern with a predicate matching on the unique property. The following motif demonstrates how that can be achieved, repeating a `Station` node pattern with the name `London Euston`: image::patterns_equijoins_motif.svg[width="700",role="middle"] @@ -302,7 +302,7 @@ Putting this path pattern with an equijoin in a query, the times of the outbound MATCH (n:Station {name: 'London Euston'})<-[:CALLS_AT]-(s1:Stop) -[:NEXT]->(s2:Stop)-[:CALLS_AT]->(:Station {name: 'Coventry'}) <-[:CALLS_AT]-(s3:Stop)-[:NEXT]->(s4:Stop)-[:CALLS_AT]->(n) -RETURN s1.departs+'-'+s2.departs AS outbound, +RETURN s1.departs+'-'+s2.departs AS outbound, s3.departs+'-'+s4.departs AS `return` ---- @@ -319,12 +319,12 @@ RETURN s1.departs+'-'+s2.departs AS outbound, [[quantified-path-patterns]] -== Quantified path patterns +== Quantified path patterns _This feature was introduced in Neo4j 5.9._ -All the path patterns discussed so far have had a fixed length. -This section considers how to match paths of _varying_ length by using _quantified path patterns_, allowing you to search for paths whose lengths are unknown or within a specific range. +All the path patterns discussed so far have had a fixed length. +This section considers how to match paths of _varying_ length by using _quantified path patterns_, allowing you to search for paths whose lengths are unknown or within a specific range. Quantified path patterns can be useful when, for example, searching for all nodes that can be reached from an anchor node, finding all paths connecting two nodes, or when traversing a hierarchy that may have differing depths. @@ -346,9 +346,9 @@ To recreate the graph, run the following query against an empty Neo4j database: .Query [source, cypher, role=test-setup] ---- -CREATE (pmr:Station {name: 'Peckham Rye'}), +CREATE (pmr:Station {name: 'Peckham Rye'}), (dmk:Station {name: 'Denmark Hill'}), - (clp:Station {name: 'Clapham High Street'}), + (clp:Station {name: 'Clapham High Street'}), (wwr:Station {name: 'Wandsworth Road'}), (clj:Station {name: 'Clapham Junction'}), (s1:Stop {arrives: time('17:19'), departs: time('17:20')}), @@ -370,7 +370,7 @@ CREATE (pmr:Station {name: 'Peckham Rye'}), Each `Stop` on a service `CALLS_AT` one `Station`. Each `Stop` has the properties `arrives` and `departs` that give the times the train is at the `Station`. Following the `NEXT` relationship of a `Stop` will give the next `Stop` of the service. -For this example, a path pattern is constructed to match each of the services that allow passengers to travel from `Denmark Hill` to `Clapham Junction`. +For this example, a path pattern is constructed to match each of the services that allow passengers to travel from `Denmark Hill` to `Clapham Junction`. The following shows the two paths that the path pattern should match: image::patterns_qpp_solutions.svg[width="700",role="middle"] @@ -443,7 +443,7 @@ For the current example, the first step is identifying the repeating pattern, wh (:Stop)-[:NEXT]->(:Stop) ---- -The shortest path has one instance of this pattern, the longest three. +The shortest path has one instance of this pattern, the longest three. So the quantifier applied to the wrapper parentheses is the range one to three, expressed as `{1,3}`: [source, role=noheader] @@ -525,10 +525,10 @@ Quantified relationships allow some simple quantified path patterns to be re-wri Continuing with the example of `Stations` and `Stops` from the previous section, consider the following query: .Query -[source,cypher,role=test-skip-result] +[source,cypher] ---- MATCH (d:Station { name: 'Denmark Hill' })<-[:CALLS_AT]-(n:Stop) - ((:Stop)-[:NEXT]->(:Stop)){1,10} + ((:Stop)-[:NEXT]->(:Stop)){1,10} (m:Stop)-[:CALLS_AT]->(a:Station { name: 'Clapham Junction' }) WHERE m.arrives < time('17:18') RETURN n.departs AS departureTime @@ -537,21 +537,21 @@ RETURN n.departs AS departureTime If the relationship `NEXT` only connects `Stop` nodes, the `:Stop` label expressions can be removed: .Query -[source, cypher, role=test-skip-result] +[source, cypher] ---- MATCH (d:Station { name: 'Denmark Hill' })<-[:CALLS_AT]-(n:Stop) - (()-[:NEXT]->()){1,10} + (()-[:NEXT]->()){1,10} (m:Stop)-[:CALLS_AT]->(a:Station { name: 'Clapham Junction' }) WHERE m.arrives < time('17:18') RETURN n.departs AS departureTime ---- When the quantified path pattern has one relationship pattern, it can be abbreviated to a _quantified relationship_. -A quantified relationship is a relationship pattern with a postfix quantifier. +A quantified relationship is a relationship pattern with a postfix quantifier. Below is the previous query rewritten with a quantified relationship: .Query -[source, cypher, role=test-skip-result] +[source, cypher] ---- MATCH (d:Station { name: 'Denmark Hill' })<-[:CALLS_AT]- (n:Stop)-[:NEXT]->{1,10}(m:Stop)-[:CALLS_AT]-> @@ -577,8 +577,8 @@ then it can be re-written as follows: [NOTE] ==== -Prior to the introduction of quantified path patterns and quantified relationships in Neo4j 5.9, the only method in Cypher to match paths of a variable length was through variable-length relationships. -This syntax is still available. +Prior to the introduction of quantified path patterns and quantified relationships in Neo4j 5.9, the only method in Cypher to match paths of a variable length was through variable-length relationships. +This syntax is still available. It is very similar to the syntax for quantified relationships, with the following differences: * Position and syntax of quantifier. @@ -596,7 +596,7 @@ This section uses the example of `Stations` and `Stops` used in the previous sec image::patterns_group_variables_graph.svg[width="700", role="middle"] -As the name suggests, this property represents the distance between two `Stops`. +As the name suggests, this property represents the distance between two `Stops`. To return the total distance for each service connecting a pair of `Stations`, a variable referencing each of the relationships traversed is needed. Similarly, to extract the `departs` and `arrives` properties of each `Stop`, variables referencing each of the nodes traversed is required. In this example of matching services between `Denmark Hill` and `Clapham Junction`, the variables `l` and `m` are declared to match the `Stops` and `r` is declared to match the relationships. @@ -655,7 +655,7 @@ Singleton variables bind at most to one node or relationship. |=== -Returning to the original goal, which was to return the sequence of depart times for the `Stops` and the total distance of each service, the final query exploits the compatibility of group variables with list comprehensions and list functions such as xref::functions/list#functions-reduce[reduce()]: +Returning to the original goal, which was to return the sequence of depart times for the `Stops` and the total distance of each service, the final query exploits the compatibility of group variables with list comprehensions and list functions such as xref::functions/list#functions-reduce[reduce()]: .Query [source, cypher] @@ -718,17 +718,17 @@ SET asc.location = point({longitude: -2.10876, latitude: 51.9989}), wop.location = point({longitude: -2.16003, latitude: 52.15605}), wof.location = point({longitude: -2.2216, latitude: 52.19514}), wos.location = point({longitude: -2.20941, latitude: 52.19473}) -CREATE (asc)-[:LINK {distance: 7.25}]->(cnm), - (asc)-[:LINK {distance: 11.29}]->(wop), - (asc)-[:LINK {distance: 14.75}]->(wos), - (bmv)-[:LINK {distance: 31.14}]->(cnm), - (bmv)-[:LINK {distance: 6.16}]->(dtw), - (bmv)-[:LINK {distance: 12.6}]->(wop), - (dtw)-[:LINK {distance: 5.64}]->(hby), - (dtw)-[:LINK {distance: 6.03}]->(wof), - (dtw)-[:LINK {distance: 5.76}]->(wos), - (psh)-[:LINK {distance: 4.16}]->(wop), - (wop)-[:LINK {distance: 3.71}]->(wos), +CREATE (asc)-[:LINK {distance: 7.25}]->(cnm), + (asc)-[:LINK {distance: 11.29}]->(wop), + (asc)-[:LINK {distance: 14.75}]->(wos), + (bmv)-[:LINK {distance: 31.14}]->(cnm), + (bmv)-[:LINK {distance: 6.16}]->(dtw), + (bmv)-[:LINK {distance: 12.6}]->(wop), + (dtw)-[:LINK {distance: 5.64}]->(hby), + (dtw)-[:LINK {distance: 6.03}]->(wof), + (dtw)-[:LINK {distance: 5.76}]->(wos), + (psh)-[:LINK {distance: 4.16}]->(wop), + (wop)-[:LINK {distance: 3.71}]->(wos), (wof)-[:LINK {distance: 0.65}]->(wos) ---- @@ -759,7 +759,7 @@ RETURN [n in nodes(p) | n.name] AS stops 1+d|Rows: 1 |=== -The path pattern passed to the `shortestPath` function defines the pattern that the shortest path must conform to. +The path pattern passed to the `shortestPath` function defines the pattern that the shortest path must conform to. It needs to be a variable-length relationship with a single relationship pattern. For more information, see the reference section on xref::patterns/reference#variable-length_relationships[variable-length relationships]. @@ -867,7 +867,7 @@ RETURN [n in nodes(p) | n.name] AS stops [[predicates-in-qpps]] == Predicates in quantified path patterns -One of the pitfalls of quantified path patterns is that, depending on the graph, they can end up matching very large numbers of paths, resulting in a slow query performance. +One of the pitfalls of quantified path patterns is that, depending on the graph, they can end up matching very large numbers of paths, resulting in a slow query performance. This is especially true when searching for paths with a large maximum length or when the pattern is too general. However, by using inline predicates that specify precisely which nodes and relationships should be included in the results, unwanted results will be pruned as the graph is traversed. Here are some examples of the types of constraints you can impose on quantified path pattern traversals: @@ -876,12 +876,12 @@ Here are some examples of the types of constraints you can impose on quantified For example, all nodes must be an `Employee`, but not a `Contractor`. * Relationships must have certain types. For example, all relationships in the path must be of type `EMPLOYED_BY`. -* Nodes or relationships must have properties satisfying some condition. +* Nodes or relationships must have properties satisfying some condition. For example, all relationships must have property `distance` `>` `10`. The same example used in the xref:patterns/concepts.adoc#shortest-path[shortest path] section above is used here to illustrate the use of inline predicates. In that section, the shortest path in terms of number of hops was found. -Here the example is developed to find the shortest path by physical distance and compared to the result from the shortestPath function. +Here the example is developed to find the shortest path by physical distance and compared to the result from the shortestPath function. The total distance from `Hartlebury` to `Cheltenham Spa` following the path yielded by `shortestPath` is given by the following query: @@ -891,7 +891,7 @@ The total distance from `Hartlebury` to `Cheltenham Spa` following the path yiel MATCH (hby:Station {name: 'Hartlebury'}), (cnm:Station {name: 'Cheltenham Spa'}) MATCH p = shortestPath((hby)-[:LINK*]-(cnm)) -RETURN reduce(acc = 0, r in relationships(p) | acc + r.distance) +RETURN reduce(acc = 0, r in relationships(p) | acc + r.distance) AS distance ---- @@ -914,7 +914,7 @@ Whether this is the shortest path by distance can be checked by looking at each MATCH (hby:Station {name: 'Hartlebury'}), (cnm:Station {name: 'Cheltenham Spa'}) MATCH p = (hby)-[:LINK]-+(cnm) -RETURN reduce(acc = 0, r in relationships(p) | acc + r.distance) +RETURN reduce(acc = 0, r in relationships(p) | acc + r.distance) AS distance ORDER BY distance LIMIT 1 ---- @@ -947,9 +947,9 @@ To compose the predicate, the xref:functions/spatial.adoc#functions-distance[poi MATCH (hby:Station {name: 'Hartlebury'}), (cnm:Station {name: 'Cheltenham Spa'}) MATCH p = (hby) - ((a)-[:LINK]-(b) WHERE point.distance(a.location, cnm.location) > + ((a)-[:LINK]-(b) WHERE point.distance(a.location, cnm.location) > point.distance(b.location, cnm.location))+ (cnm) -RETURN reduce(acc = 0, r in relationships(p) | acc + r.distance) +RETURN reduce(acc = 0, r in relationships(p) | acc + r.distance) AS distance ORDER BY distance ---- @@ -977,7 +977,7 @@ Using inline predicates or making quantified path patterns more specific where p In addition to the single path patterns discussed so far, multiple path patterns can be combined in a comma-separated list to form a graph pattern. In a graph pattern, each path pattern is matched separately, and where node variables are repeated in the separate path patterns, the solutions are reduced via equijoins. -If there are no equijoins between the path patterns, the result is a Cartesian product between the separate solutions. +If there are no equijoins between the path patterns, the result is a Cartesian product between the separate solutions. The benefit of joining multiple path patterns in this way is that it allows the specification of more complex patterns than the linear paths allowed by a single path pattern. To illustrate this, another example drawn from the railway model will be used. @@ -1096,4 +1096,4 @@ ORDER BY y.arrives LIMIT 1 4+d|Rows: 1 -|=== \ No newline at end of file +|=== diff --git a/modules/ROOT/pages/styleguide.adoc b/modules/ROOT/pages/styleguide.adoc index fb7f492cd..6f96955a0 100644 --- a/modules/ROOT/pages/styleguide.adoc +++ b/modules/ROOT/pages/styleguide.adoc @@ -176,7 +176,7 @@ RETURN b1 AND b2 ** parameters + .Bad -[source, cypher, role=test-skip] +[source, cypher] ---- CREATE (N {Prop: 0}) WITH RAND() AS Rand, $pArAm AS MAP @@ -184,7 +184,7 @@ RETURN Rand, MAP.property_key, Count(N) ---- + .Good -[source, cypher, role=test-skip] +[source, cypher] ---- CREATE (n {prop: 0}) WITH rand() AS rand, $param AS map diff --git a/modules/ROOT/pages/values-and-types/type-predicate.adoc b/modules/ROOT/pages/values-and-types/type-predicate.adoc index f44c4f8a3..1b0154d26 100644 --- a/modules/ROOT/pages/values-and-types/type-predicate.adoc +++ b/modules/ROOT/pages/values-and-types/type-predicate.adoc @@ -133,9 +133,10 @@ A graph containing the following nodes is used for the example below: //// [source, cypher, role=test-setup] ---- -CREATE (:Person {name: 'Alice', age:18}), -(:Person {name:'Bob', age:'20'}), -(:Person {name:'Charlie', age:21}), +CREATE + (:Person {name: 'Alice', age:18}), + (:Person {name:'Bob', age:'20'}), + (:Person {name:'Charlie', age:21}) ---- //// @@ -153,7 +154,7 @@ RETURN n.name AS name, n.age AS age [role="queryresult",options="header,footer",cols="2* Date: Mon, 29 Jan 2024 11:56:39 +0100 Subject: [PATCH 375/383] Typo fix (#870) --- .../indexes/search-performance-indexes/using-indexes.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc index a45deae3c..e252739de 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc @@ -261,7 +261,7 @@ For information about calculating the size of indexes, see link:https://neo4j.co Point indexes solve predicates operating on spatial xref:values-and-types/spatial.adoc#spatial-values-point-type[`POINT`] values. Point indexes are optimized for queries filtering for the xref:functions/spatial.adoc#functions-distance[distance] between property values, or for property values within a xref:functions/spatial.adoc#functions-withinBBox[bounding box]. -The following example creates a point index which is then used in an query returning the `name` and `type` of all `PointOfInterest` nodes within a set bounding box: +The following example creates a point index which is then used in a query returning the `name` and `type` of all `PointOfInterest` nodes within a set bounding box: .Create a point index [source,cypher] @@ -316,7 +316,7 @@ RETURN n.name AS name, n.type AS type Total database accesses: 34, total allocated memory: 312 ---- -For more information about the predicates supported by text indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#point-indexes-supported-predicates[Managing search-performance indexes -> Point indexes: supported predicates]. +For more information about the predicates supported by point indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#point-indexes-supported-predicates[Managing search-performance indexes -> Point indexes: supported predicates]. [[point-index-config-settings]] === Point index configuration settings From 29d5a5e0f29a8161ba6939a76a6414112c05d2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 30 Jan 2024 14:58:50 +0100 Subject: [PATCH 376/383] Fix indexes links (#873) --- .../search-performance-indexes/managing-indexes.adoc | 2 +- .../search-performance-indexes/using-indexes.adoc | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc index e484fcfae..f26786197 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc @@ -7,7 +7,7 @@ The following index types are included in this category: * xref:indexes/search-performance-indexes/managing-indexes.adoc#create-range-index[Range indexes] * xref:indexes/search-performance-indexes/managing-indexes.adoc#create-text-index[Text indexes] * xref:indexes/search-performance-indexes/managing-indexes.adoc#create-point-index[Point indexes] -* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-token-index[Token lookup indexes] +* xref:indexes/search-performance-indexes/managing-indexes.adoc#create-lookup-index[Token lookup indexes] For information about how search-performance indexes are used in Cypher queries, see xref:indexes/search-performance-indexes/using-indexes.adoc[Using search-performance indexes]. diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc index e252739de..641df1535 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc @@ -83,7 +83,7 @@ In the above example, had a node label lookup index not existed, the `NodeByLabe While useful, token lookup indexes will rarely be sufficient for applications querying databases of a non-trivial size because they cannot solve any property-related predicates. -For more information about the predicates supported by token lookup indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#lookup-index-supported-predicates[Managing search-performance indexes -> Token lookup indexes: supported predicates]. +For more information about the predicates supported by token lookup indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#lookup-index-supported-predicates[Create, show, and delete indexes -> Token lookup indexes: supported predicates]. [[range-indexes]] == Range indexes @@ -101,7 +101,7 @@ CREATE INDEX range_index_type FOR (n:PointOfInterest) ON (n.type) [TIP] If no index type is specified when creating an index, Neo4j will default to create a range index. -For more information about creating indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-indexes[Managing search-performance indexes -> CREATE INDEX]. +For more information about creating indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-indexes[Create, show, and delete indexes -> CREATE INDEX]. .Rerun query after the creation of a relevant index [source,cypher] @@ -137,7 +137,7 @@ This only produces 26 rows (representing the 26 `PointOfInterest` nodes in the d These points all illustrate the fundamental point that search-performance indexes can significantly improve the performance of Cypher queries. -For more information about the predicates supported by range indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#range-indexes-supported-predicates[Managing search-performance indexes -> Range indexes: supported predicates]. +For more information about the predicates supported by range indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#range-indexes-supported-predicates[Create, show, and delete indexes -> Range indexes: supported predicates]. [[text-indexes]] == Text indexes @@ -240,7 +240,7 @@ For more information about range index ordering, see the section on xref:indexes [TIP] Text indexes are only used for exact query matches. To perform approximate matches (including, for example, variations and typos), and to compute a similarity score between `STRING` values, use semantic xref:indexes/semantic-indexes/full-text-indexes.adoc[full-text indexes] instead. -For more information about the predicates supported by text indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#text-indexes-supported-predicates[Managing search-performance indexes -> Text indexes: supported predicates]. +For more information about the predicates supported by text indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#text-indexes-supported-predicates[Create, show, and delete indexes -> Text indexes: supported predicates]. [[text-index-string-size]] @@ -316,7 +316,7 @@ RETURN n.name AS name, n.type AS type Total database accesses: 34, total allocated memory: 312 ---- -For more information about the predicates supported by point indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#point-indexes-supported-predicates[Managing search-performance indexes -> Point indexes: supported predicates]. +For more information about the predicates supported by point indexes, see xref:indexes/search-performance-indexes/managing-indexes.adoc#point-indexes-supported-predicates[Create, show, and delete indexes -> Point indexes: supported predicates]. [[point-index-config-settings]] === Point index configuration settings @@ -992,7 +992,7 @@ As of Neo4j 5.8, there are three relevant columns returned by the xref:indexes/s * *`lastRead`*: returns the last time the index was used for reading. * *`readCount`*: returns the number of read queries issued to the index. * *`trackedSince`* returns the time when usage statistics tracking started for an index.footnote:[The `trackedSince` column is not part of the default return columns for the `SHOW INDEXES` command. To return this and all other non-default columns, use `SHOW INDEXES YIELD *`. -For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#listing-indexes-result-columns[Managing search-performance indexes -> Result columns for listing indexes].] +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#listing-indexes-result-columns[Create, show, and delete indexes -> Result columns for listing indexes].] To return these values (along with other relevant information) for the indexes in a database, run the following query: From 60e93c1e9bef941d7058f0947e73b3c2ba1a472c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 31 Jan 2024 09:58:55 +0100 Subject: [PATCH 377/383] Fix index query plan and add links on Syntax page (#875) --- .../managing-indexes.adoc | 8 ++--- .../using-indexes.adoc | 36 +++++++++---------- modules/ROOT/pages/indexes/syntax.adoc | 19 ++++++++++ 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc index f26786197..189cd8b64 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc @@ -42,7 +42,7 @@ Creating an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-ve A newly created index is not immediately available but is created in the background. [[create-range-index]] -=== Creating a range index +=== Create a range index Creating a range index can be done with the `CREATE INDEX` command. Note that the index name must be unique. @@ -200,7 +200,7 @@ FOR (n:Person) ON (n.surname) The index will not be created if there already exists an index with the same schema and type, same name or both. [[create-text-index]] -=== Creating a text index +=== Create a text index Creating a text index can be done with the `CREATE TEXT INDEX` command. Note that the index name must be unique. @@ -365,7 +365,7 @@ OPTIONS {indexProvider: 'text-2.0'} There is no supported index configuration for text indexes. [[create-point-index]] -=== Creating a point index +=== Create a point index Creating a point index can be done with the `CREATE POINT INDEX` command. Note that the index name must be unique. @@ -519,7 +519,7 @@ OPTIONS { Note that the wgs-84 and 3D cartesian settings, which are not specified in this example, will be set with their respective default values. [[create-lookup-index]] -=== Creating a token lookup index +=== Create a token lookup index Two token lookup indexes are created by default when creating a Neo4j database (one node label lookup index and one relationship type lookup index). Only one node label and one relationship type lookup index can exist at the same time. diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc index 641df1535..cc2faad7f 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/using-indexes.adoc @@ -553,7 +553,7 @@ Additionally, it bears repeating that composite indexes can only be used if a pr [[range-index-backed-order-by]] == Range index-backed ORDER BY -Range indexes store properties in ascending order (alphabetically for `STRING` values, and numerically for other `FLOAT` and `INTEGER` values). +Range indexes store properties in ascending order (alphabetically for `STRING` values, and numerically for `FLOAT` and `INTEGER` values). This can have important implications for query performance, because the planner may be able to take advantage of a pre-existing index order and therefore not have to perform an expensive xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-sort[`Sort`] operation later in the query. To demonstrate this behavior, the following query will filter out any `ROUTE` relationships with a `distance` property less than `30`, and return the `distance` property of the matched relationships in ascending numerical order using the xref:clauses/order-by.adoc[ORDER BY] clause. @@ -571,28 +571,26 @@ ORDER BY distance .Execution plan [role="queryplan"] ---- -+-----------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ -| +ProduceResults | 0 | distance | 3013 | 6744 | 0 | 0 | 0/0 | 12.784 | | | -| | +----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | | -| +Sort | 1 | distance ASC | 3013 | 6744 | 0 | 540472 | 0/0 | 50.600 | distance ASC | In Pipeline 1 | -| | +----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ -| +Projection | 2 | cache[r.distance] AS distance | 3013 | 6744 | 0 | | | | | | -| | +----+--------------------------------+----------------+-------+---------+----------------+ | +--------------+ | -| +Filter | 3 | cache[r.distance] < $autoint_0 | 3013 | 6744 | 10041 | | | | | | -| | +----+--------------------------------+----------------+-------+---------+----------------+ | +--------------+ | -| +Expand(All) | 4 | (anon_0)-[r:ROUTE]-(anon_1) | 10044 | 10041 | 151992 | | | | | | -| | +----+--------------------------------+----------------+-------+---------+----------------+ | +--------------+ | -| +AllNodesScan | 5 | anon_0 | 69165 | 69165 | 69166 | 376 | 31116/0 | 200.706 | | Fused in Pipeline 0 | -+-----------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ - -Total database accesses: 231199, total allocated memory: 540808 ++---------------------------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++---------------------------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ +| +ProduceResults | 0 | distance | 3013 | 6744 | 0 | 0 | 0/0 | 14.397 | | | +| | +----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | | +| +Sort | 1 | distance ASC | 3013 | 6744 | 0 | 540472 | 0/0 | 16.844 | distance ASC | In Pipeline 1 | +| | +----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ +| +Projection | 2 | cache[r.distance] AS distance | 3013 | 6744 | 0 | | | | | | +| | +----+--------------------------------+----------------+-------+---------+----------------+ | +--------------+ | +| +Filter | 3 | cache[r.distance] < $autoint_0 | 3013 | 6744 | 10041 | | | | | | +| | +----+--------------------------------+----------------+-------+---------+----------------+ | +--------------+ | +| +UndirectedRelationshipTypeScan | 4 | (anon_0)-[r:ROUTE]-(anon_1) | 10044 | 10041 | 5023 | 376 | 84/0 | 22.397 | | Fused in Pipeline 0 | ++---------------------------------+----+--------------------------------+----------------+-------+---------+----------------+------------------------+-----------+--------------+---------------------+ + +Total database accesses: 15064, total allocated memory: 540808 ---- This plan shows two important points about indexes and the ordering of results: -* No index was used in this query. +* Only the relationship type lookup index was used in this query (accessed by the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-type-scan[`UndirectedRelationshipTypeScan`] operator, which fetches all relationships and their start and end nodes from the relationship type index). * As a result, the planner has to perform a `Sort` operation to order the results by the distance property (in this case, it required 540472 bytes of memory). To see how an index could impact the query plan, it is first necessary to create a range index on the `distance` property: diff --git a/modules/ROOT/pages/indexes/syntax.adoc b/modules/ROOT/pages/indexes/syntax.adoc index 52cf1d6b7..b1a410be0 100644 --- a/modules/ROOT/pages/indexes/syntax.adoc +++ b/modules/ROOT/pages/indexes/syntax.adoc @@ -61,6 +61,8 @@ ON (r.propertyName_1[, r.propertyName_n]) ---- +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-range-index[Create, show, and delete indexes -> Create a range index]. + [[create-text-index]] === Text indexes @@ -87,6 +89,8 @@ Text indexes have no supported index configuration and, as of Neo4j 5.1, they ha [NOTE] It is not possible to create composite text indexes on multiple properties. +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-text-index[Create, show, and delete indexes -> Create a text index]. + [[create-point-index]] === Point indexes @@ -123,6 +127,7 @@ The following settings can be specified for point indexes: [NOTE] It is not possible to create composite point indexes on multiple properties. +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-point-index[Create, show, and delete indexes -> Create a point index]. [[create-lookup-index]] === Token lookup indexes @@ -148,6 +153,8 @@ ON [EACH] type(r) Two token lookup indexes are present by default when creating a Neo4j database, and only one node label lookup index and one relationship type lookup index can exist at the same time. +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#create-lookup-index[Create, show, and delete indexes -> Create a token lookup index]. + [[create-full-text-index]] === Full-text indexes @@ -176,6 +183,8 @@ The following settings can be specified for full-text indexes: * `fulltext.eventually_consistent` - specifies whether a full-text index is eventually consistent. If set to `true`, it will ensure that updates from committing transactions are applied in a background thread. +For more information, see xref:indexes/semantic-indexes/full-text-indexes.adoc#create-full-text-indexes[Full-text indexes - Create full-text indexes]. + [[create-vector-index]] === Vector indexes @@ -204,6 +213,8 @@ OPTIONS { [NOTE] It is not possible to create composite vector indexes on multiple properties. +For more information, see xref:indexes/semantic-indexes/vector-indexes.adoc#indexes-vector-create[Vector indexes - Create and configure vector indexes]. + [[list-index]] == SHOW INDEX @@ -221,6 +232,8 @@ SHOW [ALL | FULLTEXT | LOOKUP | POINT | RANGE | TEXT | VECTOR] INDEX[ES] When using the `RETURN` clause, the `YIELD` clause is mandatory. +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#list-indexes[Create, show, and delete indexes -> SHOW INDEXES]. + [[query-semantic-indexes]] == Query semantic indexes @@ -250,6 +263,8 @@ The valid _key: value_ pairs for the `options` map are: The `options` map and all of the keys are optional. +For more information, see xref:indexes/semantic-indexes/full-text-indexes.adoc#query-full-text-indexes[Full-text indexes - Query full-text indexes]. + [[query-vector-index]] === Vector indexes @@ -262,6 +277,8 @@ CALL db.index.vector.queryNodes(indexName :: STRING, numberOfNearestNeighbours : The `numberOfNearestNeighbours` refers to the number of nearest neighbors to return as the neighborhood. The `query` vector refers to the `LIST` in which to search for the neighborhood. +For more information, see xref:indexes/semantic-indexes/vector-indexes.adoc#indexes-vector-query[Vector indexes - Query vector indexes]. + [[drop-index]] == DROP INDEX @@ -280,3 +297,5 @@ Dropping indexes requires link:{neo4j-docs-base-uri}/operations-manual/{page-ver ---- DROP INDEX index_name [IF EXISTS] ---- + +For more information, see xref:indexes/search-performance-indexes/managing-indexes.adoc#drop-indexes[Create, show, and delete indexes -> DROP INDEX]. \ No newline at end of file From 6a1aa8d4c11286f241319c70dd8e4cf2cda16866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:14:44 +0100 Subject: [PATCH 378/383] update dbms.cluster.discovery.resolver_type (#877) Co-authored-by: Reneta Popova --- .../ROOT/pages/clauses/listing-settings.adoc | 36 +++---------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/modules/ROOT/pages/clauses/listing-settings.adoc b/modules/ROOT/pages/clauses/listing-settings.adoc index 6dcf48a42..a31c76708 100644 --- a/modules/ROOT/pages/clauses/listing-settings.adoc +++ b/modules/ROOT/pages/clauses/listing-settings.adoc @@ -183,7 +183,7 @@ For a full list of all available settings in Neo4j, refer to link:{neo4j-docs-ba == Listing settings with filtering on output columns The listed settings can be filtered by using the `WHERE` clause. -For example, the following query returns the name, value, and description of all settings starting with 'dbms': +For example, the following query returns the name, value, and description of the first three settings starting with 'dbms': .Query [source, cypher] @@ -191,7 +191,7 @@ For example, the following query returns the name, value, and description of all SHOW SETTINGS YIELD name, value, description WHERE name STARTS WITH 'dbms' RETURN name, value, description -LIMIT 10 +LIMIT 3 ---- .Result @@ -211,38 +211,9 @@ LIMIT 10 | "WARN" | "The level of middleware logging" -| "dbms.cluster.discovery.resolver_type" -| "LIST" -| "Configure the discovery type used for cluster name resolution" - -| "dbms.cluster.discovery.type" -| "LIST" -| "This setting has been replaced by 'dbms.cluster.discovery.resolver_type'" - -| "dbms.cluster.minimum_initial_system_primaries_count" -| "3" -| "Minimum number of machines initially required to formed a clustered DBMS. The cluster is considered formed when at least this many members have discovered each other, bound together and bootstrapped a highly available system database. As a result, at least this many of the cluster's initial machines must have 'server.cluster.system_database_mode' set to 'PRIMARY'.NOTE: If 'dbms.cluster.discovery.resolver_type' is set to 'LIST' and 'dbms.cluster.discovery.endpoints' is empty then the user is assumed to be deploying a standalone DBMS, and the value of this setting is ignored." - -| "dbms.cluster.network.handshake_timeout" -| "20s" -| "Time out for protocol negotiation handshake." - -| "dbms.cluster.network.connect_timeout" -| "30s" -| "The maximum amount of time to wait for a network connection to be established." - -| "dbms.cluster.network.max_chunk_size" -| "32768" -| "Maximum chunk size allowable across network by clustering machinery." - -| "dbms.cluster.network.supported_compression_algos" -| "" -| "Network compression algorithms that this instance will allow in negotiation as a comma-separated list. Listed in descending order of preference for incoming connections. An empty list implies no compression. For outgoing connections this merely specifies the allowed set of algorithms and the preference of the remote peer will be used for making the decision. Allowable values: [Gzip, Snappy, Snappy_validating, LZ4, LZ4_high_compression, LZ_validating, LZ4_high_compression_validating]" - -3+d|Rows: 10 +3+d|Rows: 3 |=== - == Listing specific settings It is possible to specify which settings to return in the list by setting names. @@ -253,6 +224,7 @@ It is possible to specify which settings to return in the list by setting names. SHOW SETTINGS "server.bolt.enabled", "server.bolt.advertised_address", "server.bolt.listen_address" ---- + .Result [role="queryresult",options="header,footer",cols="2m,1m,1m,1m,3m"] |=== From 8f8417b1ffc62a8bcdb0efb8c9c10728a514e05f Mon Sep 17 00:00:00 2001 From: Reneta Popova Date: Wed, 7 Feb 2024 06:24:49 +0000 Subject: [PATCH 379/383] Add genAI integrations (#882) --- modules/ROOT/content-nav.adoc | 1 + modules/ROOT/pages/genai-integrations.adoc | 291 +++++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 modules/ROOT/pages/genai-integrations.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index b82e15537..afc70a404 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -76,6 +76,7 @@ ** xref:functions/database.adoc[] ** xref:functions/user-defined.adoc[] +* xref:genai-integrations.adoc[] * xref:indexes/index.adoc[] ** xref:indexes/search-performance-indexes/overview.adoc[] *** xref:indexes/search-performance-indexes/managing-indexes.adoc[] diff --git a/modules/ROOT/pages/genai-integrations.adoc b/modules/ROOT/pages/genai-integrations.adoc new file mode 100644 index 000000000..005265504 --- /dev/null +++ b/modules/ROOT/pages/genai-integrations.adoc @@ -0,0 +1,291 @@ +:description: Information about GenAI integrations. + +:link-vector-indexes: xref:indexes/semantic-indexes/vector-indexes.adoc + +[role=on-aura] +[[genai-integrations]] += GenAI integrations + +_This feature is available only in Aura._ + +Neo4j provides integrations with various generative AI services using a native GenAI plugin. +This includes support for transforming text data into vector embeddings using VertexAI, OpenAI, or Amazon Bedrock. +The resulting vector embeddings can be used together with Neo4j's {link-vector-indexes}[vector search indexes]. + +[[vector-embeddings]] +== Vector embeddings + +A vector embedding is a numerical representation of some object, typically unstructured data, such as natural language text. +For more information and examples of vector embeddings in general, see {link-vector-indexes}[Vector search indexes]. + +Text can be transformed into vector embeddings using some model of natural language. +Providers of LLM (Large Language Model) and Generative AI services, such as VertexAI, OpenAI, and Amazon Bedrock, expose this functionality via their APIs, which can be invoked directly from within Neo4j using this plugin. + +[[single-embedding]] +=== Generate a single embedding + +You can use the `genai.vector.encode()` function to generate a vector embedding for a single value, for example, when applying the transformation inline in an expression when querying for or writing to a specific property. + +[IMPORTANT] +==== +This function sends one API request every time it is called, which may result in a lot of overhead in terms of both +network traffic and latency. +If you want to generate many embeddings at once, use <>. +==== + +.Signature for `genai.vector.encode()` label:function[] +[source,syntax,role="noheader",indent=0] +---- +genai.vector.encode(resource :: STRING, provider :: STRING, configuration :: MAP = {}) :: LIST +---- + +* The `resource` (a `STRING`) is the object to transform into an embedding, such as a chunk of natural language text. +* The `provider` (a `STRING`) is the case-insensitive identifier of the provider to use. +See identifiers under <> for supported options. +* The `configuration` (a `MAP`) contains provider-specific settings, such as which model to invoke, as well as any required API credentials. +See <> for details of each supported provider. ++ +[NOTE] +==== +Note that because this argument may contain sensitive data, it is obfuscated in the _query.log_. +However, if the function call is misspelled or the query is otherwise malformed, it may be logged without being obfuscated. +==== ++ +The returned list of floats is the vector representing the resource that was passed in. + +.Generating an embedding for querying a vector index +==== +The `genai.vector.encode` function is useful for generating vectors that can be used to query a vector index. + +For example, a vector index can be queried for entries that are semantically near some query string by generating a vector embedding for that query string and then using that vector as the query vector in a vector index search. + +Here, you query a vector index called `my_index` for the 10 nearest neighbors: + +.Query +[source,cypher] +---- +WITH "embeddings are cool" AS queryString +WITH genai.vector.encode(queryString, "VertexAI", { token: $token, projectId: $project }) AS queryVector +CALL db.index.vector.queryNodes("my_index", 10, queryVector) YIELD node, score RETURN node, score +---- +==== + +.Generating an embedding on the fly +==== + +Assuming nodes with the `Tweet` label have an `id` property and a `text` property, you can generate and return +the text embedding for the tweet with ID 1234: + +.Query +[source,cypher] +---- +MATCH (n:Tweet { id: 1234 }) +RETURN genai.vector.encode(n.text, "VertexAI", { token: $token, projectId: $project }) AS embedding +---- +==== + +[[multiple-embeddings]] +=== Generating a batch of embeddings + +You can use the `genai.vector.encodeBatch()` procedure to generate many vector embeddings with a single API request. +This procedure takes a list of resources as an input, and returns the same number of result rows, instead of a single one. + +Using this procedure is recommended in cases where a single large resource is split up into multiple chunks (like the pages of a book), +or when generating embeddings for a large number of resources. + +[IMPORTANT] +==== +This procedure attempts to generate embeddings for all supplied resources in a single API request. +Therefore, it is recommended to see the respective provider's documentation for details on, for example, the maximum number of embeddings that can be generated per request. +==== + +.Signature for `genai.vector.encodeBatch()` label:procedure[] +[source,syntax,role="noheader",indent=0] +---- +genai.vector.encodeBatch(resources :: LIST, provider :: STRING, configuration :: MAP = {}) :: (index :: INTEGER, resource :: STRING, vector :: LIST) +---- + +* The `resources` (a `LIST`) parameter is the list of objects to transform into embeddings, such as chunks of natural language text. +* The `provider` (a `STRING`) is the case-insensitive identifier of the provider to use. +See <> for supported options. +* The `configuration` (a `MAP`) specifies provider-specific settings such as which model to invoke, as well as any required API credentials. +See <> for details of each supported provider. ++ +[NOTE] +==== +Because this argument may contain sensitive data, it is obfuscated in the _query.log_. +However, if the function call is misspelled or the query is otherwise malformed, it may be logged without being obfuscated. +==== ++ +Each returned row contains the following columns: + +* The `index` (an `INTEGER`) is the index of the corresponding element in the input list, to aid in correlating results back to inputs. +* The `resource` (a `STRING`) is the name of the input resource. +* The `vector` (a `LIST`) is the generated vector embedding for this resource. + +.Generate embeddings for multiple chunks of a larger text +==== + +Given a list of page texts, you can generate an embedding for each of the pages with a single procedure call and create a corresponding graph structure: + +.Query +[source,cypher] +---- +CREATE (b:Book { title: $bookTitle }) +WITH book +CALL genai.vector.encodeBatch($pageTexts, "VertexAI", { token: $token, projectId: $project }) YIELD index, resource, vector +WITH book, index, resource, vector +CREATE (:Page { index: index, text: resource, vector: vector })-[:OF]->(book) +---- +==== + +.Generate embeddings for many text properties +==== + +If you want to generate embeddings for the text content of all nodes with the label `Tweet`, you can use `CALL ... IN TRANSACTIONS` to split the work up into batches and issue one API request per batch. + +Assuming nodes with the `Tweet` label have a `text` property, you can generate vector embeddings for each one and write them to the `embedding` property on each one in batches of 1000, for example: + +.Query +[source,cypher] +---- +MATCH (n:Tweet) +WHERE n.text IS NOT NULL +CALL { + WITH n + WITH collect(n) AS nodes, collect(n.text) AS resources + CALL genai.vector.encodeBatch(resources, "VertexAI", { token: $token, projectId: $project }) YIELD index, vector + CALL db.create.setNodeVectorProperty(nodes[index], "vector", vector) +} IN TRANSACTIONS OF 1000 ROWS +---- +==== + +[[ai-providers]] +== GenAI providers + +The following GenAI providers are supported for generating vector embeddings. +Each provider has its own configuration map that can be passed to the `genai.vector.encode()` or `genai.vector.encodeBatch()` functions. + +=== Vertex AI + +* Identifier: `"VertexAI"` +* https://cloud.google.com/vertex-ai/docs/generative-ai/embeddings/get-text-embeddings[Official Vertex AI documentation] + +.Configuration map +[%header,cols="1m,1m,3d,2m"] +|=== +| Key | Type | Description | Default + +| token +| STRING +| API access token. +| label:required[] + +| projectId +| STRING +| GCP project ID. +| label:required[] + +| model +| STRING +| The ID of the model you want to invoke. + +Supported values: `"textembedding-gecko@001"` +| "textembedding-gecko@001" + +| region +| STRING +| GCP region where to send the API requests. + +Supported values: +`"us-west1"`, +`"us-west2"`, +`"us-west3"`, +`"us-west4"`, +`"us-central1"`, +`"us-east1"`, +`"us-east4"`, +`"us-south1"`, +`"northamerica-northeast1"`, +`"northamerica-northeast2"`, +`"southamerica-east1"`, +`"southamerica-west1"`, +`"europe-west2"`, +`"europe-west1"`, +`"europe-west4"`, +`"europe-west6"`, +`"europe-west3"`, +`"europe-north1"`, +`"europe-central2"`, +`"europe-west8"`, +`"europe-west9"`, +`"europe-southwest1"`, +`"asia-south1"`, +`"asia-southeast1"`, +`"asia-southeast2"`, +`"asia-east2"`, +`"asia-east1"`, +`"asia-northeast1"`, +`"asia-northeast2"`, +`"australia-southeast1"`, +`"australia-southeast2"`, +`"asia-northeast3"`, +`"me-west1"` +| "us-central1" +|=== + + +=== OpenAI + +* Identifier: `"OpenAI"` +* https://platform.openai.com/docs/guides/embeddings[Official OpenAI documentation] + +.Configuration map +[%header,cols="1m,1m,3d,2m"] +|=== +| Key | Type | Description | Default + +| token +| STRING +| API access token. +| label:required[] + +| model +| STRING +| The ID of the model you want to invoke. + +Supported values: `"text-embedding-ada-002"` +| "text-embedding-ada-002" +|=== + + +=== Amazon Bedrock + +* Identifier: `"Bedrock"` +* https://docs.aws.amazon.com/bedrock/latest/APIReference/welcome.html[Official Bedrock documentation] + +.Configuration map +[%header,cols="1m,1m,3d,2m"] +|=== +| Key | Type | Description | Default + +| accessKeyId +| STRING +| AWS access key ID. +| label:required[] + +| secretAccessKey +| STRING +| AWS secret key. +| label:required[] + +| model +| STRING +| The ID of the model you want to invoke. + +Supported values: `"amazon.titan-embed-text-v1"` +| "amazon.titan-embed-text-v1" + +| region +| STRING +| AWS region where to send the API requests. + +Supported values: `"us-east-1"`, `"us-west-2"`, `"ap-southeast-1"`, `"ap-northeast-1"`, `"eu-central-1"` +| "us-east-1" + +|=== \ No newline at end of file From c0195e418ec94ab8947f3a60d6645ded6ccb4412 Mon Sep 17 00:00:00 2001 From: Reneta Popova Date: Thu, 8 Feb 2024 11:09:24 +0000 Subject: [PATCH 380/383] Add the genai.vector.encode function to the available functions (#885) --- modules/ROOT/pages/functions/index.adoc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/ROOT/pages/functions/index.adoc b/modules/ROOT/pages/functions/index.adoc index 8a47b1a6a..c3159799a 100644 --- a/modules/ROOT/pages/functions/index.adoc +++ b/modules/ROOT/pages/functions/index.adoc @@ -19,6 +19,7 @@ This section contains information on all functions in the Cypher query language. * LOAD CSV functions [xref::functions/index.adoc#header-query-functions-load-csv[Summary]|xref::functions/load-csv.adoc[Detail]] * Graph functions [xref::functions/index.adoc#header-query-functions-graph[Summary]|xref::functions/graph.adoc[Detail]] * Database functions [xref::functions/index.adoc#header-query-functions-database[Summary]|xref::functions/database.adoc[Detail]] label:new[Introduced in 5.12] +* GenAI function [xref::functions/index.adoc#header-query-functions-genai[Summary]|xref::genai-integrations.adoc#single-embedding[Detail]] label:new[Introduced in 5.16] label:aura-only[Avaliable only on Aura] * User-defined functions [xref::functions/index.adoc#header-query-functions-user-defined[Summary]|xref::functions/user-defined.adoc[Detail]] Related information may be found in xref::syntax/operators.adoc[Operators]. @@ -797,6 +798,18 @@ Database functions provide information about databases. label:new[Introduced in 5.12] |=== +[[header-query-functions-genai]] +**xref::genai-integrations.adoc#single-embedding[GenAI function]** label:new[Introduced in 5.16] label:aura-only[Available only on Aura] + +This function is used to generate a vector embedding for a single value. + +[options="header"] +|=== +| Function | Signature | Description +1.1+| xref:genai-integrations.adoc#single-embedding[`genai.vector.encode()`] | `genai.vector.encode(resource :: STRING, provider :: STRING, configuration :: MAP = {}) :: LIST` | Encode a given resource as a vector using the named provider. +label:new[Introduced in 5.16] label:aura-only[Available only on Aura] +|=== + [[header-query-functions-user-defined]] **xref::functions/user-defined.adoc[User-defined functions]** From 322ed35ec49a13c539f5a4a1f7a624e038c93d02 Mon Sep 17 00:00:00 2001 From: Therese Magnusson Date: Fri, 9 Feb 2024 15:57:20 +0100 Subject: [PATCH 381/383] Document the new notifications (#880) returned by `CREATE INDEX ... IF NOT EXISTS ...`, `CREATE CONSTRAINT ... IF NOT EXISTS ...`, `DROP INDEX ... IF EXISTS`, and `DROP CONSTRAINT ... IF EXISTS` in the cases when nothing happens. --- modules/ROOT/pages/constraints/examples.adoc | 71 ++++++++++++++++--- modules/ROOT/pages/constraints/syntax.adoc | 2 + ...ions-additions-removals-compatibility.adoc | 47 ++++++++++++ .../managing-indexes.adoc | 41 +++++++++++ .../semantic-indexes/full-text-indexes.adoc | 5 +- .../semantic-indexes/vector-indexes.adoc | 3 +- modules/ROOT/pages/indexes/syntax.adoc | 2 + 7 files changed, 160 insertions(+), 11 deletions(-) diff --git a/modules/ROOT/pages/constraints/examples.adoc b/modules/ROOT/pages/constraints/examples.adoc index cc37faf62..7664310f9 100644 --- a/modules/ROOT/pages/constraints/examples.adoc +++ b/modules/ROOT/pages/constraints/examples.adoc @@ -99,7 +99,7 @@ It will be updated to say `Node property uniqueness constraints added: 1` in Neo Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and that no constraint is created if any other constraint with the given name, or another node property uniqueness constraint on the same schema, already exists. - +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing constraint which blocks the creation. .+CREATE CONSTRAINT+ ====== @@ -452,7 +452,7 @@ It will be updated to say `Relationship property uniqueness constraints added: 1 Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and that no constraint is created if any other constraint with the given name, or another relationship property uniqueness constraint on the same schema, already exists. - +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing constraint which blocks the creation. .+CREATE CONSTRAINT+ ====== @@ -472,6 +472,13 @@ Assuming a constraint with the name `sequels` already exists: (no changes, no records) ---- +.Notification +[source] +---- +`CREATE CONSTRAINT sequels IF NOT EXISTS FOR ()-[e:SEQUEL_OF]-() REQUIRE (e.order) IS UNIQUE` has no effect. +`CONSTRAINT sequels FOR ()-[e:SEQUEL_OF]-() REQUIRE (e.order, e.seriesTitle) IS UNIQUE` already exists. +---- + [NOTE] ==== The detailed statistics view currently says `Relationship uniqueness constraints added: 1`. @@ -792,7 +799,7 @@ For the node property existence constraints, they will say `Node property existe Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and that no constraint is created if any other constraint with the given name, or another node property existence constraint on the same schema, already exists. - +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing constraint which blocks the creation. .+CREATE CONSTRAINT+ ====== @@ -820,6 +827,13 @@ Assuming a constraint with the name `author_pseudonym` already exists: (no changes, no records) ---- +.Notification +[source] +---- +`CREATE CONSTRAINT author_pseudonym IF NOT EXISTS FOR (e:Author) REQUIRE (e.pseudonym) IS NOT NULL` has no effect. +`CONSTRAINT author_pseudonym FOR (e:Author) REQUIRE (e.pseudonym) IS UNIQUE` already exists. +---- + ====== @@ -1068,7 +1082,7 @@ For the relationship property existence constraints, they will say `Relationship Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and that no constraint is created if any other constraint with the given name, or another relationship property existence constraint on the same schema, already exists. - +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing constraint which blocks the creation. .+CREATE CONSTRAINT+ ====== @@ -1088,6 +1102,13 @@ Assuming that such a constraint already exists: (no changes, no records) ---- +.Notification +[source] +---- +`CREATE CONSTRAINT wrote_year IF NOT EXISTS FOR ()-[e:WROTE]-() REQUIRE (e.year) IS NOT NULL` has no effect. +`CONSTRAINT wrote_year FOR ()-[e:WROTE]-() REQUIRE (e.year) IS NOT NULL` already exists. +---- + ====== @@ -1389,7 +1410,7 @@ Added 1 constraint. Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and that no constraint is created if any other constraint with the given name, or another node property type constraint on the same schema and property type, already exists. - +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing constraint which blocks the creation. .+CREATE CONSTRAINT+ ====== @@ -1409,6 +1430,13 @@ Assuming a node property type constraint on the label `Movie` which restricts th (no changes, no records) ---- +.Notification +[source] +---- +`CREATE CONSTRAINT movie_titles IF NOT EXISTS FOR (e:Movie) REQUIRE (e.title) IS :: STRING` has no effect. +`CONSTRAINT movie_title FOR (e:Movie) REQUIRE e.title IS :: STRING` already exists. +---- + ====== @@ -1791,7 +1819,7 @@ Added 1 constraint. Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and that no constraint is created if any other constraint with the given name, or another relationship property type constraint on the same schema and property type, already exists. - +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing constraint which blocks the creation. .+CREATE CONSTRAINT+ ====== @@ -1811,6 +1839,13 @@ Assuming that such a constraint already exists: (no changes, no records) ---- +.Notification +[source] +---- +`CREATE CONSTRAINT part_of IF NOT EXISTS FOR ()-[e:PART_OF]-() REQUIRE (e.order) IS :: INTEGER` has no effect. +`CONSTRAINT part_of FOR ()-[e:PART_OF]-() REQUIRE (e.order) IS :: INTEGER` already exists. +---- + ====== @@ -2141,7 +2176,7 @@ Added 1 constraint. Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and that no constraint is created if any other constraint with the given name, or another node key constraint on the same schema, already exists. - +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing constraint which blocks the creation. .+CREATE CONSTRAINT+ ====== @@ -2161,6 +2196,13 @@ Assuming a node key constraint on `(:Actor {firstname, surname})` already exists (no changes, no records) ---- +.Notification +[source] +---- +`CREATE CONSTRAINT actor_names IF NOT EXISTS FOR (e:Actor) REQUIRE (e.firstname, e.surname) IS NODE KEY` has no effect. +`CONSTRAINT actor_fullname FOR (e:Actor) REQUIRE (e.firstname, e.surname) IS NODE KEY` already exists. +---- + ====== @@ -2488,7 +2530,7 @@ Added 1 constraint. Creating an already existing constraint will fail. To avoid such an error, `IF NOT EXISTS` can be added to the `CREATE` command. This will ensure that no error is thrown and that no constraint is created if any other constraint with the given name, or another relationship key constraint on the same schema, already exists. - +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing constraint which blocks the creation. .+CREATE CONSTRAINT+ ====== @@ -2508,6 +2550,13 @@ Assuming a relationship key constraint on `()-[:KNOWS {since, how}]-()` already (no changes, no records) ---- +.Notification +[source] +---- +`CREATE CONSTRAINT knows IF NOT EXISTS FOR ()-[e:KNOWS]-() REQUIRE (e.since, e.how) IS RELATIONSHIP KEY` has no effect. +`CONSTRAINT knows_since_how FOR ()-[e:KNOWS]-() REQUIRE (e.since, e.how) IS RELATIONSHIP KEY` already exists. +---- + ====== @@ -2850,6 +2899,12 @@ DROP CONSTRAINT missing_constraint_name IF EXISTS (no changes, no records) ---- +.Notification +[source] +---- +`DROP CONSTRAINT missing_constraint_name IF EXISTS` has no effect. `missing_constraint_name` does not exist. +---- + ====== diff --git a/modules/ROOT/pages/constraints/syntax.adoc b/modules/ROOT/pages/constraints/syntax.adoc index f36697cbe..6d9fb3523 100644 --- a/modules/ROOT/pages/constraints/syntax.adoc +++ b/modules/ROOT/pages/constraints/syntax.adoc @@ -16,6 +16,7 @@ This means its default behavior is to throw an error if an attempt is made to cr With the `IF NOT EXISTS` flag, no error is thrown and nothing happens should a constraint with the same name or same schema and constraint type already exist. It may still throw an error if conflicting data, indexes, or constraints exist. Examples of this are nodes with missing properties, indexes with the same name, or constraints with same schema but a different conflicting constraint type. +label:new[Introduced in 5.17] In case nothing happens an informational notification is returned showing the existing constraint which blocks the creation. For constraints that are backed by an index, the index provider for the backing index can be specified using the `OPTIONS` clause. Only one valid value exists for the index provider, `range-1.0`, which is the default value. @@ -284,6 +285,7 @@ DROP CONSTRAINT constraint_name [IF EXISTS] This drop command is optionally idempotent. This means its default behavior is to throw an error if an attempt is made to drop the same constraint twice. With the `IF EXISTS` flag, no error is thrown and nothing happens should the constraint not exist. +label:new[Introduced in 5.17] An informational notification is instead returned detailing that the constraint does not exist. [NOTE] ==== diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 0a47d3de7..4c8a4c657 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -19,6 +19,53 @@ Replacement syntax for deprecated and removed features are also indicated. [[cypher-deprecations-additions-removals-5.17]] == Neo4j 5.17 +=== Updated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:updated[] +[source, cypher, role=noheader] +---- +CREATE [index_type] INDEX [index_name] IF NOT EXISTS FOR ... +---- +| +When attempting to create an index using `IF NOT EXISTS` with either the same name or same index type and schema, or both, as an existing index the command now returns a notification showing the existing index which blocks the creation. + +a| +label:functionality[] +label:updated[] +[source, cypher, role=noheader] +---- +CREATE CONSTRAINT [constraint_name] IF NOT EXISTS FOR ... +---- +| +When attempting to create a constraint using `IF NOT EXISTS` with either the same name or same constraint type and schema (and property type for property type constraints), or both, as an existing constraint the command now returns a notification showing the existing constraint which blocks the creation. + +a| +label:functionality[] +label:updated[] +[source, cypher, role=noheader] +---- +DROP CONSTRAINT constraint_name IF EXISTS +---- +| When attempting to drop a non-existing index using `IF EXISTS` the command will now return a notification about the index not existing. + +a| +label:functionality[] +label:updated[] +[source, cypher, role=noheader] +---- +DROP INDEX index_name IF EXISTS +---- +| When attempting to drop a non-existing constraint using `IF EXISTS` the command will now return a notification about the constraint not existing. + +|=== + === New features [cols="2", options="header"] diff --git a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc index 189cd8b64..869d05a00 100644 --- a/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc +++ b/modules/ROOT/pages/indexes/search-performance-indexes/managing-indexes.adoc @@ -29,6 +29,7 @@ The `CREATE INDEX` command is optionally idempotent. This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. If `IF NOT EXISTS` is appended to the command, no error is thrown and nothing happens should an index with the same name or same schema and index type already exist. It may still throw an error if conflicting constraints exist, such as constraints with the same name or schema and backing index type. +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing index which blocks the creation. Index providers and configuration settings can be specified using the `OPTIONS` clause.footnote:[Index providers are essentially different implementations of the same index type. Different providers are only available for xref:indexes/search-performance-indexes/managing-indexes.adoc#create-a-text-index-specifying-the-index-provider[text indexes].] @@ -198,6 +199,14 @@ FOR (n:Person) ON (n.surname) ---- The index will not be created if there already exists an index with the same schema and type, same name or both. +label:new[Introduced in 5.17] An informational notification is instead returned. + +.Notification +[source] +---- +`CREATE RANGE INDEX node_range_index_name IF NOT EXISTS FOR (e:Person) ON (e.surname)` has no effect. +`RANGE INDEX node_range_index_name FOR (e:Person) ON (e.surname)` already exists. +---- [[create-text-index]] === Create a text index @@ -346,6 +355,14 @@ CREATE TEXT INDEX node_index_name IF NOT EXISTS FOR (n:Person) ON (n.nickname) ---- Note that the index will not be created if there already exists an index with the same schema and type, same name or both. +label:new[Introduced in 5.17] An informational notification is instead returned. + +.Notification +[source] +---- +`CREATE TEXT INDEX node_index_name IF NOT EXISTS FOR (e:Person) ON (e.nickname)` has no effect. +`TEXT INDEX node_text_index_nickname FOR (e:Person) ON (e.nickname)` already exists. +---- [discrete] [[create-a-text-index-specifying-the-index-provider]] @@ -483,6 +500,14 @@ FOR (n:Person) ON (n.sublocation) ---- Note that the index will not be created if there already exists an index with the same schema and type, same name or both. +label:new[Introduced in 5.17] An informational notification is instead returned. + +.Notification +[source] +---- +`CREATE POINT INDEX node_point_index IF NOT EXISTS FOR (e:Person) ON (e.sublocation)` has no effect. +`POINT INDEX node_point_index_name FOR (e:Person) ON (e.sublocation)` already exists. +---- [discrete] [[create-a-point-index-specifying-the-index-configuration]] @@ -631,6 +656,14 @@ CREATE LOOKUP INDEX node_label_lookup IF NOT EXISTS FOR (n) ON EACH labels(n) ---- The index will not be created if there already exists an index with the same schema and type, same name or both. +label:new[Introduced in 5.17] An informational notification is instead returned. + +.Notification +[source] +---- +`CREATE LOOKUP INDEX node_label_lookup IF NOT EXISTS FOR (e) ON EACH labels(e)` has no effect. +`LOOKUP INDEX node_label_lookup_index FOR (e) ON EACH labels(e)` already exists. +---- [[create-conflicting-index]] @@ -1009,6 +1042,7 @@ DROP INDEX index_name [IF EXISTS] The `DROP INDEX` command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. +label:new[Introduced in 5.17] An informational notification is instead returned detailing that the index does not exist. [TIP] Dropping an index requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-administration/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. @@ -1097,3 +1131,10 @@ DROP INDEX missing_index_name IF EXISTS ---- If an index with that name exists it is removed, if not the command does nothing. +label:new[Introduced in 5.17] Additionally, an informational notification is returned. + +.Notification +[source] +---- +`DROP INDEX missing_index_name IF EXISTS` has no effect. `missing_index_name` does not exist. +---- diff --git a/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc b/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc index cf199c306..d49b438cd 100644 --- a/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc +++ b/modules/ROOT/pages/indexes/semantic-indexes/full-text-indexes.adoc @@ -37,7 +37,8 @@ If no name is given when created, a random name will be assigned to the full-tex The `CREATE FULLTEXT INDEX` command is optionally idempotent. This mean that its default behavior is to throw an error if an attempt is made to create the same index twice. If `IF NOT EXISTS` is appended to the command, no error is thrown and nothing happens should an index with the same name or a full-text index on the same schema already exist. -As of Neo4j 5.16, the index name can also be given as a parameter, `CREATE FULLTEXT INDEX $name FOR ...`. +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing index which blocks the creation. +label:new[Introduced in 5.16] The index name can also be given as a parameter, `CREATE FULLTEXT INDEX $name FOR ...`. [TIP] Creating a full-text index requires the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[`CREATE INDEX` privilege]. @@ -350,7 +351,7 @@ In the following example, the previously created `communications` full-text inde DROP INDEX communications ---- -As of Neo4j 5.16, the index name can also be given as a parameter when dropping an index: `DROP INDEX $name`. +label:new[Introduced in 5.16] The index name can also be given as a parameter when dropping an index: `DROP INDEX $name`. [[full--text-index-procedures]] == List of full-text index procedures diff --git a/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc index d47ed18bc..44b446a90 100644 --- a/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc +++ b/modules/ROOT/pages/indexes/semantic-indexes/vector-indexes.adoc @@ -114,7 +114,7 @@ The options map is mandatory because setting the vector dimensions and similarit It is considered best practice to give the index a name when it is created. This name is needed for both dropping and querying the index. If the index is not explicitly named, it will get an auto-generated name. -As of Neo4j 5.16, the index name can also be given as a parameter, `CREATE VECTOR INDEX $name FOR ...`. +label:new[Introduced in 5.16] The index name can also be given as a parameter, `CREATE VECTOR INDEX $name FOR ...`. [IMPORTANT] ==== @@ -128,6 +128,7 @@ The `indexConfig` is a `MAP` from `STRING` values to `STRING` and `INTEGER` valu The command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to create the same index twice. With `IF NOT EXISTS`, no error is thrown and nothing happens should an index with the same name, schema or both already exist. It may still throw an error should a constraint with the same name exist. +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing index which blocks the creation. [NOTE] ==== diff --git a/modules/ROOT/pages/indexes/syntax.adoc b/modules/ROOT/pages/indexes/syntax.adoc index b1a410be0..5ef19fe80 100644 --- a/modules/ROOT/pages/indexes/syntax.adoc +++ b/modules/ROOT/pages/indexes/syntax.adoc @@ -22,6 +22,7 @@ ON property_or_token_lookup_pattern The `CREATE … INDEX …` command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to create an index with the same name twice. With `IF NOT EXISTS`, no error is thrown and nothing happens should an index with the same name or same schema and index type already exist (it may still throw an error if conflicting constraints exist, such as constraints with the same name or with the same schema and backing index). +label:new[Introduced in 5.17] An informational notification is instead returned showing the existing index which blocks the creation. The index name must be unique among both indexes and constraints. A random name will be assigned if no name is explicitly given when an index is created. @@ -288,6 +289,7 @@ The name of the index can be found using the `SHOW INDEXES` command, given in th The `DROP INDEX` command is optionally idempotent. This means that its default behavior is to throw an error if an attempt is made to drop the same index twice. With `IF EXISTS`, no error is thrown and nothing happens should the index not exist. +label:new[Introduced in 5.17] An informational notification is instead returned detailing that the index does not exist. [TIP] Dropping indexes requires link:{neo4j-docs-base-uri}/operations-manual/{page-version}/authentication-authorization/database-administration/#access-control-database-administration-index[the `DROP INDEX` privilege]. From a3a1d70b69d2b389538871583586c4a0b5037c18 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Thu, 15 Feb 2024 06:25:15 +0100 Subject: [PATCH 382/383] Remove inaccurate remark on drivers and `USE` clause. (#890) --- modules/ROOT/pages/clauses/use.adoc | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index e76a65e97..20df616ca 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -6,9 +6,6 @@ The `USE` clause determines which graph a query, or query part, is executed against. It is supported for queries and schema commands. -[TIP] -With link:{neo4j-docs-base-uri}/create-applications/[Neo4j Drivers], do not rely on the `USE` clause to specify the database against which a query should run. -Drivers expect the database name to be specified as a parameter in the query's function call. [[query-use-syntax]] == Syntax From fba1ab1b11ae29f21f34f2fa820b2b5db64e703b Mon Sep 17 00:00:00 2001 From: Reneta Popova Date: Mon, 19 Feb 2024 13:41:34 +0000 Subject: [PATCH 383/383] Document that the GenAI plugin is available on prem as well (#889) Linked to https://github.com/neo4j/docs-operations/pull/1429 --- ...ions-additions-removals-compatibility.adoc | 22 +++++++++---------- modules/ROOT/pages/functions/index.adoc | 5 ++--- modules/ROOT/pages/genai-integrations.adoc | 15 +++++++++++-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 4c8a4c657..02f2108bc 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -874,20 +874,20 @@ The `trackedSince` column returns the time when usage statistics tracking starte |=== -=== New features +=== New features [cols="2", options="header"] |=== | Feature | Details -a| +a| label:functionality[] label:new[] New operator: `AssertSameRelationship` -a| +a| The `AssertSameRelationship` operator is used to ensure that no relationship property uniqueness constraints are violated in the slotted and interpreted runtime. More information can be found xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-assert-same-relationship[here]. @@ -990,7 +990,7 @@ label:functionality[] label:new[] [source, syntax, role=noheader] ---- -CALL { +CALL { } IN TRANSACTIONS [ OF ROWS ] [ ON ERROR CONTINUE / BREAK / FAIL ] @@ -1004,7 +1004,7 @@ New fine-grained control mechanism to control how an inner transaction impacts s * `ON ERROR BREAK` - will ignore an error and stop the execution of subsequent inner transactions. -* `ON ERROR FAIL` - will fail in case of an error. +* `ON ERROR FAIL` - will fail in case of an error. * `REPORT STATUS AS ` - reports the execution status of the inner transaction (a map value including the fields `started` `committed`, `transactionId`, and `errorMessage`). This flag is disallowed for `ON ERROR FAIL`. @@ -1130,13 +1130,13 @@ RETURN 'val' as one, 'val' as two | Feature | Details -a| +a| label:functionality[] label:new[] New operator: `IntersectionNodeByLabelsScan` -a| +a| The `IntersectionNodeByLabelsScan` operator fetches all nodes that have all of the provided labels from the node label index. More information can be found xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-intersection-node-by-labels-scan[here]. @@ -1161,7 +1161,7 @@ label:updated[] SHOW DATABASES ---- a| -Changes to the visibility of databases hosted on offline servers. +Changes to the visibility of databases hosted on offline servers. For such databases: @@ -1208,20 +1208,20 @@ The property uniqueness constraint type filter now allow both `UNIQUE` and `UNIQ |=== -=== New features +=== New features [cols="2", options="header"] |=== | Feature | Details -a| +a| label:functionality[] label:new[] New operator: `NodeByElementIdSeek` -a| +a| The `NodeByElementIdSeek` operator reads one or more nodes by ID from the node store, specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]. More information can be found xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-by-elementid-seek[here]. diff --git a/modules/ROOT/pages/functions/index.adoc b/modules/ROOT/pages/functions/index.adoc index c3159799a..bfee98569 100644 --- a/modules/ROOT/pages/functions/index.adoc +++ b/modules/ROOT/pages/functions/index.adoc @@ -19,7 +19,7 @@ This section contains information on all functions in the Cypher query language. * LOAD CSV functions [xref::functions/index.adoc#header-query-functions-load-csv[Summary]|xref::functions/load-csv.adoc[Detail]] * Graph functions [xref::functions/index.adoc#header-query-functions-graph[Summary]|xref::functions/graph.adoc[Detail]] * Database functions [xref::functions/index.adoc#header-query-functions-database[Summary]|xref::functions/database.adoc[Detail]] label:new[Introduced in 5.12] -* GenAI function [xref::functions/index.adoc#header-query-functions-genai[Summary]|xref::genai-integrations.adoc#single-embedding[Detail]] label:new[Introduced in 5.16] label:aura-only[Avaliable only on Aura] +* GenAI function [xref::functions/index.adoc#header-query-functions-genai[Summary]|xref::genai-integrations.adoc#single-embedding[Detail]] label:new[Available in Aura] label:new[Introduced in 5.17] * User-defined functions [xref::functions/index.adoc#header-query-functions-user-defined[Summary]|xref::functions/user-defined.adoc[Detail]] Related information may be found in xref::syntax/operators.adoc[Operators]. @@ -799,7 +799,7 @@ label:new[Introduced in 5.12] |=== [[header-query-functions-genai]] -**xref::genai-integrations.adoc#single-embedding[GenAI function]** label:new[Introduced in 5.16] label:aura-only[Available only on Aura] +**xref::genai-integrations.adoc#single-embedding[GenAI function]** label:new[Introduced in 5.17] This function is used to generate a vector embedding for a single value. @@ -807,7 +807,6 @@ This function is used to generate a vector embedding for a single value. |=== | Function | Signature | Description 1.1+| xref:genai-integrations.adoc#single-embedding[`genai.vector.encode()`] | `genai.vector.encode(resource :: STRING, provider :: STRING, configuration :: MAP = {}) :: LIST` | Encode a given resource as a vector using the named provider. -label:new[Introduced in 5.16] label:aura-only[Available only on Aura] |=== [[header-query-functions-user-defined]] diff --git a/modules/ROOT/pages/genai-integrations.adoc b/modules/ROOT/pages/genai-integrations.adoc index 005265504..3064da3d7 100644 --- a/modules/ROOT/pages/genai-integrations.adoc +++ b/modules/ROOT/pages/genai-integrations.adoc @@ -2,16 +2,27 @@ :link-vector-indexes: xref:indexes/semantic-indexes/vector-indexes.adoc -[role=on-aura] [[genai-integrations]] = GenAI integrations -_This feature is available only in Aura._ +_This feature is available from Neo4j 5.17 in both Neo4j Aura and on-prem deployments._ Neo4j provides integrations with various generative AI services using a native GenAI plugin. This includes support for transforming text data into vector embeddings using VertexAI, OpenAI, or Amazon Bedrock. The resulting vector embeddings can be used together with Neo4j's {link-vector-indexes}[vector search indexes]. +== Prerequisites + +To use the GenAI plugin, you need to have the following: + +* A Neo4j DBMS running Neo4j 5.17 or later. +* A GenAI provider account, such as VertexAI, OpenAI, or Amazon Bedrock. +* API credentials for the provider you want to use. +* (On-prem only) the Neo4j GenAI plugin installed in your Neo4j deployment. +The plugin JAR is located in the _/products_ directory of the Neo4j installation. +For more information on how to install and configure the plugin, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/configuration/plugins/[Operations Manual -> Configure plugins]. +The plugin is enabled by default in Neo4j Aura. + [[vector-embeddings]] == Vector embeddings

~D|^CfhO%XMOHqff`$-7Nc`boQhD~)d>B7 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%0Agja3^2}#%XV7l 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 z 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 diff --git a/modules/ROOT/images/privileges_hierarchy_dbms.svg b/modules/ROOT/images/privileges_hierarchy_dbms.svg index 3cffcbd45..705c3fd5d 100644 --- a/modules/ROOT/images/privileges_hierarchy_dbms.svg +++ b/modules/ROOT/images/privileges_hierarchy_dbms.svg @@ -1,14 +1 @@ - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/modules/ROOT/images/privileges_on_graph_syntax.png b/modules/ROOT/images/privileges_on_graph_syntax.png deleted file mode 100644 index c70792be274ea58de16b2c0cd480bf2675f128fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57154 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